【问题标题】:stack overflow with quicksort implementation [duplicate]快速排序实现的堆栈溢出[重复]
【发布时间】:2019-10-24 18:55:24
【问题描述】:

我正在尝试实现快速排序算法(在 C# 中),这是我的代码:

public static void sort(ref int[] A)
{
    if (A.Length==0 || A.Length==1) 
    { 
        return; 
    }
    quickSort(ref A,0,A.Length-1);
}

private static void quickSort(ref int[] A, int left, int right)
{
    if (left >= right || left < 0 || right < 0) 
        return;

    int pivot = A[left];

    int i = left - 1;
    int j = right + 1;

    while (true) 
    {
        do 
        { 
            i++; 
        } 
        while (A[i] < pivot);

        do 
        { 
            j--; 
        } 
        while (A[j] > pivot);

        if (i >= j) 
            break;

        int temp = A[i];
        A[i] = A[j];
        A[j] = temp;
    }

    quickSort(ref A,left,j);
    quickSort(ref A, j + 1, right);
}

这个算法工作得很好,但是有些东西似乎不合逻辑,我选择pivot 等于A[left] 然后在while(true) 循环中我写了do{i++}while(A[i]&lt;pivot),我现在注意到第一次它会增加@ 987654327@ 所以A[i] 将等于A[left] 这是枢轴值,所以这应该意味着这个do while 循环将在i 的第一个增量之后停止,所以当我尝试将= 运算符添加到while 使其在第一次递增后不会停止:while(A[i]&lt;=) 我遇到了堆栈溢出异常(当我将相等运算符添加到第二个 do while 时,也遇到了堆栈溢出异常:while(A[j]&gt;=pivot)),我不明白为什么会这样,谁能解释一下?

【问题讨论】:

  • 请注意,ref 是不必要的,因为您永远不会分配给变量 A。 (ref 影响变量,而不是值。)
  • 我们将不得不保留它,因为我的老师说它,但我很想知道你所说的“ref 影响变量,而不是值”@Ry-
  • @DanielK see 那里。
  • @EdPlunkett 是的,我的代码工作正常,当我将 = 运算符添加到 while(true) 内的两个 while 之一时,它停止工作并给我一个堆栈溢出异常
  • 了解调试器的好时机。当它抛出堆栈溢出异常时,上一个堆栈并查看本地值是否符合您的预期。如果不是,那么您很可能处于无限循环(递归)

标签: c# algorithm stack-overflow quicksort


【解决方案1】:

这是霍尔分区方案。 do while 循环需要使用 ,而不是 =,因为它们依赖于找到等于中枢的枢轴或元素来停止循环并防止循环超出范围 [lo,你好]。通常将中间元素用作枢轴,例如

    int pivot = A[(left+right)/2];   // or A[left + (right-left)/2]

在当前代码中,唯一不能用于枢轴的元素是 A[right],因为这会导致堆栈溢出(快速排序调用最终会卡在 high = low + 1)。

所以使用 pivot = A[left],第一个 do while 以 i == left 停止,第二个 do while 以 j = j 以检查分区步骤是否完成。

为枢轴选择中间元素有助于某些数据模式,例如已排序的数据或反向排序的数据。尽管如此,如果左元素 == 枢轴,第一个 do while 将在第一个循环上停止。

请注意,Hoare 分区方案仅将分区拆分为元素 = 枢轴。枢轴或任何等于枢轴的元素可以在分区步骤之后的任何位置结束。这不是问题,因为最终枢轴或等于枢轴的元素最终会位于正确的位置(直到达到低 == 高的递归级别时才会发生这种情况)。

【讨论】:

  • 但是当我使用while( A[i] &lt; pivot) 时,这并不意味着do while 第一次将i 增加1,这意味着i 将等于left 所以@ 987654327@ 将等于A[left] 等于pivot,这是否意味着这个do while 将在第一次增量后停止,那么算法怎么可能工作?
  • @DanielK - 我更新了我的答案以解释是的,如果pivot = A [left],或者如果A [left] == pivot,它将第一次停止。这不是问题,因为枢轴将被交换到 A[j](除非 i >=j 第一次)。
最近更新 更多