快速排序可以说是面试常考题了,这里主要以“小白”的视角,用直白的“图画”来解释快速排序中“分区”的具体操作步骤。
这里的代码来自于社区整理的资料:sort.hust.cc/6.quicksort
代码:
const arr = [9, 11, 12, 13, 8, 14, 7, 6, 5, 15, 3];
function partition(arr, low, high) {
const p = arr[low];
while (low < high) {
while (low < high && arr[high] > p) {
high--;
}
arr[low] = arr[high];
while (low < high && arr[low] <= p) {
low++;
}
arr[high] = arr[low];
}
arr[low] = p;
return low;
}
function quickSort(arr, low, high) {
if (low < high) {
// 1. 找到分区的基准下标
const pivotIndex = partition(arr, low, high);
// 2. 给分区继续分区
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, high);
}
return arr;
}
const newArr = quickSort(arr, 0, arr.length - 1);
这里不仅解释 partition 函数具体在做什么,还要回答两个问题:
- (1)外层 while 循环中,为什么是 low < high 而不是 low <= high?
- (2)为什么内层循环的 while 中,需要再加 low < high 的判断?不加会怎么样?
让我们循序渐进,用图示来回答上面两个问题。先从 partition 内的第一个语句出发:
下面关键的点来了,我们试图来回答一下问题(2)为什么需要在内层 while 中增加 low < high 的问题:
如果在此时的步骤 12 中,我们去掉 low < high 判断条件,或者 low <= high,会发生什么?
好的,所以我们已经知道为什么内层 while 循环需要增加 low < high 的问题了,我们继续回到步骤12:
停!我们来看一下问题(1)为什么是外层 while 循环需要的是 low < high 而不是 low <= high?
如果 low === high,我们继续循环,只会重复 arr[low] = arr[high], 还有 arr[high] = arr[low] 语句,没有意义。所以,一旦 low === high,我们跳出循环,得到了 low ,即为 pivot 归位的下标位置。
小结:通常学习算法的时候,都知道大致的思路,但是写起来还是会“恐惧”。为什么呢,因为不知道如何进行边界判断。这就需要我们不断地去训练、扣细节,才能提升对边界判断的敏感度,有效消除这种“恐惧感”。