新的一年就快到了,给大家拜一个早年,祝大家身体健康,万事如意,恭喜发财,学业有成,事业进步
快速排序
常见两种快速排序的套路,大致思想是一致的,同样采用了分治思想,在内部遍历交换的时候有一点区别,为了便于描述,我们把这两种常见的办法称为 方案A 和 方案B
- 主要体现在标准位(默认为数组首位 index = 0)何时被操作,方案A 是最开始就进行赋值,方案B则是最后两个指针相遇后 (low == high)才对标准位进行赋值。
- 还有区别在,方案B 是 low 和 high 的值进行调换;方案A 则是单向的赋值,仅仅是 high 的值赋值给 low ,或者 low 赋值给 high。注意:不是调换 low 和 high 的值。
两种办法大同小异,殊途同归。
快速排序主要利用了分治思想,将数组按某个中轴位置划分,中轴左侧的数均小于中轴右侧的数。再对中轴左侧部分的数组进行排序,顺便找出下一个中轴;对中轴右侧部分的数据进行排序,顺便找出中轴;... 直到数组无法再分割为止。
方案 A
图解原理
-
起始状态,默认 low = 0 ,high = 数组.length -1
-
一般选中 low 的位置作为参考值,这边参考值 refer = 7。这个值需要记住,在最后 low = high 的时候,把 refer 赋值给中轴位置。
-
既然参考值选择了 low,那就需要由 high 指针先动了,向左移动,找到一个小于 refer 值的位置停下来,将 high 的值赋值给当前 low 的位置。
-
接着,low 指针开始向右移动,移动到大于 refer 的位置停下来,将 low 的值赋给 high 的位置。high 被覆盖的值,并没有丢,而是在 第上一步的 low 位置,而最初的 low 又是 refer
-
最终, low = high 的时候,我们暂且把此位置称为中轴,把 refer 赋值给中轴位置。单次操作结束。
此时,中轴左侧的数都是 小于等于 中轴的,而中轴右侧的数总的大于等于 中轴的。我们再把中轴左侧的数组按上面的流程进行排序,此时 low = 0,high = 中轴-1。把中轴右侧的数组也进行上述排序。以此递归
,直到 low < high 不成立。则整个数组完成从小到大排序。
注意,此方案只有单向的赋值,并没有 low 和 high 调换。
代码实现
public static int[] quickSortA(int[] array, int low, int high) {
if (low < high) {
int pivot = part(array, low, high);
quickSortA(array, low, pivot - 1);
quickSortA(array, pivot + 1, high);
}
return array;
}
/**
* @param array
* @param low
* @param high
* @return
*/
private static int part(int[] array, int low, int high) {
int refer = array[low];
while (low < high) {
while (array[high] >= refer && high > low) {
high--;
}
array[low] = array[high];
while (array[low] <= refer && high > low) {
low++;
}
array[high] = array[low];
}
array[low] = refer;
return low;
}
照旧,在完成代码后,走一下该有的单元测试。
方案 B
方案 B 和 方案 A 实现方式非常的相似,看了之后可能有混淆的反作用,万一后续面试答错了那就尴尬了。可以不看 Ctrl + W
学海无涯,回头是岸 = =。
图解原理
- 首先,依然是 起始状态,默认 low = 0 ,high = 数组.length -1
- 一般选中 low 的位置作为参考值,这边参考值 refer = 7。
- 依然是 high 先动,向左移动,遇到 小于 refer 的时候,停下来;
- 接着是 low 像右移动,遇到大于 refer 的时候停下来,互换 low 和 high 的值,注意是互换。
- 若 low 在右移的过程中一直都没遇到大于 refer 的值,则移动到 low 和 high 相遇。
上述移动过程有一个约束条件,low 和 high 一旦相遇,也就是找到中轴位置,那本次遍历访问就结束。中轴的值和当次数组头(也就是low的最初位置)互换 。
找到中轴位置后,照旧,分别处理中轴左侧和右侧部分的数组,直到不能再被下一个中轴分割(新的 low < high 不成立)
代码实现
public static int[] quickSortB(int[] array, int low, int high) {
if (low < high) {
int refer = array[low];
int tempLow = low;
int tempHigh = high;
while (tempLow < tempHigh) {
while (tempHigh > tempLow && array[tempHigh] >= refer) {
tempHigh--;
}
while (tempHigh > tempLow && array[tempLow] <= refer) {
tempLow++;
}
int temp = array[tempHigh];
array[tempHigh] = array[tempLow];
array[tempLow] = temp;
}
array[low] = array[tempLow];
array[tempLow] = refer;
//递归中轴左侧的数组
quickSortB(array, low, tempHigh - 1);
//递归中轴右侧的数组
quickSortB(array, tempHigh + 1, high);
}
return array;
}
思考
为什么每次都是 high 指针先动 ?
答:因为我们选择的基准是 low。
-
针对方案 A:非常直接。按逻辑,我们每次找到 low 或者 high 满足条件的时候都会进行赋值操作,若从 low 开始动,找到大于基准的位置,就直接将 low 赋值给 high,这时候 high 还没有动过,high 的值是不确定的。 相反,若 high 先动,那 low 的起始值是可以确定的,也就是我们的基准值 refer。相等的,随便换,没有破坏规则。
-
针对方案 B:high 先动可以保证最后的中轴位置的值一定小于或等于基准值。而中轴最后要和基准位互换值,必然中轴的值必须小于等于基准值才能满足要求。(排序结束中轴左侧的值必须都小于等于中轴值)。
high 每次都是移动到小于基准值再停下,或者挪到和 low 碰到,low 停的地方值必然小于(上次刚刚和 high 换过位置) 或等于(low 从没移动过)基准值。
等于基准值的情况是 high 从最开始一路挪过来都没碰到小于基准的坐标,在本次数组的头部直接和 low 相遇。这种情况也满足我们的要求,中轴就是 low 起始位,也就是数组 low 后面的值都是大于或等于它的。
不完全统计,掌握使用 A 的人多于 B,A 也更优雅,不用那个多局部变量存状态。赶紧把方案 B 忘了吧
参考文章:
招聘广告 🐂
【优酷】杭州团队,长期招聘!!!
- 前端「急」
- Java 后端 「爆」
- 移动端:安卓 & iOS 「热」
办公地点:蚂蚁Z空间。
面试方式:电话&视频优先。
主要有优酷少儿、创新项目等业务,P6/P7 都有。
想试一试的小伙伴,邮件联系 hdtpjhz@163.com