数据结构-排序
来源:互联网 发布:360电脑管家mac版 编辑:程序博客网 时间:2024/06/06 01:40
1.基本概念
排序:所谓排序,就是将原本无序的序列重新排列成有序的序列。
稳定性:稳定性指的是需要排序的序列中,有2个或者多个相同的元素项,在排序前和排序后,他们之间的相对位置并没有发生变化。如果没有发生变化就是稳定的;如果发生变化就是不稳定的。
排序算法的分类:
插入类排序
对一个已经有序的序列中,插入一个新的记录。就好比军队排队,已经排好了一个纵队,现在来了一个新的人加入到对于,这是军官喊:“新来的,迅速找到你的位置,入队。”于是新来的“插入”到了这个队伍中合适的位置。属于这种插入排序的有“直接插入排序”、“折半插入排序”、“希尔排序”。交换类排序
交换类排序的核心是“交换”,即每一趟排序,都通过一系列的“交换” 动作,让一个记录排到它最终的位置。属于这种交换类排序有冒泡排序,快速排序。选择类排序
选择类排序的核心是“选择”,即通过每一趟排序都选出一个最小(最大)的元素项,然后将它与序列中的第一个(或者最后一个)进行交换,这样最小(最大)的元素项就已经就位了。属于选择类排序的有简单选择排序、堆排序。归并类排序
归并类排序是将2个或者2个以上的有序序列合并成一个新的有序序列。属于归并类排序的有二路归并排序。基数类排序
基数类的排序是最特别的一类,跟之前的排序思想完全不同(之前的排序是进行“比较”、“移动”这2个操作)。基数类排序是基于多关键字排序的思想,把一个逻辑关键字拆分成多个关键字。例如对于一副已经去除了大小王的52张扑克牌中,按照基数排序类的思想,可以先按照花色排(黑桃、红桃、方块、梅花),这样就分成了4堆,然后每一堆在按照A到K的顺序排,这样这副牌就最终有序了。一个逻辑关键字(如黑桃8)被拆分为多个关键字(黑桃和8)就是这个意思了。
2.插入类排序
2.1 直接插入排序:
① 思想:最基本的插入排序,将第i个插入到前i-1个中的适当位置。
② 时间复杂度:T(n) = O(n²)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:稳定排序。
⑤ 程序:
public static void InsertSort(int[] array,int length){ int temp; //保存插入值 int j; //记录与插入值比较的索引 for(int i = 1;i < length;i++){ temp = array[i]; j = i-1; while(j >= 0 && array[j] > temp ){ array[j+1] = array[j]; j = j - 1; } array[j+1] = temp; } }
2.2折半插入排序:
① 思想:因为是已经确定了前部分是有序序列,所以在查找插入位置的时候可以用折半查找的方法进行查找,提高效率。
② 时间复杂度:比较时的时间减为O(n㏒n),但是移动元素的时间耗费未变,所以总是得时间复杂度还是O(n²)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:稳定排序。
⑤ 程序:
public static void BinSort(int[] array,int length){ int num = length -1;//最后一个元素为待插入的元素 int x = array[num]; int low = 0; int high = num - 1; int mid; while(low <= high){//查找出需要插入的位置(即low位置) mid = (low + high) / 2; if(x < array[mid]){ high = mid - 1; }else{ low = mid + 1; } } //将插入点(含插入点)之后的数据全部后移一位 for(int i = num - 1; i >= low; --i){ array[i+1] = array[i]; } //将数据插入 array[low] = x; }
2.3 希尔排序:
① 思想:又称缩小增量排序法。把待排序序列分成若干较小的子序列,然后逐个使用直接插入排序法排序,最后再对一个较为有序的序列进行一次排序,主要是为了减少移动的次数,提高效率。原理应该就是从无序到渐渐有序,要比直接从无序到有序移动的次数会少一些。
② 时间复杂度:O(n的1.5次方)
③ 空间复杂度:O(1)
④ 稳定性:不稳定排序。{2,4,1,2},2和1一组4和2一组,进行希尔排序,第一个2和最后一个2会发生位置上的变化。
⑤ 程序:
public static void ShellSort(int[] array, int length){ int temp; //保存临时数据(数据交换时使用) int step; //设置增量 for(step = length/2; step > 0; step /= 2){ //增量循环递减直至到1 for(int i = 0; i < step; i++){ //遍历全部子序列 for(int j = i + step; j < length; j += step){ //遍历每个子序列中的元素 if(array[j] < array[j-step]){//直接插入排序 temp = array[j]; int n = j - step; while(n >= 0 && array[n] > temp){ array[n + step] = array[n]; n -= step; } array[n + step] = temp; } } } } }
3.交换类排序
3.1 冒泡排序:
① 思想:反复扫描待排序序列,在扫描的过程中顺次比较相邻的两个元素的大小,若逆序就交换位置。第一趟,从第一个数据开始,比较相邻的两个数据,(以升序为例)如果大就交换,得到一个最大数据在末尾;然后进行第二趟,只扫描前n-1个元素,得到次大的放在倒数第二位。以此类推,最后得到升序序列。如果在扫描过程中,发现没有交换,说明已经排好序列,直接终止扫描。所以最多进行n-1趟扫描。
② 时间复杂度:T(n) = O(n²)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:稳定排序。
⑤ 程序:
public static void BubbleSort(int[] array, int length){ int temp; //保存临时数据(数据交换时使用) Boolean flag; //设置标记,记录此趟排序是否发生交换 for(int i = 0; i < length-1; i++){ //设置需要扫描的趟数 flag = false; for(int j = 0; j < length - i - 1; j++){ //遍历,比相邻两个数据的大小 if(array[j] < array[j+1]){ temp = array[j+1]; array[j+1] = array[j]; array[j] = temp; flag = true; } } } }
3.2 快速排序:
① 思想:冒泡排序一次只能消除一个逆序,为了能一次消除多个逆序,采用快速排序。以一个关键字为轴,从左从右依次与其进行对比,然后交换,第一趟结束后,可以把序列分为两个子序列,轴的一边全是比它小的(或者小于等于),轴的另一边全是比它大的(或者大于等于),然后再分段进行快速排序,达到高效。
② 时间复杂度:平均T(n) = O(n㏒n),最坏O(n²)。
③ 空间复杂度:S(n) = O(㏒n)。
④ 稳定性:不稳定排序。{3, 2, 2}
⑤ 程序:
public static void QuickSort(int[] array, int low, int high){//对从array[low]到array[high]之间的元素进行排序 int temp = array[low]; //保存进行比较的关键值 int i = low,j = high; //i指向最开始索引,j指向最末尾索引 if(i<j){ //下面的这个循环完成一趟排序,即将数组中小于temp的元素放置在左边,大于temp的元素放置在右边 while(i != j){ while(i < j && array[j] > temp) --j; //从最后索引递减,直到遇到小于temp的元素 if(i < j){//将小于temp的元素,放置到数组最前面 array[i] = array[j]; ++i; } while(i<j && array[i] < temp) ++i; //从前面的索引递增,直到遇到大于temp的元素 if(i < j){//将大于temp的元素,放置在数组后面 array[j] = array[i]; --j; } } //数组中相比较于temp已经划分分到2边,将temp放置到最终中间位置 array[i] = temp; QuickSort(array, low, i-1);//递归对temp左边的元素进行排序 QuickSort(array, i+1, high);//递归对temp右边的元素进行排序 } }
4.选择类排序
4.1 简单选择排序:
① 思想:第一趟时,从第一个记录开始,通过n – 1次关键字的比较,从n个记录中选出关键字最小的记录,并和第一个记录进行交换。第二趟从第二个记录开始,选择最小的和第二个记录交换。以此类推,直至全部排序完毕。
② 时间复杂度:T(n) = O(n²)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:不稳定排序,{3, 3, 2}。
⑤ 程序:
public static void SelectSort(int[] array, int length){ int k; //保存目标元素的索引 int temp; //临时变量(数据交换时候使用) for(int i = 0; i < length; i++){ //对数组中的每一个数进行遍历 k = i; for(int j = i+1; j < length; j++){ if(array[k] > array[j]){//选择出目前最小元素的索引 k = j; } } if(k != i){ temp = array[i]; array[i] = array[k]; array[k] = temp; } } }
4.2 堆排序
① 思想:把待排序记录的关键字存放在数组r[1…n]中,将r看成是一刻完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]作为二叉树的根,一下个记录r[2…n]依次逐层从左到右顺序排列,任意节点r[i]的左孩子是r[2i],右孩子是r[2i+1],双亲是r[i/2向下取整]。然后对这棵完全二叉树进行调整建堆。
② 时间复杂度:T(n) = O(n㏒n)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:不稳定排序。{5, 5, 3}
⑤ 程序:
/** * 堆排序 *//** * 1.完成数组在array[low]到array[high]范围内对位置在low上的结点进行调整 * @param array * @param low * @param high */public static void Sift(int[] array, int low, int high){ int i = low,j = low*2; //array[j]为array[i]的左子节点 int temp = array[i]; //获取low上的值 while(j <= high){ if(j < high && array[j] < array[j+1]){//如果右子节点比左子节点大,那么j指向右子节点 ++j; } if(array[j] > temp){ array[i] = array[j]; //将array[j]移动到其双亲结点上 i = j; //修改i与j的值,以便继续向下调整 j = i * 2; }else break; } array[i] = temp; //被调整结点放入最终位置} /** * 2.堆排序函数 * @param array * @param length */public static void HeapSort(int[] array, int length){ int temp; for(int i = length / 2; i >=0 ;--i) //从length/2先上遍历,建立初始堆。(根据完全二叉树原理,length/2均为叶子结点) Sift(array, i, length-1); for(int i = length-1; i>=1;--i){ //进行遍历,每一次将根节点中的元素取出来,然后放入最终位置。 temp = array[i]; array[i] = array[0]; array[0] = temp; Sift(array,0,i-1); //在减少1个元素的序列中进行调整 }}
5. 归并类排序
5.1 二路归并排序
① 思想:假设初始序列右n个记录,首先将这n个记录看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2向上取整 个长度为2(n为奇数时,最后一个序列的长度为1)的有序子序列。在此基础上,在对长度为2的有序子序列进行两两归并,得到若干个长度为4的有序子序列。如此重复,直至得到一个长度为n的有序序列为止。
② 时间复杂度:T(n) = O(n㏒n)。
③ 空间复杂度:S(n) = O(n)。
④ 稳定性:稳定排序。
⑤ 程序:
/* * 二路归并排序 *//** * 1.归并数组操作 * 已知array1[low...mid]和array1[mid + 1...high]分别按关键字有序排列, * 将它们合并成一个有序序列,存放在array2[low...high] * @param array1 * @param low * @param mid * @param high * @param array2 */public static void Merge(int[] array1, int low, int mid, int high , int[] array2){ /**/ int i = low; int j = mid + 1; int k = low; while((i <= mid) && (j <= high)){ //2个数组进行合并操作 if(array1[i] <= array1[j]) { array2[k] = array1[i]; ++i; } else { array2[k] = array1[j]; ++j; } ++k; } while(i <= mid){ //如果还有其中一个数组剩余元素,直接加在末尾 array2[k] = array1[i]; k++; i++; } while(j <= high){ //如果还有其中一个数组剩余元素,直接加在末尾 array2[k] = array1[j]; k++; j++; } }/** * 2. r1[low...high]经过排序后放在r3[low...high]中,r2[low...high]为辅助空间 * @param r1 * @param low * @param high * @param r3 */public static void MSort(int r1[], int low, int high, int r3[]){ int[] r2 = new int[high+1]; int mid; if(low == high) r3[low] = r1[low]; else{ mid = (low + high) / 2; MSort(r1, low, mid, r2); MSort(r1, mid + 1, high, r2); Merge(r2, low, mid, high, r3); } } /** * 3.合并排序 * 对记录数组r[0...n]做归并排序 * @param array * @param length */public static void MergeSort(int array[], int length){ MSort(array, 0, length-1, array); }
6. 基数类排序
6.1 链式基数排序
① 思想:先分配,再收集,就是先按照一个次关键字收集一下,然后进行收集(第一个排序),然后再换一个关键字把新序列分配一下,然后再收集起来,又完成一次排序,这样所有关键字分配收集完后,就完成了排序。
② 时间复杂度:T(n) = O( d ( n + rd ) )。
③ 空间复杂度:S(n) = O(rd)。
④ 稳定性:稳定排序。
7. 总结
(1)简单排序法一般只用于n较小的情况(例如n<30)。当序列的记录“基本有序”时,直接插入排序是最佳的排序方法。如果记录中的数据较多,则应采用移动次数较少的简单选择排序法。
(2)快速排序、堆排序和归并排序的平均时间复杂度均为O(n㏒n),但实验结果表明,就平均时间性能而言,快速排序是所有排序方法中最好的。遗憾的是,快速排序在最坏情况下的时间性能为O(n²)。堆排序和归并排序的最坏时间复杂度仍为O(n㏒n),当n较大时,归并排序的时间性能优于堆排序,但它所需的辅助空间最多。
(3)可以将简单排序法与性能较好的排序方法结合使用。例如,在快速排序中,当划分子区间的长度小于某值时,可以转而调用直接插入排序法;或者先将待排序序列划分成若干子序列,分别进行直接插入排序,然后再利用归并排序法,将有序子序列合并成一个完整的有序序列。
(4)基数排序的时间复杂度可以写成O(d·n)。因此,它最适合于n值很大而关键字的位数d较小的序列。当d远小于n时,其时间复杂度接近O(n)。
(5)从排序的稳定性上来看,在所有简单排序法中,简单选择排序是不稳定的,其他各种简单排序法都是稳定的。然而,在那些时间性能较好的排序方法中,希尔排序、快速排序、堆排序都是不稳定的,只有归并排序、基数排序是稳定的。
参考:
书籍:《2017版数据结构高分笔记》 率辉 机械工业出版社
博客:http://blog.csdn.net/wzyhb123456789/article/details/5974790#
- 数据结构-排序
- 数据结构----排序
- 数据结构 - 排序
- 数据结构--排序
- 数据结构排序
- 数据结构 排序
- 数据结构排序
- 数据结构-----排序
- 数据结构排序
- 数据结构--排序
- 数据结构 排序
- 排序 - 数据结构
- 数据结构-排序
- 数据结构 - 排序
- 【数据结构】排序
- 数据结构 - 排序
- 数据结构-排序
- 数据结构排序
- doom3 Articulated Figures编辑器
- 338. Counting Bits
- 省赛总结
- ARM架构汇编语言常用指令浅析
- 算法提高 盾神与积木游戏
- 数据结构-排序
- 变量与常量
- 文件描述符fd和FILE的关系
- eclipse导入项目,提示path for project must have only one segment错误信息的解决办法
- APK多渠道打包方案汇总
- 蓝丝雨第十四季商业实战系列《全民…
- 独立团-第九版封包脱机辅助课程教…
- 易编远航第一期-七套多线程高级视…
- 易编远航第一期十一套-回合制-大话…