快速排序是一种经常被用到的排序算法,许多语言中的sort函数也是基于快速排序实现的。因此在面试中,时常会看到要求手写快速排序的题目。
时间复杂度O(nlogn)
快速排序的主要思路为:
- 选择一个数
selected
作为界限,我们的目标是在完成一轮排序后,数组左边的值都小于这个选定的数,数组右边的值都大于它。 - 设置两个指针分别在最左和最右。
- 若左右指针未到达同一位置则循环(
while(left<right)
),对于右侧指针不断左移,如果指向的value[right]<selected
则停止循环,因为我们前面说过右边的值都要大于选中的值,此时找到了右边却小于选中的值,把它丢到左边也就是value[left]=value[right]
。 - 现在右指针不动。由于刚刚的赋值操作,可以想象右边的数组槽已经空了,等待左边值中比
selected
大的值传过来。因此对于左侧指针不断右移,直到找到比选中值大的,放进右指针指向的空槽value[right]=value[left]
。 - 重复执行3、4,直到左右指针相遇。
- 相遇的地方表示在这里左侧的值都比选中值小,右侧的值都比选中值大,因此此处放入选中的值即可。
- 对选中值左右两边的剩余数组部分进行递归排序。
以《算法笔记》中的例子为例进行作图。
一开始数组如图所示,此处将5作为selected数选中,左指针就指向5,右指针指向4。
接下来右指针开始循环找到比5小的值,4就是,因此此时将4赋值给左指针指向的位置也就是第一个。
左指针也开始循环找比5大的值,找到了9,因此将9赋值给右指针所在的位置,即此时的最后一个。
就是这样,继续循环。
左指针位置 | 右指针位置 | 操作 |
---|---|---|
2,数字为9 | 5,数字为9 | 右指针左移找到小于5 的值 |
2,数字为9 | 4,数字为1 | 将1放在左指针位置处,左指针右移,此时数组为[4,3,1,6,1,9] |
3,数字为6 | 4,数字为1 | 将6放在右指针位置处,右指针左移,此时数组为[4,3,1,6,6,9] |
3,数字为6 | 3,数字为6 | 左右指针重合,将5放在此处,数组为[4,3,1,5,6,9] |
这样数组就实现了选定的数(5)左边的值都小于5,右边的数都大于5。
细心的同学可能发现,我在前面的主要思路上将选择加粗了。因为选择的方法有很多,此处选择了第一个数,还可以随机选择,对算法性能有影响噢。
有了上述过程的理解,相信代码一定是小菜一碟。
var quickSortPart = function(array,left,right){
let temp = array[0];
while(left<right){
while(left<right && array[right]>temp)right--;
array[left] = array[right];
while(left<right && array[left]<temp)left++;
array[right] = array[left];
}
array[left] = temp;//这里已经重合了,left写right也可以
return left;//返回相遇时的下标
}
//递归调用
var quickSort = function(array,left,right){
if(left<right){
let pos = quickSortPart(array,left,right);
quickSort(array,left,pos-1);
quickSort(array,pos+1,right);
}
}