推广 热搜: 可以  page  搜索引擎  企业  个数  百度  使用  音视频  选择  行业 

排序篇(七大基于比较的排序算法)

   日期:2024-12-25     移动:http://ww.kub2b.com/mobile/quote/10324.html

目录

插入排序

直接插入排序

希尔排序(缩小增量排序)

选择排序

选择排序

堆排序

交换排序

冒泡排序

快速排序

1.挖坑法

2.Hoare版

3.前后指针

快速排序优化

三数取中法 选基准数

2.递归到小的子区间时 可以考虑使用插入排序

非递归快速排序

归并排序

归并排序----非递归

其他非基于比较的排序

计数排序


几种常见的排序算法

        1.插入排序:直接插入排序、希尔排序

        2.选择排序:选择排序、堆排序

        3.交换排序:冒泡排序、快速排序

        4.归并排序:归并排序

接下来我们一一讲解

思想

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

简单点说 就是在已排好序的区间中 找到当前元素(无序区间)的应插入位置

代码实例

特性

时间复杂度:O(N^2)

空间复杂度:O(1) 它是一种稳定的排序算法

这里我们对稳定做下解释 当一个数组中 当采用某一排序算法时 重复数据原先在前面的仍在前面 这便称之为稳定

关于时间复杂度的分析

· 最好情况下 也就是有序的时候 我们不需要移动元素 每次只需要比较一次即可 那么最好情况时的时间复杂度为O(N)

· 最坏情况下 也就是待排序区间为逆序的情况

例如: 9  8  7  6  5 (需要排为升序)

此时需要比较 1+2+3+4=10次

如果有n个元素的话 则需要比较1+2+3+....+(n-1)=n*(n-1)/2

所以最坏时间复杂度为:O(N^2)

空间复杂度分析

插入排序不需要额外的存储空间 所以其空间复杂度为O(1)

稳定性分析

根据插入排序思想可知 我们只会移动比temp值大的元素 所以我们排序后可以保证相同元素的相对位置不变 所以直接插入排序是稳定的

思想:先选定一个整数gap  把待排序文件中所有记录分成多个组 所有距离为该整数gap的记录分在同一组内 并对每一组内的记录进行排序 然后 重复上述分组和排序的工作

直到该整数=1时 所有记录最后再统一排好序

特性

1.希尔排序是对直接插入排序的优化

2.当gap>1时 都是预排序 目的是 让数组更接近于有序 当gap==1时 数组已经接近有序 这样就会很快 这样整体而言 可以达到优化的效果

3.希尔排序的时间复杂度不好计算 因为gap的取值方法有很多 导致很难去计算 因此在好些书中给出的希尔排序的时间复杂度都不固定

4.当然 希尔排序是不稳定的

代码实例

基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素 存放在序列的起始位置 直到全部待排序的数据元素排完

例如升序:每一次从待排序的数据元素中 找到最小的那个元素 存放在序列的起始位置

代码1

关于选择排序 我们还有另一种方法:同时找到最大和最小的元素 的下标 每次排好两个元素

代码实例

时间复杂度:O(N^2)

空间复杂度:O(1)

稳定性:不稳定

堆排序(HeapSort)是指利用堆积树(堆:完全二叉树)这种数据结构所设计的一种排序算法 它也是选择排序的一种 它是通过堆来进行选择数据

需要注意的是:排升序要建大堆  排降序要建小堆

现在我们来理解一下 什么是大/小堆

大堆:每个结点的值都不大于其父节点的值(也就是 结点的值较大)

小堆:每个结点的值都不小于其父节点的值(也就是 结点的值较小)

大堆(结点的值较大)

数组{6,7,9,4,5,1,3}建好大堆如下图

小堆(结点的值较小)

看了这两张图后 我们发现 即使建好大堆/小堆后 仍然需要排序

大概流程建大堆(升序)---->然后需要将根节点和从最后一个元素进行交换(根节点的值是最大的)  然后再重新建大堆  直到每个元素都与根节点交换过

现在呢 我们先进行建大堆

首先呢 先创建parent child两个变量

parent指向最后一棵树的根   child指向最后一棵树的子树  然后依次将每棵树置成大堆

:让child指向子树元素较大的 若child指向位置的元素大于parent处的元素 则交换

然后令parent-- 直到parent走到0下标处

接下来我们给出上述图片转化为大堆的过程图

当child指向超过下标最大值时 siftDown结束

展示代码

在创建好大堆以后 我们就需要将根节点的数 与end指向的元素 进行交换

然后再重排大堆 让end--

整体代码

时间复杂度:O(N*logN)

空间复杂度:O(1)

稳定性:不稳定

这里我们就不对冒泡排序进行讲解了 在之前有讲过

如果大家有些遗忘 可以通过下列链接进行复习

基本思想:任取待排序元素序列中的某元素作为基准值 按照该基准值 将待排序集合分割成两个子序列 左子序列中所有元素均小于基准值 右子序列中所有元素均大于基准值 然后重复该过程 直到所有元素都排列在相应位置上为止

