快速排序思路及代码解释

1,303 阅读2分钟

为什么要学习快速排序

快排,可以说是排序效率最高的一种排序,时间复杂度为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){
  	.....
  }

到这里,如果还有不懂的,可以留言评论,或者我的代码有什么不好的地方都欢迎各位大神指教