推广 热搜: page  关键词  红书  哪些  数据分析  链接  搜索  获取  小红  服务 

快速排序详解及优化方式

   日期:2024-12-17     作者:j3opa    caijiyuan   评论:0    移动:https://sicmodule.kub2b.com/mobile/news/8719.html
核心提示:目录 1.快速排序的核心思想 2.快速排序的两个重要方法 Hoare法 quickHoare函数(想办法把基准放到数组正确排序好的位置&#x

目录

1.快速排序的核心思想

2.快速排序的两个重要方法

Hoare法

quickHoare函数(想办法把基准放到数组正确排序好的位置

代码

挖坑法

逻辑

翻译成代码

3.快速排序的时间/空间复杂度分析

最好情况

最坏情况

总结

空间复杂度分析

4.基于减少递归的优化方法

三数取中

末尾换用直接插入排序

快速排序的稳定性


下面都以升序为例进行讲解

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为

1.在数组中任取一个元素作为基准(基准一般选取最左边的元素

想办法排列数组让所有小于基准的元素都放到基准的左边所有大于基准的元素都放到基准的右边,完成后,基准元素就排列好了。

2.采用分而治之的思想,分别对基准左区间的元素基准右区间的元素进行1中的操作,从而完成排序。

传入的数组作为唯一的接口,对quickSortHoare方法进行实现

 

核心思想中提到了分治,所以大概率我们是要递归定义这个quickHoare方法的

 

先不关心quickHoare函数的具体实现和细节,把总体思路捋一遍以上的代码就是核心思路了只要把quickHoare实现函数,快速排序就搞定了

quickHoare函数(想办法把基准放到数组正确排序好的位置

默认最左边的元素为基准(stand)

left下标和right下标分别指向最左和最右

{1 3 4 5 6 7 9}--》这是排序好的数组,下面看看quickHoare函数,怎么把没排好序的数组中6放到4的位置的

1.先让right指针做移,直到arr[right]<stand或者左右指针相遇,停下:

2.然后再让left右移,直到arr[left]>stand或者左右指针相遇,停下:

3.到了这里,左右两个指针都停下,arr[left]和arr[right]两个元素进行交换:

4.在重复上述right和left的移动逻辑,直到,left与right相遇一定会相遇

5.遇到上面这个情况实际上就是循环终止条件,直接把arr[left](当然arr[right]也是可以的)和6进行交换即可

此时,元素“6”的位置已经排好,在对1 3 4 5和7 9进行分治采用相同的方法即可达到排序目的。

代码

 

Hoare方法的整体代码

 

不知道小伙伴们有没有这样的困惑

1、能让left先走,然后right再走吗

2、为什么arr[left]<=stand 并且arr[right]>=stand ,小于或者等于不可以吗

首先第一个问题,我们还是用前面那个数组作为样例,进行升序(基准设置成最左端元素

让left先走,然后是right,其他逻辑不变

交换

left先走:

哦豁,按照这样,6因该放到7的位置,显然是错的

根据1 3 4 5 6 7 9,应该放到4的位置(倒数第三个位置)。

这通过这个例子,证明left先走是不可取的。

注意&总结

1、选择不同的基准(stand,right和left俩指针移动的先后顺序可能不同

只要基准选择的不是尾元素,即不是最右端的元素,right先移动。

反之如果基准选取尾元素,也就是最右端的元素,left先移动。


2、不论以那个元素为基准right指向比stand小的值就停下,left指向比stand大的值就停下,另外两个指针相遇时也得停下(如果以降序为例,反之亦然


其次,第二个问题,left>=stand或者right<=stand就停下,以这个数组为例

{ 1 ,1, 1, 1, 1}

如果是这种重复的数组,那么left和right就不可能相遇了,会死循环的。

逻辑

顾名思义就是挖坑,举个例子

此时的3,就被挖走了,放到了tmp中(当然这是抽象的理解,实际上3还在数组的原来位置)。

接下来的逻辑就和hoare法差不多,先让right走,但是因为arr[right]=2<3所以,right就不走了,然后把arr[right]“挖走”,填到arr[left]中,然后再让left++

此时arr[right]形成了新的坑(当然也是抽象理解,实际装的还是2

我们继续尝试让left前进,但是arr[left]=7>3了,所以left停下

挖走arr[left]填给arr[right],然后right往上移动

循环上面的逻辑

right移动到1的位置停止

挖走arr[right]填到arr[left],然后left往下移动

此时left==right,退出循环,把tmp填进坑

翻译成代码

 
 

快速排序的时间复杂度比较特殊,它不是很稳定,最坏情况和最好情况我们都要了解。

假设有N个元素,且恰好定义的基准,排序好的位置在数组的中间

一共有logN+1层

每一层都有N个元素要被right和left走完

所以每一层都要执行N此,因此时间复杂度是

O(fast=N*(logN+1)=N*logN+N

取最高阶O(fast=N*logN

和堆排序的时间复杂度是一样的不过快速排序实际上要比堆排序快一些,在最好情况下。

什么是最坏情况

我们可以分离变量,来理解一下,首先对于一个数组进行快速排序,那么他的执行次数至少是N次因为left=0,right=数组长度-1,即使递归子树,但是每层的子树之和次数也是N

所以每一层的执行次数,永远是N次,那我们只用考虑啥时候层数是最多的就可以了

我来举个最坏情况的例子

O(slow=N^2

这种最坏情况可以说就是一个冒泡排序了

快速排序的时间复杂度是O(n log n),其中n为待排序数组的长度。快速排序的每次划分操作需要O(n)的时间复杂度,而递归调用的次数为O(log n),因此总的时间复杂度为O(n log n)。在最坏的情况下,即数组已经有序的情况下,快速排序的时间复杂度为O(n^2)。

实际上快速排序的这三个方法

定义的变量都是常量级的,是O(1)我们不用管。重要的是,quickHoare方法需要递归,递归要开辟栈帧,所以快速排序的空间复杂度和递归的深度有关。

最优情况时间复杂度为O(logN)-->每次均等分割

最差情况时间复杂度为O(N)

如图与时间复杂度最坏情况类似

想要优化快速排序,从时间复杂度分析中,我们直到,关键就 在于如果减少递归次数。

核心思路就是,选取三个数中中间大的那个数字,作为key(基准,尽量从中间分割数组,从而减少递归次数。

方式也很简单,写一个getMid方法,返回一个靠中间的元素的下标即可

private static int getMid(int[] arr,int left,int right){//一定是有效的闭区间
    int mid=(left+right)/2;
 //arr[left] arr[right] arr[mid]三个数组返回中间大的
    }
}

依据mid的位置来写,就不容易乱了

 

在使用,hoare方法的时候调用getMid方法,来获取基准即可

private static int partitionHoare(int[] arr,int left,int right){//返回排序好元素的下标

    int i=left;
    int mid=getMid(arr,left,right);
    swap(arr,mid,left);//把获得的基准和最左侧交换,这样好管理,也好理解
    int stand=arr[left];//基准默认是最左边的元素
}

先来将两个引子

第一个,我们先来看一下这颗树

一共有7个节点,最后一层就占据了4个,是总结点数的一般还多,如果总结点数更大,这个占比可能更大。

第二个,学过直接插入排序,我们都知道,当一个数组趋近于有序,他的排序速度会很快(时间复杂度是O(n)


学习了上面的直接快速排序,我们都知道,快排需要像二叉树一样递归进行,也会和第一个引子类似,在最后几层,递归次数非常大,而我们使用的又是递归,在测试用例很大的情况下,很可能出现栈溢出。

所以,能不能这样

在最后几层我们就不递归了,改用,直接插入排序来代替,虽然直接插入排序最坏情况下,时间复杂度是O(O^2),但是,实际上在最后几层,整个数组,已经被快速排序排列的差不多了,所以这个思路没有问题。

 

如果直接插入排序还没有学的小伙伴可以看一下这个篇博客,有详细介绍

只要把_insert方法,放到递归的方法中,设置一个条件来调用即可

 

优化后快速排序的整体代码

本文地址:https://sicmodule.kub2b.com/news/8719.html     企库往 https://sicmodule.kub2b.com/ , 查看更多

特别提示:本信息由相关用户自行提供,真实性未证实,仅供参考。请谨慎采用,风险自负。

 
 
更多>同类最新资讯
0相关评论

文章列表
相关文章
最新动态
推荐图文
最新资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报  |  鄂ICP备2020018471号