排序算法总结(一)
查找和排序是计算机应用中较为基础同时也很重要的两个组成成分。特别是当今网络如此盛行,Internet上充斥着各种各样的数据,图片,文本,音视频等,用户如何搜寻自己想要的数据,服务者如何及时有效的将用户的需求返回,都需要这些基础算法的帮助。在本人复习数据结构的时候,特意结合网上各位大牛的神贴,以及自己的一些理解,整理了一下一些常用的查找和排序算法,如有雷同之处,请各位大牛多多包涵。
基于比较的算法:
1. 选择排序
在众多的排序算法中,选择排序应该是最容易理解的一个算法了。其平均时间复杂度为O(n^2),空间复杂度则是O(1), 并且选择排序是不稳定的排序(值相同的两个元素在排序之后相互位置可能发生变化)。
选择排序是一个递归的查找比较算法:
(1)遍历一次尚未排序的数组元素,找到其中最大的元素,
(2)将其与尚未排序的数组最后一个元素互换
(3)将找到的最大元素从未排序的数组元素中取出
(4)其余未排序数组元素,重复操作(1),直到未排序数组中没有剩余元素。
1 void insertion_sort(int a[],int length) 2 { 3 for(int i=length -1; i>0;i--) 4 { 5 int maxData = a[i]; 6 int index =0; // 寻找最大元素 7 for(int j=0;j<i;j++) 8 { 9 if(maxData < a[j]) 10 { 11 maxData = a[j]; 12 index =j; 13 } 14 } // 是否需要交换位置 15 if(maxData != a[i]) 16 { 17 swap(a[i],a[index]); 18 } 19 } 20 }
2. 冒泡排序
冒泡排序也是一个比较基础的排序算法,基于比较。其平均时间复杂度为O(n^2), 空间复杂度O(1), 稳定的排序。
1 void bubble_sort(int a[],int length) 2 { 3 for(int i=0;i<length -1; i++) // 外层循环为需要递归的次数 4 { 5 for(int j = 0; j<length -i -1; j++) // 内层循环用于调整相邻节点的位置 6 { 7 if(a[j]>a[j+1]) 8 swap(a[j],a[j+1]); 9 } 10 } 11 }
需要指出的是,在冒泡排序中,往往不需要进行n-1次调整,数组就可能已经是有序的了,这时候继续比较就显得比较浪费,而且效率也低。通常的做法是在内循环中加入一个哨兵标志,如果当前循环中没有相互交换的元素,则认为数组已经有序,可以结束排序,其运作如下:
其实现如下:
1 void supper_bubble_sort(int a[],int length) 2 { 3 for(int i=0;i<length -1; i++) // 外层循环为需要递归的次数 4 { 5 bool sorted =false; 6 for(int j = 0; j<length -i -1; j++) // 内层循环用于调整相邻节点的位置 7 { 8 if(a[j]>a[j+1]) 9 { 10 swap(a[j],a[j+1]); 11 sorted = true; // 本次循环过程中,出现了交换 12 } 13 } 14 if(sorted == false) // 本次过程中没有出现数据交换,算法提前结束 15 break; 16 } 17 }
3. 插入排序
相对比较基础的一个排序算法。 莫非大牛们都比较喜欢“斗地主”,为嘛都拿打扑克来做例子!!(不过确实很形象~)
这个算法呢,理解起来比较容易,不过写程序的时候,因为移位操作比较多,在边界条件的选择上往往容易出问题。
要注意从数组起始位置开始查找,还是数组最后记录的位置。
算法的时间复杂度也是0(n^2),空间复杂度O(1),稳定的排序。
1 /************************************** 2 * Insertion_sort 3 * 每次从已插入的数组最后一个元素开始比较, 4 * 如果新的元素比最后一个元素大,则直接放入 5 * 已排序队列的最后位。否则,将新元素与当前 6 * 元素互换位置递归查找,直到找到第一个比新 7 * 元素小的元素,并将新的元素插入到该位置 8 **************************************/ 9 void Insertion_sort(int a[],int length) 10 { 11 for(int i =1;i<length; i++) 12 { 13 int key = a[i]; 14 int j=i -1; 15 while(j>=0 && a[j]>a[j+1]) 16 { 17 swap(a[j],a[j+1]); // 循环移位操作 18 j--; 19 } 20 if(a[j]>key) //插入新元素 21 a[j] = key; 22 } 23 }
4. 希尔排序
希尔排序又称“缩小增量排序”,从本质上来说,依旧是插入排序,因此希尔排序也可以叫做分组插入排序。不过希尔排序的效率比简单的插入排序则要高很多。
tips:希尔排序的一大特点是,通过增量,将数组分割成对应大小的子序列,每次并不是对所有的元素进行插入排序,而只是对各个子序列进行排序。
由于增量是逐个减小的,那么对应的各个子序列的大小则是从小变大的。而当某一趟排序后,进行下一次排序的时候,每个子序列中已经排好序的元素都会增加,从而可以减少由插入较小元素而导致的数据移动,进而提高排序的效率。
tips: 对于希尔排序来说,增量的选择对于排序的效率影响很大,如果选择合适的增量作为每次的分组依据显得尤为重要。这里没有一个统一的标准,但是需要保证最后一次的增量为1.