数据结构(23)--排序篇之插入排序
2016-03-08 17:50
260 查看
参考书籍:数据结构(C语言版)严蔚敏吴伟民编著清华大学出版社
1.查找L.r[i]在有序子序列L.r [1..i-1]中的插入位置j;
2.将L.r [j..i-1]中的记录后移一个位置;
3.将L.r [i]复制到L.r [j]的位置上。
整个排序过程进行n–1趟插入,即:先将序列中的第1个记录着成一个有序的子序列,然后从第2个记录起逐个插入,直至整个序列变成接关键字非递减有序序列为止。
对于直接插入排序:
最好情况“比较”次数:n-1;“移动”次数:2(n-1)
最坏的情况“比较”和“移动”的次数均达到最大值,分别为:(n+2)(n-1)/2;(n+4)(n-1)/2
由于待排记录序列是随机的,取上述二值的平均值。所以直接插入排序的时间复杂度为
O(n^2)。
直接插入排序是“稳定的”:关键码相同的两个记录,在整个排序过程中,不会通过比较而相互交换。
。折半插入排序是“稳定的”。
运行结果:
希尔排序的时间复杂度较直接插入排序低。希尔排序的分析是一个复杂的问题,因为它的时间是和所取“增量”序列的函数密切相关。到目前为止,还没有求得一种最好的增量序列,但有大量的局部结论。
注意:应使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须等于1。
由于希尔排序对每个子序列单独比较,在比较时进行元素移动,有可能改变相同排序码元素的原始顺序,因此希尔排序是不稳定的。
1.直接插入排序
1.1基本思想
一趟直接插入排序的基本思想: 将记录L.r[i]插入到有序子序列L.r[1..i-1]中,使记录的有序序列从L.r[1..i-1]变为L.r[1..i]。 完成这个“插入”分三步进行:1.查找L.r[i]在有序子序列L.r [1..i-1]中的插入位置j;
2.将L.r [j..i-1]中的记录后移一个位置;
3.将L.r [i]复制到L.r [j]的位置上。
整个排序过程进行n–1趟插入,即:先将序列中的第1个记录着成一个有序的子序列,然后从第2个记录起逐个插入,直至整个序列变成接关键字非递减有序序列为止。
1.2代码实现
package sort.insertionSort; public class StraightSort { /** * @param args */ //对顺序表L做直接插入排序 public static void InsertSort(int[] L){ //先将第一个元素看成是一个有序子序列 for(int i = 2; i <= L.length-1; i++){ //在已经有序的1->i-1的子序列中插入第i个元素,以保证仍然有序,成为一个1->i的有序子序列 L[0] = L[i];//监视哨 int j = i-1; /* for(; j > 0; j--){//没有利用监视哨,仍然用j>0作为条件以避免数组下标越界 if(L[0] < L[j]) L[j+1] = L[j]; else break; } */ for(; L[0] < L[j]; j--) L[j+1] = L[j];//利用监视哨 //当L[0] >= <[j]时跳出循环,由于j做了一次自减,所以是L[j+1] = L[0], //当是因为=而跳出循环时(j后来没有自减),L[0]插在L[j]的后面以保证了“稳定” L[j+1] = L[0]; } } public static void main(String[] args) { // TODO Auto-generated method stub int[] test = {0, 53, 27, 36, 15, 69, 42}; //0号单元未使用 InsertSort(test); for(int i = 1; i <= test.length-1; i++) System.out.print(test[i]+" "); } }运行结果:
1.3性能分析
直接插入排序性能分析 ,实现排序的基本操作有: (1)“比较” 关键字的大小 (2)“移动”记录对于直接插入排序:
最好情况“比较”次数:n-1;“移动”次数:2(n-1)
最坏的情况“比较”和“移动”的次数均达到最大值,分别为:(n+2)(n-1)/2;(n+4)(n-1)/2
由于待排记录序列是随机的,取上述二值的平均值。所以直接插入排序的时间复杂度为
O(n^2)。
直接插入排序是“稳定的”:关键码相同的两个记录,在整个排序过程中,不会通过比较而相互交换。
2.折半插入排序
2.1基本思想
考虑到 L.r[1..i-1] 是按关键字有序的有序序列,则可以利用折半查找实现“ L.r[1…i-1]中查找 L.r[i] 的插入位置”如此实现的插入排序为折半插入排序。折半插入排序在寻找插入位置时,不是逐个比较而是利用折半查找的原理寻找插入位置。待排序元素越多,改进效果越明显。2.2代码实现
package sort.insertionSort; public class BinaryInsertionSort { /** * @param args */ //对顺序表L做折半插入排序,利用折半查找快速找到要插入的位置 public static void binaryInsertSort(int[] L){ for(int i = 2; i <= L.length-1; i++){ //利用折半查找找到要插入的位置 int low = 1, high = i-1;//在1->i-1的有序子序列中插入第i个元素,使之成为1->i的有序子序列 L[0] = L[i];//暂存要插入的元素 while(low <= high){ int mid = (low+high)/2; if(L[0] < L[mid]) high = mid -1; else //L[0] >= L[mid] low = mid+1;//等于当成大于处理,这样后出现的相等值就会排在后面,从而到达“稳定” } //此时high = low-1,且high+1即low的位置即为要插入的位置 for(int j = i-1; j >= low; j--) L[j+1] = L[j]; L[low] = L[0]; } } public static void main(String[] args) { // TODO Auto-generated method stub int[] test = {0, 53, 27, 36, 15, 69, 42}; //0号单元未使用 binaryInsertSort(test); for(int i = 1; i <= test.length-1; i++) System.out.print(test[i]+" "); } }运行结果:
2.3性能分析
折半插入排序减少了关键字的比较次数,但记录的移动次数不变,其时间复杂度与直接插入排序相同,时间复杂度为O(n^2)。折半插入排序是“稳定的”。
3.希尔排序(缩小增量排序)
3.1基本思想
希尔排序(Shell Sort)又称为“缩小增量排序”。其基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。3.2代码实现
package sort.insertionSort; public class ShellSort { /** * @param args */ //对顺序表L做一趟希尔排序,本算法和一趟直接插入排序相比,做了如下修改: //1.前后记录位置的增量式dk,而不是1 //2.L[0]只是暂存单元,而不再是哨兵 public static void shellInsert(int[] L, int dk){ /*对于下面for循环的i = i+dk和i++的分析: 一趟希尔排序里有L.length/dk个子序列,每个子序列要进行直接插入排序,即要进行L.length/dk个直接插入排序 子序列轮着来(有点并发的感觉),即第一个子序列的第2个数排完序,然后是第2个子序列的第2个数排序,然后是第3个子序列。。。 i继续自增,然后是第1个子序列的第3个数往第一个子序列的1->2的有序子序列里插入并排序,然后是第2个子序列的第3个数 往第2个子序列的1->2的有序子序列里插入并排序,然后是第3个子序列的第3个数往第3个子序列的1->2的有序子序列里插入并排序。。。 i继续自增,然后是第1个子序列的第4个数往第一个子序列的1->3的有序子序列里插入并排序,接着 第2个子序列的第4个数往第2个子序列的1->3的有序子序列里插入并排序.。。。。。以此类推,直到所有的完成 */ //相当于每个子序列的第一个数都被看成是每个子序列的有序子序列 for(int i = 1+dk; i <= L.length-1; i++){ L[0] = L[i]; //找L[i]应该插入的位置 int j = i-dk; for(; j>0&&L[0]<L[j]; j-=dk) L[j+dk] = L[j]; L[j+dk] = L[0]; } } //按增量dlta[0......len(dlta)-1]对顺序表L做希尔排序 public static void shellSort(int[] L, int[] dlta){ for(int i = 0; i < dlta.length; i++) shellInsert(L, dlta[i]); } public static void main(String[] args) { // TODO Auto-generated method stub int[] test = {0, 65, 49, 97, 25, 25, 13}; //0号单元未使用 int[] dlta = {3, 2, 1};//应使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须等于1。 shellSort(test, dlta); for(int i = 1; i <= test.length-1; i++) System.out.print(test[i]+" "); } }
运行结果:
3.3性能分析
虽然我们给出的算法是三层循环,最外层循环为log2n数量级,中间的for循环是n数量级的,内循环远远低于n数量级,因为当分组较多时,组内元素较少;此循环次数少;当分组较少时,组内元素增多,但已接近有序,循环次数并不增加。因此,希尔排序的时间复杂性在O(nlog2n)和O(n^2 )之间,大致为O(n^1. 3)。希尔排序的时间复杂度较直接插入排序低。希尔排序的分析是一个复杂的问题,因为它的时间是和所取“增量”序列的函数密切相关。到目前为止,还没有求得一种最好的增量序列,但有大量的局部结论。
注意:应使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须等于1。
由于希尔排序对每个子序列单独比较,在比较时进行元素移动,有可能改变相同排序码元素的原始顺序,因此希尔排序是不稳定的。
相关文章推荐
- Android Binder机制(二) Binder中的数据结构
- R语言中的数据结构
- 阿里巴巴实习内推二面经验总结(客户端开发岗)
- 2015年大二上-数据结构-查找-2-(1)-HashTable
- 数据结构笔记——
- 【数据结构】线性表(数组实现)
- 小蚂蚁学习数据结构(35)——直接插入排序
- 简单数据结构(三)栈
- 数据结构与算法学习笔记(四)
- 数据结构之“Ordered List and Sorted List”(六)
- 数据结构与算法笔记 —— 向量
- 数据结构与算法笔记 —— 排序算法及代码实现
- bzoj1012[最大数]
- 《数据结构》链栈
- 《数据结构》顺序栈
- 最小堆建立
- 数据结构复习提纲
- PHP实现栈数据结构
- 数据结构-----线性表学习笔记
- 【数据结构】堆