【DS】排序算法的稳定性
主要的排序算法有八种:直接插入排序,希尔排序(这两种统称为插入排序),冒泡排序,快速排序(这两种统称为交换排序),直接选择排序,堆排序(这两种统称为选择排序),归并排序,基数排序。今天我们就讨论一下它们各自的稳定性。如果对算法不熟悉,可以查看我的另外几篇博客,然后再来阅读。
一、什么是算法稳定性
考察排序算法的时候有一个很重要的特性,就是算法的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
二、算法稳定性的重要性
算法稳定性为什么这么重要呢?
1)在实际的应用中,我们交换的不一定只是一个整数,而可能是一个很大的对象,交换元素存在一定的开销;
2)参照基数排序(后面会讲),不稳定排序是无法完成基数排序的,讲述完基数排序后,还会补充这里的原因。
三、八大算法的稳定性
1)直接插入排序@排序算法之插入排序(Insertion Sort)
其大致原理是:将数组分为无序区和有序区两个区,然后不断将无序区的第一个元素按大小顺序插入到有序区中去,最终将所有无序区元素都移动到有序区完成排序。
我们假设一个数组,元素已经排序为{1,5A,7,5B,9},其中前面三个已经排序完成,后面没有排序,即前面三个是有序区,后面两个是无序区,现在要将无序区的5B插入到有序区,则如果我们将元素插入到5A之前,我们需要往后移动两个元素,如果插入到5A之后,则需要移动一个元素,因此我们选择移动一个元素,而5A和5B也保持原来的顺序,因而直接插入排序是稳定的。
2)希尔排序@排序算法之希尔排序(Shell Sort)
其大致原理是:又称Gap缩小排序。先将序列按Gap划分为元素个数相同的若干组,使用直接插入排序法进行排序,然后不断缩小Gap直至为1,最后使用直接插入排序完成排序。希尔排序其实是直接插入排序的增强版。
我们来证明它是不稳定的,假设有一个数组{3,2A,2B,4},我们要升序排列,按照算法,第一次Gap=2,即可以分为{3,2B}和{2A,4}两组,然后对每一组进行插入排序,可以排序成{2B,2A,3,4},第二次Gap=1,由于插入排序是稳定的,所以2A和2B不会交换顺序了。由此可以看到,希尔排序是不稳定的。
3)冒泡排序@排序算法之冒泡排序(Bubble Sort)
其大致原理是:将序列划分为无序和有序区,不断通过交换较大元素至无序区尾完成排序。
熟悉冒泡排序的人一定知道,冒泡排序通过不断的交换元素,将无序区的最大(最小)元素往无序区搬运,因而和插入排序一样,为了减少其交换次数,冒泡排序是稳定的。
4)快速排序@排序算法之快速排序(Quick Sort)
其大致原理是:不断寻找一个序列的中点,将小于该中点的元素搬移到中点左边,大于该中点的元素搬移到中点右边,或者反过来。然后对中点左右的序列递归的进行排序,直至全部序列排序完成,使用了分治的思想。
关于算法的稳定性有一点本来是打算后面再讲的,但是讲到快速排序就一定要说了。读者肯定注意到了,前面的插入排序和冒泡排序完全可以实现为不稳定算法,只是在比较元素决定是否交换的时候,是否加上等于号而已。快速排序更加显示了这一点,解释如下:
在算法导论里面,快速排序选择都是元素序列的最后一个元素,假设元素序列如下{3,9,5A,6,8,5B},这种情况下,和上面的情况一下,稳不稳定还是看判断的时候是否出现等号,但是如果选择不是这样的,我们假设一种特殊状况:{3,9,5A,5B,6,8,5C},算法的实现是选择中间的5B作为中点,则不论等号与否,都是不稳定的。实际上,算法导论的选择是非常有意义的,了解其算法过程的人可以看到,这样的选择极大的降低了交换元素的复杂度和移动元素的次数。算法导论中是加了等号的,即≤最后一个元素的值被移到了左边,因而快速排序是稳定的。
5)直接选择排序@排序算法之选择排序(Selection Sort)
其大致原理是:将序列划分为无序和有序区,寻找无序区中的最小值和无序区的首元素交换,有序区扩大一个,循环最终完成全部排序。
我们还是假设一个序列{1,3,5,10A,10B,7},看这个数列,假设前面三个是有序区,后面三个是无序区,则无序区中最小的元素是7,和无序区的首元素交换10A交换,则可以看到序列变成了{1,3,5,7,10B,10A},然后继续,无序区就剩下{10B,10A},我们又可以看到,这里又是一个等号问题,同样,前面的交换是必然的,而后面的交换(如果等于也要交换)则不是必然的,为了减少元素交换,直接选择排序是不稳定的。
6)堆排序
其大致原理是:利用大根堆或小根堆思想,首先建立堆,然后将堆首与堆尾交换,堆尾之后为有序区。
考虑序列{9,5A,7,5B},按照堆排序的算法走一遍(算法导论中用的是最大堆,这个序列也是用最大堆来设计的),很快就可以发现,输出序列为{5B,5A,7,9},而且与等号无关,因此堆排序是不稳定的。
7)归并排序@排序算法之归并排序(Merge Sort)
其大致原理是:将原序列划分为有序的两个序列,然后利用归并算法进行合并,合并之后即为有序序列。
归并排序一样是稳定的,但是归并排序的稳定性并不是为了减少元素交换次数,因为它的算法实现中没有元素交换这一概念。
8)基数排序
其大致原理是:将数字按位数划分出n个关键字,每次针对一个关键字进行排序,然后针对排序后的序列进行下一个关键字的排序,循环至所有关键字都使用过则排序完成。具体请参见:算法总结系列之五: 基数排序(Radix Sort)
基数排序对多个关键字进行排序,并且这些关键字还是有优先级别的,对于整数来说,位数越高的数字优先级越高,而基数排序则是对优先级低的先排序,因此,基数排序对于整数是从个十百千万一个个去排序的。注意,这里必须使用稳定排序,否则,就会让原先的地位排序成果毁于一旦,最终的不到正确的排序结果。
基数排序不过是一种思想,其每一位的排序都需要稳定算法,否则无法得到正确的结果。
三、总结
算法稳定性到底为什么如此重要?上面提到的八种算法可以看到,其实很多算法都是可以实现稳定和不稳定两种情形的,那为什么选择稳定?一个基本原因就是减少元素交换次数,但是也有像归并排序这样的算法,与交换无关,那么稳定算法的意义在哪里呢?
稳定算法在单次排序的时候,意义并不显著,虽然上面提到减少元素交换,其实链表是可以避免这个消耗的,只不过操作比较复杂,其意义显示在基数排序中,即,我们要对多个关键词多次排序,这个时候,就一定要使用稳定算法。举一个现实的例子,比如排序的对象是人名,假设有以下两个人名:
- Smith, Alfred
- Smith, Zed
我们先按first name排序,再按照last name排序,按照first name排序完成以后,就是上面的样子,再去按照last name排序,如果算法不稳定,则顺序极就会颠倒,是不是?这里的last name和first name完全可以抽象成基数排序的不同位,不是稳定算法,就不能得到正确结果。
【DS】排序算法的稳定性的更多相关文章
- JS实现常用排序算法—经典的轮子值得再造
关于排序算法的博客何止千千万了,也不多一个轮子,那我就斗胆粗制滥造个轮子吧!下面的排序算法未作说明默认是从小到大排序. 1.快速排序2.归并排序3.冒泡排序4.选择排序(简单选择排序)5.插入排序(直 ...
- 八大排序算法Java
目录(?)[-] 概述 插入排序直接插入排序Straight Insertion Sort 插入排序希尔排序Shells Sort 选择排序简单选择排序Simple Selection Sort 选择 ...
- [Data Structure & Algorithm] 八大排序算法
排序有内部排序和外部排序之分,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存.我们这里说的八大排序算法均为内部排序. 下图为排序 ...
- 排序算法 ----(转载::http://blog.csdn.net/hguisu/article/details/7776068)
1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表.即:先将序列的第1个记录看成是一个有序 ...
- Java常用排序算法+程序员必须掌握的8大排序算法
概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大, ...
- C++:主要几种排序算法及其复杂度
常见排序算法稳定性和复杂度分析快速简记以及转载 分类: 算法 2012-02-07 22:18 399人阅读 评论(1) 收藏 举报 算法mergeshell http://blogold.chin ...
- C#排序算法的比较
首先通过图表比较不同排序算法的时间复杂度和稳定性. 排序方法 平均时间 最坏情况 最好情况 辅助空间 稳定性 直接插入排序 O(n2) O(n2) O(n) O(1) 是 冒泡排序 O(n2) O(n ...
- 8种排序算法的C#实现
排序是将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列.排序根据涉及的存储器的不同分为内部排序和外部排序:内部排序是指待排序记录存放在内存进行的排序过程:外部排序是指待排序记录的数 ...
- 排序算法总结及Java实现
1. 整体介绍 分类 排序大的分类可以分为两种,内排序和外排序.在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序.主要需要理解的都是内排序算法: 内排序可以分为 ...
随机推荐
- DOM的基本操作
什么是DOM 1:文档对象模型(DocumentObjectModel,DOM) 2:DOM定义了访问和操作HTML文档的标准方法. 3:DOM将HTML 文档表达为树结构. 其他查询元素的方法: d ...
- [转帖] “王者对战”之 MySQL 8 vs PostgreSQL 10
原贴地址:https://www.oschina.net/translate/showdown-mysql-8-vs-postgresql-10?lang=chs&page=2# 英文原版地址 ...
- gitlab搭建与配置说明
1. 概述 Gitlab分为社区版和企业版,此次安装的是社区版(gitlab-ce). 2. 准备 本次使用系统为Ubuntu16.04. 3. 安装 添加GitLab仓库,并安装到服务器上(将git ...
- std::shared_ptr 和普通指针的转换
相互转化见示例 struct test { int num; string name; }; test* pTest = new test(); std::shared_ptr<test> ...
- jenkins--svn+添加钩子去触发jenkins的job工作
找到svn钩子脚本 post-commit: 添加一个接口: /usr/bin/curl http://admin:admin@x.x.x.x:8080/job/svn/buildWithParame ...
- 经典Java面试题收集
1.面向对象的特征有哪些方面? 答:面向对象的特征主要有以下几个方面: 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面.抽象只关注对象有哪些属性和行为,并不关注这些 ...
- Codeforces Round #420 (Div. 2) A,B,C
A. Okabe and Future Gadget Laboratory time limit per test 2 seconds memory limit per test 256 megaby ...
- MT【61】含参数二次函数最大最小值
评:此类题目在高考中作为压轴题也曾考过,一般通性通法都如上面的做法,但是我们如果可以站在包络的角度,很多问题将变得很清晰:
- 【luogu4320】道路相遇 (圆方树 + LCA)
Description 给你一张\(~n~\)个点\(~m~\)条边的无向图,保证无重边无自环, 共\(~q~\)组询问求\(~x~\)到\(~y~\)的路径上必经的点数. Solution ...
- [luogu3258][JLOI2014]松鼠的新家
题解 我们就在\([a_i,a_{i+1}]\)的路径上都\(+1\),然后单点查询就可以了. ac代码(吸了氧才过的QwQ) # include <bits/stdc++.h> # de ...