为什么要学习快速排序
快排,可以说是排序效率最高的一种排序,时间复杂度为O(nlogn),java,js等语言的数组对象的sort方法底层,就是用了快排来实现的(当数组长度大于10用快排,小于10用插入排序),而且大厂面试也经常见到快排的身影,所以学习快排还是有一定必要的
快排的基本思路
网上有很多中不同角度理解的快排,不过,万变不离其宗
快排最基本的思想就是:随机选择一个基准元素,把数组里所有小于这个基准元素的,放在它左边,大于基准元素的放在它的右边,然后再递归的去对这个基准元素的左数组和右数组分别进行快排
记住这一点,你快排就学的差不多了
不过思路看起来很好理解,实际上,代码很难写,你要怎么用最高的效率,把数组中小于基准数的通通移到它的左边,把数组中大于基准元素的通通移到它的右边? 这里才是快排的难点所在。
实现代码
说了这么多,先上代码
function QuickSort(arr,start,end){
if(start<end){
let left = start;
let right = end;
let flag = arr[start];
while(left < right){
while(left < right && arr[right] >= flag){
right--;
}
if(left<right)arr[left++] = arr[right];
while(left < right && arr[left] <= flag){
left++;
}
if(left<right)arr[right--] = arr[left];
}
arr[left] = flag;
QuickSort(arr,start,left - 1);
QuickSort(arr,left + 1,end);
}
}
代码没注释,不要紧,我一行一行解释 首先讲一下大概的工作流程,我们的目标是,把数组中所有小于基准元素的数移动到它的左边,把数组中所有大于基准元素的数移动到它的右边。
好,正文开始,这里我们用到了一种东拆西补的思想,基准元素随机选对吧?那我们就直接选最左边的元素为基准元素,下一步,划重点,我们定义两个左右指针,左指针用于找比基准元素大的元素,右指针用于找比基准元素小的元素,左指针开始时指向起点,右指针开始时指向终点 到底怎么个东拆西补法?因为我们选择了最左边第一个元素作为基准元素,将他用另一个变量flag保存起来
此时你可以进行想象,左指针现在指向的元素(基准元素)是一个漏洞,因为它的元素已经被保存了,现在我们右指针慢慢往左移动,当找到第一个比基准元素小的点,把右指针指向的元素补到左指针处
while(left < right && arr[right] >= flag){
right--;
}
if(left<right)arr[left++] = arr[right];
这一步看懂了没?找一个比基准元素小的,补到左指针处,那比基准元素大的是不是不用动?然后!!!此时的右指针是不是又变成了一个漏洞?它的元素已经到别的地方去了,需要补全,此时我们左指针慢慢往右移动,找到第一个比基准元素大的,补到右指针处
while(left < right && arr[left] <= flag){
left++;
}
if(left<right)arr[right--] = arr[left];
到这里,应该都懂了东拆西补的方法了吧?最后结束条件:左右指针碰撞,此时你就可以把基准元素放进左或右指针里面,因为它两是漏洞,补进去,这样就完成了一轮东拆西补,成功的把小的全部移到左边,把大的移到右边。
但是,注意,此时基准元素左右两边的数还不是有序的,它们只是比基准元素大或者小而已,所以要对基准元素的左右两侧数组进行下一轮的快排:
QuickSort(arr,start,left - 1);
QuickSort(arr,left + 1,end);
最后,递归结束条件:假如你要排序的数组只有一个元素,是不是就可以不用排序了?所以,只有当start<end时我们才进行排序
if(start<end){
.....
}
到这里,如果还有不懂的,可以留言评论,或者我的代码有什么不好的地方都欢迎各位大神指教