1.挖坑法

我们先给个排序过程图

代码实现

2.Hoare版

这个跟挖坑法也很相似 只是没有坑 当选择L处的元素作为基准值时 先让R移动 找到小于基准值的数 让L移动 找到大于基准值的数 然后让L、R两处的元素进行交换 当L、R相遇时 让相遇处的元素与基准数进行交换 然后在L、R相遇处的左右两侧接着重复上述步骤

3.前后指针

初始时 prev指针指向序列开头  cur指针指向prev指针的后一个位置

cur指针向后移动 寻找比基准值小的元素

当找到比基准值小的元素时 如果 ++prev和cur不相等 则交换prev和cur位置处的元素

这样做的原因是:cur和prev本身相差一个距离 当cur碰到几个大于基准值的元素时 prev就会与cur的距离增加几个距离 prev会停留在大于基准值处  当cur再次找到比基准值小的元素时 则可以把小于基准值的元素与大于基准值的元素进行交换 直到cur走到数组之外

代码实例

总结:在这三种方法中 我们使用挖坑法最多 其次是Hoare法 至于前后指针已经使用的很少了

对于快速排序的过程 其实可以形象地用递归树来表示

具体来说 快速排序的递归树中的每个结点代表一次递归调用 左子树代表 对小于基准值的子数组进行排序的递归调用 右子树代表对大于基准值的子数组进行排序的递归调用

  1. 三数取中法 选基准数

我们前面的基准数 选择的都是Left处的元素(也就是每个数组的最左侧元素)

但基准数当选择的过大或者过小时 会让数组排序的次数增加

如下图

我们可以看到当选择的基准数过大时 会导致每次只排好一个元素或者很少的元素

而如果当选择的基准数为中间的数时 每次会排好一半的元素  大大减少了时间

所以我们三数取中法是 在最左侧、最右侧和中间的数中 选择某一位于两者区间的数

代码实例

2.递归到小的子区间时 可以考虑使用插入排序

我们知道快速排序 每次会数组分成两个部分 类似于二叉树

这里 我们借用下之前的图

在这里我们更加明显的看到了二叉树的样子

我们把每个圈当成一个数组 每次排序就会分成两个数组

当某一数组被分成另外几个数组时 也就是某一次递归 递归到剩余的几次 我们就可以采取直接插入排序  

我们在前面都是用递归实现快速排序的

现在我们来学习一下非递归的快速排序

我们知道在使用递归的快速排序中 递归起到了将数组一分为二的作用

所以我们的非递归排序的核心就是 如何将数组一分为二

这里我们需要一个栈来配合

先利用自已写过的代码(partition)找到基准数要替换到的位置--pivot

注意:partition在找到基准值要替换的位置的同时 也进行着将小于基准数的划分到左边 将大于基准数的划分到其右边

让基准数左右两侧数组的起始和结尾的下标入栈

注意:当基准数某一侧只剩下一个元素时 代表着已经有序 不需要再进行排序  

当pivot满足pivot>left+1或pivot<right-1 代表着至少有两个元素---》即需要入栈 排序

代码如下

时间复杂度:O(N*logN)

空间复杂度:O(logN)

稳定性:不稳定

基本思想

归并排序(Merge Sort)是一种分而治之的排序算法。它将一个大的列表分成两个小列表,分别进行排序,然后将排序好的小列表合并成一个最终的排序列表。这个过程递归地进行,直到子列表的大小为1(因为只有一个元素的列表自然是排序好的

归并排序算法主要包含两个主要步骤

分解:将数组分解成两个较小的子数组,直到子数组的大小为1。

合并:将已排序的子数组合并成一个大的有序数组,直到合并为1个完整的数组

过程实例

代码实现

排序篇(七大基于比较的排序算法)

在递归的归并排序中 递归的作用在于将数组进行分解

所以我们在 非递归中主要是将数组进行分解

我们利用gap当作每个子数组的大小

利用left mid right来指示要合并的区间

代码实现

时间复杂度:O(N*logN)

空间复杂度:O(N)

稳定性:稳定

思想:计数排序又称为鸽巢原理 是对哈希直接定址法的变形应用

操作步骤

  1. 统计相同元素出现次数
  2. 根据统计的结果将序列回收到原来的序列中

例如

但也有一个问题 假如数组arr存放的是 {90,95,91,92,90,92}  tmp数组难道还要申请100个空间吗

其实不然  我们仍然只需要申请max-min+1个空间 即:6个空间(下标:0~5)

只需要在放入tmp数组时 让 下标=元素-min

在tmp数组中 取出元素放入arr的时候 让元素=下标+min 即可

代码实现

计数排序总结

  1. 计数排序在数据范围集中时 效率很高 但是适用范围及场景有限
  2. 时间复杂度:O(N+(arr的长度))
  3. 空间复杂度:O(arr的长度)
  4. 稳定性:稳定

排序算法总结

本文地址:http://ww.kub2b.com/quote/10324.html     企库往 http://ww.kub2b.com/ ,  查看更多

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


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