在待排序的文件中,若存在多个关键字相同的记录,通过排序后这些具备相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的;若具备相同关键字的记录之间的相对次序发生改变,则称这种排序方法是不稳定的。即全部相等的数通过某种排序方法后,仍能保持它们在排序以前的相对次序,则说这种排序算法是稳定的,反之,就是不稳定的。html
稳定的排序算法以下表所示: 算法
稳定的排序shell |
时间复杂度数组 |
空间复杂度数据结构 |
冒泡排序(bubble sort)ide |
最差、平均都是O(n^2),最好是O(n)ui |
1url |
插入排序(insertion sort)spa |
最差、平均都是O(n^2),最好是O(n)3d |
1 |
归并排序(merge sort) |
最差、平均、最好都是O(n log n) |
O(n) |
桶排序(bucket sort) |
O(n) |
O(k) |
基数排序(Radix sort) |
O(dn)(d是常数) |
O(n) |
二叉树排序(Binary tree sort) |
O(n log n) |
O(n) |
不稳定的排序算法以下表所示:
不稳定的排序 |
时间复杂度 |
空间复杂度 |
选择排序(selection sort) |
最差、平均都是O(n^2) |
1 |
希尔排序(shell sort) |
O(n log n) |
1 |
堆排序(heapsort) |
最差、平均、最好都是O(n log n) |
1 |
快速排序(quicksort) |
平均是O(n log n),最差是O(n^2) |
O(log n) |
1、冒泡排序
冒泡排序(BubbleSort)的基本概念是:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。而后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(由于可能因为第2个数和第3个数的交换,使得第1个数再也不小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已是最大的),第二趟结束,在倒数第二的位置上获得一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。
代码实现以下:
2、插入排序
插入排序的基本思想是每步将一个待排序的记录按其排序码值的大小,插到前面已经排好的文件中的适当位置,直到所有插入完为止。插入排序方法主要有直接插入排序和希尔排序。
直接插入排序具体算法描述以下:
1.从第一个元素开始,该元素能够认为已经被排序
2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
3. 若是该元素(已排序)大于新元素,将该元素移到下一位置
4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5. 将新元素插入到下一位置中
6. 重复步骤2
伪码描述以下:
3、归并排序
归并排序是将两个或两个以上的有序子表合并成一个新的有序表。初始时,把含有n个结点的待排序序列看做由n个长度都为1的有序子表组成,将它们依次两两归并获得长度为2的若干有序子表,再对它们两两合并。直到获得长度为n的有序表,排序结束。
归并操做的工做原理以下:
一、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
二、设定两个指针,最初位置分别为两个已经排序序列的起始位置
三、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
四、重复步骤3直到某一指针达到序列尾
五、将另外一序列剩下的全部元素直接复制到合并序列尾
代码实现以下:
4、桶排序
桶排序的基本思想就是把区间[0,1)划分红n个相同大小的子区间,或称桶,而后将n个输入数分布到各个桶中去。由于输入数均匀分布在[0,1)上,因此通常不会有不少数落在一个桶中的状况。为获得结果,先对各个桶中的数进行排序,而后按次序把各桶中的元素列出来便可。
在桶排序算法的代码中,假设输入是个含n个元素的数组A,且每一个元素知足0≤A[i]<1。另外还须要一个辅助数组B[O..n-1]来存放链表实现的桶,并假设能够用某种机制来维护这些表。
个人理解是:桶排序至关于一个N路的归并排序,首先将输入按均匀分布分到N个桶中,每个桶都用一个链表来维护,并用插入排序对每一个桶(也就是每一路)进行排序,最后将N个有序桶合并成一个,即得最终的排序结果。
伪码实现以下:
5、基数排序
设单关键字的每一个份量的取值范围均是C0<=Kj<=Crd-1(0<=j<=rd),可能的取值个数rd称为基数.基数的选择和关键字的分解因关键字的类型而异.
(1)若关键字是十进制整数,则按个、十等位进行分解,基数rd=10,C0=0,C9=9,d为最长整数的位数.
(2)若关键字是小写的英文字符串,则rd=26,C0='a',C25='z',d为最长字符串的长度.
基数排序的基本思想是:从低位到高位依次对待排序的关键码进行分配和收集,通过d趟分配和收集,就能够获得一个有序序列.
基数排序从低位到高位进行,使得最后一次计数排序完成后,数组有序。其原理在于对于待排序的数据,总体权重未知的状况下,先按权重小的因子排序,而后按权重大的因子排序。例如比较时间,先按日排序,再按月排序,最后按年排序,仅需排序三次。可是若是先排序高位就没这么简单了。基数排序源于老式穿孔机,排序器每次只能看到一个列,不少教科书上的基数排序都是对数值排序,数值的大小是已知的,与老式穿孔机不一样。将数值按位拆分再排序,是无聊并自找麻烦的事。算法的目的是找到最佳解决问题的方案,而不是把简单的事搞的更复杂。基数排序更适合用于对时间、字符串等这些总体权值未知的数据进行排序。
个人理解是:基数排序算法中,数据可分解为d个因子,每一个因子对排序结果都有影响(即权重),先按权重小的因子进行排序,后按权重大的因子进行排序,全部因子排序完即得结果。如时间可分解为三个因子:日、月、年,先按日对时间排序,再按月对时间排序,最后按年对时间进行排序,便可。注意:每一趟按因子进行的排序都必须是稳定的!
伪码实现以下:
6、二叉树排序
二叉排序树(Binary Sort Tree)又称二叉查找树。它或者是一棵空树;或者是具备下列性质的二叉树:
(1)若左子树不空,则左子树上全部结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上全部结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
个人理解是:二叉树排序,即先建一个二叉排序树,而后中序遍历,即获得一个从小到大的排序结果。
插入结点:
一、首先执行查找算法,找出被插结点的父亲结点。
二、判断被插结点是其父亲结点的左、右儿子。将被插结点做为叶子结点插入。
三、若二叉树为空。则首先单独生成根结点。
PS:新插入的结点老是叶子结点。依次插入数据即完成建树。
伪码实现以下:
删除结点:
将结点z从二叉排序树中删除,分三种状况讨论:
一、若是结点z没有子女,则修改其父结点p[z],使NIL为其子女;
二、若是结点z只有一个子女,则能够经过在其子结点与父结点间创建一条链来删除z;
三、若是结点z只有两个子女,先删除z的后继y(它没有左子女),再用y的内容来替代z的内容。
PS:某一结点x的后继即具备大于key[x]中的关键字中最小者的那个结点,即中序遍历顺序下的后继
伪码实现以下:
7、选择排序
选择排序的基本思想是每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到所有待排序的数据元素排完。选择排序中主要使用直接选择排序和堆排序。
直接选择排序的过程是:首先在全部记录中选出序码最小的记录,把它与第1个记录交换,而后在其他的记录内选出排序码最小的记录,与第2个记录交换......依次类推,直到全部记录排完为止。
代码实现以下:
希尔(Shell)排序的基本思想是:先取一个小于n的整数d1做为第一个增量把文件的所有记录分红d1个组。全部距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;而后,取得第二个增量d2<d1重复上述的分组和排序,直至所取的增量di=1,即全部记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。
通常取d1=n/2,di+1=di/2。若是结果为偶数,则加1,保证di为奇数。
代码实现以下:
9、堆排序
堆的定义:n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列知足以下性质(简称为堆性质):
(1) ki≤K2i 且 ki≤K2i+1
或(2)Ki≥K2i 且 ki≥K2i+1(1≤i≤ n)
若将此序列所存储的向量R[1..n]看做是一棵彻底二叉树的存储结构,则堆实质上是知足以下性质的彻底二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
根结点(堆顶)的关键字是堆里全部结点关键字中最小者,称为小根堆;根结点的关键字是堆里全部结点关键字中最大者,称为大根堆。
用大根堆排序的基本思想以下:
一、先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
二、再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此获得新的无序区R[1..n-1]和有序区R[n],且知足R[1..n-1].keys≤R[n].key
三、因为交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。而后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此获得新的无序区R[1..n-2]和有序区R[n-1..n],且仍知足关系R[1..n-2].keys≤R[n-1..n].keys,一样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。
伪码实现以下:
10、快速排序
快速排序采用了一种分治的策略,一般称其为分治法,其基本思想是:将原问题分解为若干个规模更小但结构与原问题类似的子问题。递归地解这些子问题,而后将这些子问题的解组合为原问题的解。
快速排序的具体过程以下:
第一步,在待排序的n个记录中任取一个记录,以该记录的排序码为准,将全部记录分红两组,第1组各记录的排序码都小于等于该排序码,第2组各记录的排序码都大于该排序码,并把该记录排在这两组中间。
第二步,采用一样的方法,对左边的组和右边的组进行排序,直到全部记录都排到相应的位置为止。
代码实现以下: