首先,排序算法的稳定性大家应该都知道,通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj, Ai原来在位置前,排序后Ai还是要在Aj位置前。为了简便下面讨论的都是不降序排列的情形,对于不升序排列的情形讨论方法和结果完全相同。

其次,说一下稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。另外,如果排序算法稳定,对基于比较的排序算法而言,元素交换的次数可能会少一些(个人感觉,没有证实)。

回到主题,现在分析一下常见的排序算法的稳定性,每个都给出简单的理由。

(1)冒泡排序

冒泡排序是通过相邻比较、实时交换、缩小范围实现排序的。第1次操作n个元素,通过相邻比较将0~n-1中的最大元素交换到位置n-1上,第2次操作n-1个元素,通过相邻比较将0~n-2中的最大元素交换到位置n-2上……第n-1次操作2个元素,通过相邻比较将0~1上的最大元素交换到位置1上完成排序。在相邻比较时如果两个元素相等,一般不执行交换操作,因此冒泡排序是一种稳定排序算法。

(2)选择排序

选择排序是通过不断缩小排序序列长度来实现的。第1次操作n个元素,选择0~n-1中的最小者交换到位置0上,第2次操作n-1个元素,选择1~n-1中的最小者交换到位置1上……第n-1次操作2个元素,选择n-2~n-1上的最小者交换到位置n-2上完成排序。在每次选择最小元素进行交换时,可能破坏稳定性。这种情况可以描述为:约定要发生交换的位置称为当前位置,被交换的位置称为被交换位置,被交换位置上的元素为选中的最小元素。如果当前位置之后和被交换位置之前存在与当前位置相等的元素,执行交换后就破坏了稳定性。如序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

(3)插入排序

插入排序是通过不断扩大排序序列的长度来实现的。第1次操作1个元素,直接放到位置0上即可;第2次操作2个元素,在0~1上为当前元素找到合适位置并插入;第3次操作3个元素,用在0~2上为当前元素找到合适位置并插入它……第n次操作n个元素,在0~n-1上为当前元素找到合适位置并插入完成排序。讨论元素的插入过程,假设当前是第n次操作,要在0~n-1上为当前元素寻找合适位置,设置一个工作指针初始化为n-1,向前移动工作指针直到遇到一个不大于当前元素的元素,就在这个元素的后面插入当前元素,仔细体会这个插入过程,不难理解插入排序是稳定的。

(4)快速排序
       快速排序有两个方向,左边的i下标当a[i] <= a[center]时一直往右走,其中center是中枢元素的数组下标,一般取为当前排序段的第一个元素。而右边的j下标当a[j] > a[center]时一直往左走。如果i和j都走不动了,这时必有结论a[i] > a[center] >= a[j],我们的目的是将a分成不大于a[center]和大于a[center]的两个部分,其中前者位于左半部分后者位于右半部分。所以如果i>j(i不能等于j,为什么?)表明已经分好,否则需要交换两者。当左右分好时,j指向了左侧的最后一个元素,这时需要将a[center]与a[j],交换,这个时侯可能会破坏稳定性。这种情形可以这样描述:center位置之后j位置前存在与j相等的元素,指向center与j的交换后,稳定性破坏。比如序列为 5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法。

(5)归并排序
       归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。

(6)基数排序
      基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以其是稳定的排序算法。

(7)希尔排序(shell)
       希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

(8)堆排序
      我们知道基于0序的堆结构,节点i的孩子为2*i+1和2*i+2节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n/2-1, n/2-2, ...1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。

通过上面的论述不难发现规律:存在不相邻交换的排序算法一般是不稳定的,相邻交换的排序算法一般是稳定的;对于相邻交换的稳定排序算法,通过控制交换条件可以转换成不稳定排序算法;冒泡、插入、归并和基数排序是稳定的;选择、快速、希尔和堆排序是不稳定的。

