我正在努力想出一个高效的quicksort algo。它工作正常,但是当元素数量很大,并且数组的某些部分是预先排序的时候,运行起来需要很长的时间。我在quicksort上查找维基百科的文章,在那里我发现了这样的一篇文章:
为了确保最多使用O(log )空间,请先对数组的较小部分进行递归,然后使用尾调用对另一个数组进行递归。 使用插入排序,它具有较小的常数因子,因此在小数组上更快,用于调用这些小数组(即长度小于实验确定的阈值t)。这可以通过将这些数组保持未排序并在末尾运行一个插入排序传递来实现,因为插入排序有效地处理了几乎已排序的数组。在识别每个小段时,一个单独的插入排序增加了启动和停止许多小排序的开销,但避免了跨多个段边界比较键的浪费,这些键由于快速排序过程的工作而变得井然有序。它还改进了缓存的使用。
我目前正在对两个分区进行递归处理。知道如何实现第一个提示吗?什么是指递归首先进入数组的较小部分,然后使用尾调用递归到另一个?其次,如何在快速排序中实现insertion-sort?它是总是提高效率,还是只有当数组的某些部分是预先排序的时候?如果是第二种情况,那么我当然无法知道什么时候会发生这种情况。那么我什么时候应该包括insertion-sort
发布于 2012-09-17 07:48:39
在快速排序中,您选择一个随机的枢轴将数组划分为两个半的,其中一个可能更小,
例如,数组大小为100,枢轴将数组分隔为40 / 60,40为较小的大小。
假设您决定了使用插入排序为10的阈值大小,则需要继续递归地按枢轴拆分数组,每当其中一半的值变小或等于10时,您可以使用在小数组上表现为O(n)的插入排序。
考虑到如果数组是反向排序的(最坏的情况),插入排序将表现得很糟糕。
关于递归内容,您只需要修改快速排序递归->数组大小为<= 10停止递归的停止情况,并使用插入排序对所有数组(在此递归步骤中要小得多)进行排序。
所谓尾递归,是指对前半部分做您需要的一切,然后调用插入排序作为最后一个方法,它用于节省空间。
Quick-sort()
choose a pivot
move the smaller elements from left
move the bigger elements from right
quick-sort on the bigger half of the array
if half is less then X
only then do an insertion sort on the other half <- this is a tail recursion insertion sort
else
quick sort on this half also据我所见,第二个优化建议不对每个递归步骤使用插入排序,但请记住为其设置约束的索引,然后在一个批中调用插入排序,将所有切片中的项连接起来,这将确保提高缓存的使用,但更难实现,
发布于 2012-09-17 08:32:34
有多种方法可以使标准快速排序更有效。要实现文章中的第一个提示,您应该编写如下内容:
void quicksort(int * tab, int l, int r)
{
int q;
while(l < r)
{
q = partition(tab, l, r);
if(q - l < r - q) //recurse into the smaller half
{
quicksort(tab, l, q - 1);
l = q + 1;
} else
{
quicksort(tab, q + 1, r);
r = q - 1;
}
}
}希望这足够清楚。下一步将是实现--您自己的堆栈(或者使用您正在使用的任何语言中的一些内置语言),而不是使用递归调用。示例(伪)代码:
void quicksort2(int * tab, int l, int r)
{
int le, ri, q;
init stack;
push(l, r, stack);
while(!empty(stack))
{
//take the top pair of values from the stack and set them to le and ri
pop(le, ri, stack);
if(le >= ri)
continue;
q = partition(tab, le, ri);
if(q - le < ri - q) //smaller half goes first
{
push(le, q - 1, stack);
push(q + 1, ri, stack);
} else
{
push(q + 1, ri, stack);
push(le, q - 1, stack);
}
}
delete stack;
}然后,您可以继续执行来自您的文章中的另一个提示。要做到这一点,您应该将一些任意的常量(让我们称之为CUT_OFF )设置为大约20。这将告诉您的算法何时应该切换到插入排序。修改前面的示例应该相当容易(添加一个if-语句),以便在到达CUT_OFF点后切换到插入排序,所以我将让您继续这样做。
至于分区方法,我建议使用洛穆托分区而不是Hoare。
但是,如果您的数据已经预先排序,那么可以考虑使用完全不同的算法。根据我的经验,如果您的数据是预先排序的,在链接列表上实现的自然系列合并排序是一个非常好的选择。
发布于 2012-09-17 11:03:56
不久前,我编写了一个基于快速排序的算法,您可以在那里找到它(实际上它是一个选择算法,但也可以使用一个排序算法):
我从这次经验中吸取的教训如下:
我希望这能帮上忙洛朗。
https://stackoverflow.com/questions/12454866
复制相似问题