QuickSort 程序达到最大递归限制 500?

QuickSort program reaching maximum recursion limit of 500?

我正在通过在 MATLAB 中绘制图形来分析排序算法。下面是我的快速排序代码。当我 运行 它给出了这个错误:

Maximum recursion limit of 500 reached. Use set(0,'RecursionLimit', N) to change the limit. Be aware that exceeding your available stack space can crash MATLAB and/or your computer. Error in ==> quickSort

为什么会出现这个错误?我的代码有什么问题吗?

function [ar] = quickSort(ar, low, high)
    if low < high
        [ar, q] = parti(ar, low, high);
        ar = quickSort(ar, low, q - 1);
        ar = quickSort(ar, q + 1, high);
    end
end

function [ar, i] = parti(ar, p, r)
    x = ar(r);
    i = p - 1;

    for j = p : r
        if ar(j) <= x
            i = i + 1;
            if i ~= j
                tmp = ar(i);
                ar(i) = ar(j);
                ar(j) = tmp;
            end
        end
    end
    i = i + 1;
    tmp = ar(i);
    ar(i) = ar(r);
    ar(r) = tmp;
end

我正在使用

调用此函数
ar = [7,7,3,0,3,1,4,7,5,6]
quickSort(ar, 1, 10)

在函数 parti 中,要根据枢轴对数组进行分区,您在尝试确定其正确位置时 包括枢轴本身

这在某些情况下导致无限递归,因为 枢轴只是在相邻位置之间交换,因为它 与自身比较 .

两种解决方案:

解决方案 1:

function [ar,i]= parti(ar,p,r)
    x=ar(r);
    i=p-1;

    for j=p:r-1 % Notice the r-1
        if ar(j) <= x
            i=i+1;
            if i~=j
    % ...

解决方案 2:

function [ar,i]= parti(ar,p,r)
    x=ar(r);
    i=p-1;

    for j=p:r
        if ar(j) < x % Notice the change in checking
            i=i+1;
            if i~=j
    % ...

我在这里深入探讨了如何优化您对 MATLAB 的使用、您应该如何更好地利用递归函数和重新起草代码。 要跳到快速排序函数的工作重写,请参阅下面的第 2nd 和第 4th 个代码块


在您的函数中使用 lowhigh 索引来跟踪您的分区并没有帮助。使用递归快速排序函数的全部要点是:

  1. 从数组 ar
  2. 中选取一个枢轴元素 ar(p)
  3. 拆分为 2 个数组,一个值小于 ar(p),一个值大于 ar(p)。相等的值可以放在任一数组中,因为它们将保留在枢轴旁边。
  4. Return 到第 1 步。对每个列表重复。

列表是自相似的,我的意思是将每个子列表视为需要排序的自己的列表,而不是更大列表的一部分!您不需要跟踪索引(当您深入 3 个递归时,索引最终会造成混淆),只需单独分析每个列表。

您可能会发现 Wikipedia article 很有用,因为它详细说明了这 3 点,并建议了一些最佳选择枢轴的方案。在下面的代码中,我只选择了中间的元素。


此外,您的分区函数 parti 确实 效率低下。在 MATLAB 中,如果您发现自己循环遍历数组的每个索引,那么您可能会加快速度。相反,使用逻辑索引向量化您的代码!

至少,在 documentation 中了解更多关于索引的知识,因为您可以像这样做整洁的事情:

% Your code for swapping array elements
tmp = arr(i);
ar(i) = ar(r);
ar(r) = tmp;
% Using MATLAB's indexing
ar([i,r]) = ar([r,i]);

编辑:虽然这展示了一种更 "MATLAB-esque" 的做事方式,you incur a performance hit assigning elements like this,所以如果您不介意更长的代码,请随意使用临时工!


您可以用这个 7 行函数替换所有代码,这样会更快。我添加了评论来解释逻辑...

function ar = qs(ar)
% Quicksort algorithm, for some 1D array "ar"
% First check we have more than one element, vital to terminate the recursion!
    if numel(ar) > 1
        % Choose some pivot index p, which we will split the array around
        p = ceil(numel(ar)/2);
        % Put values from "ar" which are smaller than "ar(p)" in one array
        smaller = ar(ar < ar(p));
        % Put values from "ar" which are larger than "ar(p)" in another array
        larger = ar(ar > ar(p));
        % Rebuild "ar" from a (qs-sorted) array of the smaller elemenets, then all
        % elements which were equal the pivot element (no need to sort these!) then 
        % a (qs-sorted) array of the larger elements.
        ar = [qs(smaller), ar(ar == ar(p)), qs(larger)];    
    end
end

这可以是 运行,如您的测试所示:

ar = [7,7,3,0,3,1,4,7,5,6];
qs(ar)
>> ans = [0 1 3 3 4 5 6 7 7 7]

我们可以通过不显式声明 smallerlarger 以及始终选择 p=1(尽管对于已经排序的数组来说这是最坏的情况)来使它更短。

function ar = qs(ar)
    if numel(ar) > 1
        ar = [qs(ar(ar < ar(1))), ar(ar == ar(1)), qs(ar(ar > ar(1)))];    
    end
end