各种排序算法稳定性的探讨相关推荐

  1. 各种排序算法稳定性的总结!!!

    这几天笔试了好几次了,连续碰到一个关于常见排序算法稳定性判别的问题 ,往 往还是多选,对于我以及和我一样拿不准的同学可不是一个能轻易下结论的题目,当 然如 果你笔试之前已经记住了数据结构书上哪些是稳定 ...

  2. 各种排序算法稳定性比较

    前面有讲到了9种排序算法: 1.简单选择排序 2.堆排序        (1和2是属于选择排序) 3.直接插入排序 4.希尔排序     (3和4属于插入排序,有时把改进后的直接插入排序叫做二分插入) ...

  3. 常用排序算法稳定性、时间复杂度分析

    1. 选择排序.快速排序.希尔排序.堆排序不是稳定的排序算法,      冒泡排序.插入排序.归并排序和基数排序是稳定的排序算法. 2.研究排序算法的稳定性有何意义? 首先,排序算法的稳定性大家应该都 ...

  4. 排序算法稳定性和不稳定性的理解

    稳定性初解 说到稳定性,与之对应就是不稳定性,那么排序算法的稳定性又为何意呢?通俗地讲就是,能保证排序前两个相等的数其在序列的前后位置顺序与排序后它们的前后位置顺序一致.形式化解释如下:一列数中,如果 ...

  5. 8种常用排序算法稳定性分析

    选择排序.快速排序.希尔排序.堆排序不是稳定的排序算法 冒泡排序.插入排序.归并排序和基数排序都是稳定的排序算法. [1]为什么要区分排序算法的稳定性? 排序算法的稳定性通俗地讲就是能保证排序前两个相 ...

  6. 排序算法稳定性的好处

    排序算法稳定性概念: 在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中 r[i] = r[j],且r[i]在r[j]之前,而在排序后的序列中, ...

  7. 常用排序算法稳定性分析

    [1]选择排序.快速排序.希尔排序.堆排序不是稳定的排序算法 冒泡排序.插入排序.归并排序和基数排序都是稳定的排序算法. [2]研究排序算法的稳定性有何意义? 首先,排序算法的稳定性大家应该都知道,通 ...

  8. 八大排序算法稳定性分析

    八大排序算法稳定性分析 稳定性定义: 排序前后两个相等的数相对位置不变,则算法稳定. 稳定性的好处: 从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用. 各排序算法的稳 ...

  9. 数据结构的各种排序算法稳定性比较

    一.前面有讲到了9种排序算法: 1.简单选择排序 2.堆排序 (1和2是属于选择排序) 3.直接插入排序 4.希尔排序 (3和4属于插入排序,有时把改进后的直接插入排序叫做二分插入) 5.冒泡排序 6 ...

  10. 常用的8种排序算法稳定性分析

    [1]选择排序.快速排序.希尔排序.堆排序不是稳定的排序算法 冒泡排序.插入排序.归并排序和基数排序都是稳定的排序算法. [2]研究排序算法的稳定性有何意义? 首先,排序算法的稳定性大家应该都知道,通 ...

最新文章

  1. 探索 | 神经网络到底是如何思考的?MIT精英们做了这么一个实验室来搞清楚
  2. 多个if语句并列_P009 python基础之控制语句01
  3. python【力扣LeetCode算法题库】1111- 有效括号的嵌套深度
  4. Android 的全盘加密容易破解
  5. 关于memcached
  6. ios开发时,在Xcode中添加多个targets进行版本控制
  7. 强大而优雅,API 研发管理 EOLINKER 新版正式发布!
  8. 超过响应缓冲区限制_Nginx如何限制并发连接数和连接请求数?
  9. mysql checkpoint_MySQL checkpoint深入分析
  10. JavaScript综述
  11. NB-IOT智能云家具项目系列实站
  12. 图文详解YUV420数据格式
  13. 微信小程序学习并搭建
  14. Python基础_闭包和迭代器
  15. 使用MyQR和qrcode来制作二维码
  16. SQL计算月环比、月同比
  17. 报错:module ‘brotli‘ has no attribute ‘error‘
  18. html中让两段文字并列排放,言语理解:探究阅读之并列文段
  19. D3.js的v5版本入门教程(第一章)—— 如何在项目中使用D3.js
  20. 计算机网络--数据链路层[微课堂]

热门文章

  1. 更新新网卡驱动,修复win7雷凌网卡Ralink RT3290在电脑睡眠时和启动网卡时出现蓝屏netr28x.sys驱动文件错误...
  2. 远程桌面连接的几种方法
  3. SetMutableGraph
  4. HCIE-RS面试--MAC地址漂移及应对
  5. cisco 的网络地址转换技术(NAT)
  6. 在windows的IDE中使用linux命令行
  7. 数据库管理工具 Navicat使用教程:导航窗格提示和技巧 - 管理连接
  8. 同一进程不同线程之间的资源共享与独享
  9. Extjs的radio单选框的使用
  10. getAttribute 与getParmeter 区别