提问者:小点点

我在快速排序算法中缺少什么


我试图使快速排序算法通过选择第一项作为枢轴始终在分区函数中,并返回该枢轴项的索引以在快速排序函数中使用…我不知道为什么我面临这个错误

函数分区(项目,左,右){^RangeError:超过最大调用堆栈大小

整个代码如下

var items = [5, 3, 6, 7, 2, 9];

function swap(items, leftIndex, rightIndex) {
  var temp = items[leftIndex];
  items[leftIndex] = items[rightIndex];
  items[rightIndex] = temp;
}

function partition(items, left, right) {
  var pivot = items[0];

  i = left;
  j = right;
  while (i < j) {
    do {
      i++;
    } while (items[i] <= pivot);
    do {
      j--;
    } while (items[j] > pivot);

    if (i < j) {
      swap(items, i, j);
    }
  }
  swap(items, 0, j);

  return j;
}

function quickSort(items, left, right) {
  var index;
  if (items.length > 1) {
    index = partition(items, left, right); //index returned from partition

    if (left < index - 1) {
      //more elements on the left side of the pivot
      quickSort(items, left, index - 1);
    }
    if (index < right) {
      //more elements on the right side of the pivot
      quickSort(items, index, right);
    }
  }
  return items;
}
var sortedArray = quickSort(items, 0, items.length - 1);
//console.log(sortedArray); / / expected[(2, 3, 5, 6, 7, 9)];

共1个答案

匿名用户

如果分区(项目,左,右)是为了获取整个数组(项目)及其左右两端的索引,那么我认为支点不应该是项目[0],而应该是项目[左]。同样,在最后,它应该是交换(项目,左,j)

但是,请注意,如果分区中的数组将其最小的元素作为其第一个元素,例如[5,7,6,9],则会发生什么。然后5将是支点,i将落在7上,j将落在5上,并且j=左将返回,此时在快速排序中您将拥有index=左,然后您将执行快速排序(项目,索引,右),即快速排序(项目,左,右),从而导致无限递归。因此,您应该执行快速排序(项目,索引1,右),因为支点已经在它的正确位置上,不应该进一步移动。

这些更改后,代码如下所示:

var items = [5, 3, 6, 7, 2, 9];

function swap(items, leftIndex, rightIndex) {
  var temp = items[leftIndex];
  items[leftIndex] = items[rightIndex];
  items[rightIndex] = temp;
}

function partition(items, left, right) {
  var pivot = items[left];
  console.log("partition: " + items + " " + left + " " + right);

  i = left;
  j = right;
  while (i < j) {
    do {
      i++;
    } while (items[i] <= pivot);
    do {
      j--;
    } while (items[j] > pivot);

    if (i < j) {
      swap(items, i, j);
    }
  }
  swap(items, left, j);

  return j;
}

function quickSort(items, left, right) {
  console.log("quickSort: " + items + " " + left + " " + right);
  var index;
  if (right - left > 1) {
    index = partition(items, left, right); //index returned from partition

    if (left < index - 1) {
      //more elements on the left side of the pivot
      quickSort(items, left, index - 1);
    }
    if (index + 1 < right) {
      //more elements on the right side of the pivot
      quickSort(items, index + 1, right);
    }
  }
  return items;
}

var sortedArray = quickSort(items, 0, items.length - 1);
// returns [2,3,5,7,6,9]

这意味着分区有错误,我把这个问题留给你。也许维基百科或G4G网站会帮助你编写正确的分区算法。(提示:这与ij分区中相互传递有关,导致了最初的问题。

但这不是结束!

如果您每次都选择第一个元素作为支点,您可以得出相同的错误。想象一下,数组已经排序,所以假设输入是[1,2,3,4,5]。然后在第一次调用快速排序时,您将数组划分为1和[2,3,4,5]。然后,您将在右侧数组上递归调用该函数,将其划分为2和[3,4,5]等等。

这意味着,如果你的数组很大(比如,它有100万元素),那么你将有一个非常大的堆栈大小(至少有200万个函数调用,100万个分区和100万个快速排序)。这是由于不幸的枢轴选择。

要解决这个问题,您应该随机化支点。这样,您的堆栈大小大于对数的机会非常小(基本上为零),并且问题可以毫不费力地解决。