什么是快速排序
个人认为,本质上就是“冒泡”的高级版。
它的原则是:
- 从排序的数列中,选一个数作为“基准数”,个人更喜欢叫它“基数”
- 根据选定的“基数”,分为 2 部分。比“基数”大的放一边,比“基数”小的放到另一边。
- 分别对 2 部分数列,重复步骤 1、2。直到队列中只有一位数时。
代码说话
public void sort(int left, int right, int... sortNums) {
if (left >= right) {
return;
}
// 确定“基”数为左边的第一个
int flag = sortNums[left];
// 确定最左边和最右边的索引,当做探测哨兵。
int probeLeft = left;
int probeRight = right;
// 根据“基”数,分为 2 部分,左边的是比它小的,右边时比它大的。
// 直到哨兵相遇,才退出循环
while (probeLeft < probeRight) {
// 右哨兵先行动,顺序不可乱。
// 在未和左哨兵相遇,且探测到某数值比“基数”小【之前】,往左哨兵方向一直探索。否则停止行动,等待左哨兵行动
// 即暂停的情况为:①两个哨兵相遇。②右哨兵探测发现的数值比“基数”小。
// 往前探测的情况为:两个哨兵未相遇,且右哨兵探测发现的数值比“基数”大。
while (probeLeft < probeRight && sortNums[probeRight] >= flag) {
// 右哨兵,往左哨兵地盘行动
probeRight--;
}
// 左哨兵等待行为原则和右哨兵是恰恰相反的。
while (probeLeft < probeRight && sortNums[probeLeft] <= flag) {
// 左哨兵,往右哨兵地盘行动
probeLeft++;
}
// 当左右哨兵未相遇,交换左右哨兵探测到的数位置。
// 左哨兵探测值是比“基数”大。右哨兵探测值是比“基数”小。
if (probeLeft < probeRight) {
int temp = sortNums[probeLeft];
sortNums[probeLeft] = sortNums[probeRight];
sortNums[probeRight] = temp;
}
}
// 因为是根据“基数”分为2部分,所有需要把“基数”放在【划分点】
sortNums[left] = sortNums[probeLeft];
sortNums[probeLeft] = flag;
// 继续处理左边的。
sort(left, probeLeft - 1, sortNums);
// 继续处理右边的。
sort(probeLeft + 1, right, sortNums);
}
时间复杂度
最坏为
O(N2)
最好为
O(NlogN)
总结
左哨兵和右哨兵,他们职能各不相同。左为探测比“基数”大的,右为探测比“基数”小的。当两哨兵都完成了当前探测任务时,互换探测到的数值位置。继续进行下一回合探测。