数据结构-排序

时间:2024.4.27

第8章 排序

8.1 排序的基本概念

8.2 插入排序

8.3 选择排序

8.4 交换排序

本章主要知识点:

● 排序的基本概念和衡量排序算法优劣的标准,其中衡量标准有算法的时间复杂度、空间复杂度和稳定性 直接插入排序,希尔排序 直接选择排序,堆排序 冒泡排序,快速排序

8.1排序的基本概念

1.排序是对数据元素序列建立某种有序排列的过程。

2.排序的目的:便于查找。

3.关键字是要排序的数据元素集合中的一个域,排序是以关键字为基准进行的。

关键字分主关键字和次关键字两种。对要排序的数据元素集合来说,如果关键字满足数据元素值不同时该关键字的值也一定不同,这样的关键字称为主关键字。不满足主关键字定义的关键字称为次关键字。

4.排序的种类:分为内部排序和外部排序两大类。

若待排序记录都在内存中,称为内部排序;若待排序记录一部分在内存,一部分在外存,则称为外部排序。

注:外部排序时,要将数据分批调入内存来排序,中间结果还要及时放入外

存,显然外部排序要复杂得多。

5.排序算法好坏的衡量标准:

(1)时间复杂度—— 它主要是分析记录关键字的比较次数和记录的移动次数。

(2)空间复杂度——算法中使用的内存辅助空间的多少。

(3)稳定性——若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。

8.2 插入排序

插入排序的基本思想是:每步将一个待排序的对象,按其关键字大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。

简言之,边插入边排序,保证子序列中随时都是排好序的。

常用的插入排序有:直接插入排序和希尔排序两种。

8.2.1 直接插入排序

1、其基本思想是:

顺序地把待排序的数据元素按其关键字值的大小插入到已排序数据元素子集合的适当位置。 例1:关键字序列T=(13,6,3,31,9,27,5,11),

请写出直接插入排序的中间过程序列。

初始关键字序列:【13】, 6, 3, 31, 9, 27, 5, 11

第一次排序: 【】, 3, 31, 9, 27, 5, 11

第二次排序: 【3, 6, 13】, 31, 9, 27, 5, 11

第三次排序: 【3, 6, 13,31】, 9, 27, 5, 11

第四次排序: 【3, 6, 9, 13,31】, 27, 5, 11

第五次排序: 【3, 6, 9, 13,27, 31】, 5, 11

第六次排序: 【3, 5, 6, 9, 13,27, 31】, 11

第七次排序: 【3, 5, 6, 9, 11,13,27, 31】

注:方括号 [ ]中为已排序记录的关键字,下划横线的 关键字

表示它对应的记录后移一个位置。

2.直接插入排序算法

public static void insertSort(int[] a){

}

初始关键字序列:【13】, 6, 3, 31, 9, 27, 5, 11

第一次排序: 【6, 13】, 3, 31, 9, 27, 5, 11

第二次排序: 【3, 6, 13】, 31, 9, 27, 5, 11

3、直接插入排序算法分析

(1)时间效率:当数据有序时,执行效率最好,此时的时间复杂度为O(n);当数据基本反序时,执行效率最

差,此时的时间复杂度为O(n)。所以当数据越接近有序,直接插入排序算法的性能越好。

(2)空间效率:仅占用1个缓冲单元——O(1)

(3)算法的稳定性:稳定

8.2.2 希尔(shell)排序 (又称缩小增量排序)

1、基本思想:把整个待排序的数据元素分成若干个小组,对同一小组内的数据元素用直接插入法排序;

小组的个数逐次缩小,当完成了所有数据元素都在一个组内的排序后排序过程结束。

2、技巧:小组的构成不是简单地“逐段分割”,而是将相隔某个增量d的记录组成一个小组,让增量d逐趟

缩短(例如依次取5,3,1),直到d=1为止。

3、优点:让关键字值小的元素能很快前移,且序列若基本有序时,再用直接插入排序处理,时间效率会

高很多。

例2:设待排序的序列中有12个记录,它们的关键字序列 T=(65,34,25,87,12,38,56,46,14,77,

92,23),请写出希尔排序的具体实现过程。

public static void shellSort(int[] a, int[] d, int numOfD){

int i, j, k, m, span; int temp; int n = a.Length; for(m = 0; m < numOfD; m ++){ span = d[m]; for(k = 0; k < span; k ++){ //共numOfD次循环 //取本次的增量值 //共span个小组 2 int i, j, temp; int n = a.Length; for(i = 0; i < n - 1; i ++){ temp = a[i + 1]; j = i; while(j > -1 && temp < a[j]){ a[j + 1] = a[j]; j --; } a[j + 1] = temp; } for(i = k; i < n-span; i = i + span){ temp = a[i+span];

} } } } j = i; while(j > -1 && temp < a[j]){ } a[j + span] = temp; a[j + span] = a[j]; j = j - span;

算法分析:开始时d 的值较大,子序列中的对象较少,排序速度较快;随着排序进展,d 值逐渐变小,子序列中对象个数逐渐变多,由于前面工作的基础,大多数记录已基本有序,所以排序速度仍然很快。 时间效率:O(n(log2n))

空间效率:O(1)——因为仅占用1个缓冲单元

算法的稳定性:不稳定

练习:

1. 欲将序列(Q, H, C, Y, P, A, M, S, R, D, F, X)中的关键码按字母升序重排,则初始d为4的希尔排序一趟的结果是?

答: 原始序列: Q, H, C, Y, P, A, M, S, R, D, F, X

shell一趟后: P,A,C,S,Q,D,F,X,R,H,M,Y

2. 以关键字序列(256,301,751,129,937,863,742,694,076,438)为例,写出执行希尔排序(取d=5,3,1)算法的各趟排序结束时,关键字序列的状态。

解:原始序列: 256,301,751,129,937,863,742,694,076,438

希尔排序第一趟d=5 256 301 694 076 438 863 742 751 129 937

第二趟d=3 076 301 129 256 438 694 742 751 863 937

第三趟d=1 076 129 256 301 438 694 742 751 863 937

8.3 选择排序

选择排序的基本思想是:每次从待排序的数据元素集合中选取关键字最小(或最大)的数据元素放到数据元素集合的最前(或最后),数据元素集合不断缩小,当数据元素集合为空时选择排序结束。 常用的选择排序算法:

(1)直接选择排序

(2)堆排序

8.3.1直接选择排序

1、其基本思想

每经过一趟比较就找出一个最小值,与待排序列最前面的位置互换即可。

(即从待排序的数据元素集合中选取关键字最小的数据元素并将它与原始数据元素集合中的第一个数据元素交换位置;然后从不包括第一个位置的数据元素集合中选取关键字最小的数据元素并将它与原始数据集合中的第二个数据元素交换位置;如此重复,直到数据元素集合中只剩一个数据元素为止。)

2、优缺点

优点:实现简单

缺点:每趟只能确定一个元素,表长为n时需要n-1趟

例3:关键字序列T= (21,25,49,25*,16,08),请给出直接选择排序的具体实现过程。

原始序列: 21,25,49,25*,16,08

第1趟 08,25,49,25*,16,21 2

第2趟 08,16, 49,25*,25,21

第3趟 08,16, 21,25*,25,49

第4趟 08,16, 21,25*,25,49

第5趟 08,16, 21,25*,25,49

public static void selectSort(int[] a){

}

3、算法分析

时间效率: O(n)——虽移动次数较少,但比较次数仍多。

空间效率:O(1)——没有附加单元(仅用到1个temp)

算法的稳定性:不稳定

4、稳定的直接选择排序算法

例:关键字序列T= (21,25,49,25*,16,08),请给出稳定的直接选择排序的具体实现过程。 原始序列: 21,25,49,25*,16,08

第1趟08, 21 , 25 , 49 , 25 *, 16

第2趟08,16, 21,25,49 ,25 *

第3趟08,16, 21,25,49 ,25 *

第4趟08,16, 21,25,49 ,25 *

第5趟08,16, 21,25,25 * ,49

public static void selectSort2(int [] a){

int i,j,small; int temp; int n = a.Length; for(i = 0; i < n-1; i++){ small = i; for(j = i+1; j < n; j++){ //寻找最小的数据元素 if(a[j] < a[small]) small = j; //记住最小元素的下标 } if(small != i){ temp = a[small]; a[j] = a[j-1]; for(j = small; j > i; j--) //把该区段尚未排序元素依次后移 2int i, j, small; int temp; int n = a.Length; for(i = 0; i < n - 1; i ++){ small = i; //设第i个数据元素最小 for(j = i + 1; j < n; j ++) //寻找最小的数据元素 if(a[j] < a[small]) small = j; //记住最小元素的下标 if(small != i){ //当最小元素的下标不为i时交换位置 temp = a[i]; a[i] = a[small]; a[small] = temp; } }

} } a[i] = temp; //插入找出的最小元素 }

8.3.2 堆排序

1. 什么是堆? 2. 怎样建堆? 3. 怎样堆排序?

堆的定义:设有n个数据元素的序列 k0,k1,…,kn-1,当且仅当满足下述关系之一时,称之为堆。 解释:如果让满足以上条件的元素序列 (k0,k1,…,kn-1)顺次排成一棵完全二叉树,则此树的特点是: 树中所有结点的值均大于(或小于)其左右孩子,此树的根结点(即堆顶)必最大(或最小)。 例4:有序列T1=(08, 25, 49, 46, 58, 67)和序列T2=(91, 85, 76, 66, 58, 67, 55),判断它们是否 “堆”?

2. 怎样建堆?

步骤:从第一个非终端结点开始往前逐步调整,让每个双亲大于(或小于)子女,直到根结点为止。

终端结点(即叶子)没有任何子女,无需单独调整

例:关键字序列T= (21,25,49,25*,16,08),请建最大堆。

解:为便于理解,先将原始序列画成完全二叉树的形式:

这样可以很清晰地从(n-1-1)/2开始调整。

public static void createHeap(int[] a, int n, int h){

}

利用上述createHeap(a,n,h)函数,初始化创建最大堆的过程就是从第一个非叶子结点a[i]开始,到根结点a[0]为止,循环调用createHeap(a,n,h)的过程。

初始化创建最大堆算法如下:

public static void initCreateHeap(int[] a){

} int n = a.Length; for(int i = (n-1-1) / 2; i >= 0; i --) createHeap(a, n, i); int i, j, flag; int temp; i = h; // j为i结点的左孩子结点的下标 j = 2 * i + 1; temp = a[i]; flag = 0; while(j < n && flag != 1){ //寻找左右孩子结点中的较大者,j为其下标 } a[i] = temp; if(j < n - 1 && a[j] < a[j + 1]) j++; if (temp >= a[j]) } else{ i = j; j = 2 * i + 1; //a[i]>=a[j] flag = 1; //标记结束筛选条件 //否则把a[j]上移 a[i] = a[j];

3. 怎样进行整个序列的堆排序?

*基于初始堆进行堆排序的算法步骤:

堆的第一个对象a[0]具有最大的关键码,将a[0]与a[n-1]对调,把具有最大关键码的对象交换到最后;

再对前面的n-1个对象,使用堆的调整算法,重新建立堆(调整根结点使之满足最大堆的定义)。结再对调a[0]和a[n-2],然后对前n-2个对象重新调整,…如此反复,最后得到全部排序好的对象序列。 果具有次最大关键码的对象又上浮到堆顶,即a[0]位置; 例5:对刚才建好的最大堆进行排序:

public static void heapSort(int[] a){

}

4、堆排序算法分析:

?

?

? 时间效率:O(nlog2n)。 空间效率:O(1)。 稳定性: 不稳定。 int temp; int n = a.Length; initCreateHeap(a); for(int i = n - 1; i > 0; i --){ } temp = a[0]; a[0] = a[i]; a[i] = temp; createHeap(a,i, 0); //调整根结点满足最大堆 //初始化创建最大堆 //当前最大堆个数每次递减1 //把堆顶a[0]元素和当前最大堆的最后一个元素交换

练习1:以下序列是堆的是( )

A. {75,65,30,15,25,45,20,10} B. {75,65,45,10,30,25,20,15}

C. {75,45,65,30,15,25,20,10} D. {75,45,65,10,25,30,20,15}

练习2:有一组数据{15,9,7,8,20,1,7*,4},建成的最小堆为( )。

A.{1,4,8,9,20,7,15,7*} B.{1,7,15,7*,4,8,20,9}

C.{1,4,7,8,20,15,7*,9} D.以上都不对

练习3:已知序列{503,87,512,61,908,170,897,275,653,462},写出采用堆排序对该序列作非递减排列时的排序过程。

排序好的序列为:61,87,170,275,462,503,512,653,897,908

8.4 交换排序

交换排序的基本思想是:利用交换数据元素的位置进行排序的方法。

交换排序的主要算法有:

1) 冒泡排序

2) 快速排序

8.4.1 冒泡排序

1、基本思路:每趟不断将记录两两比较,并按“前小后大”(或“前大后小”)规则交换。

2、优点:每趟结束时,不仅能挤出一个最大值到最后面位置,还能同时部分理顺其他元素;一旦下趟没有交换发生,还可以提前结束排序。

例:关键字序列 T=(21,25,49,25*,16,08),请按从小到大的顺序,写出冒泡排序的具体实现过程。 初态:21,25,49, 25*,16, 08

第1趟21,25,25*,16, 08 , 49

第2趟21,25, 16, 08 ,25*,49

第3趟21,16, 08 ,25, 25*,49

第4趟16,08 ,21, 25, 25*,49

第5趟08,16, 21, 25, 25*,49

3、冒泡排序算法

public static void bubbleSort(int[] a){

}

4、冒泡排序的算法分析

时间效率:O(n) —因为要考虑最坏情况(数据元素全部逆序),当然最好情况是数据元素已全部排好序,此时循环n-1次,时间复杂度为O(n)

空间效率:O(1) —只在交换时用到一个缓冲单元

稳 定 性: 稳定 —25和25在排序前后的次序未改变

练习:关键字序列 T=(31,15,9,25,16,28),请按从小到大的顺序,写出冒泡排序的具体实现过程。 初态:31,15,9, 25,16, 28

第1趟15,9,25,16, 28, 31

第2趟9,15, 16, 25,28,31

第3趟9,15, 16,25, 28,31

1、基本思想:设数组a中存放了n个数据元素,low为数组的低端下标,high为数组的高端下标,从数组a中任取一个元素(通常取a[low])做为标准元素,以该标准元素调整数组a中其他各个元素的位置,使排在标准元素前面的元素均小于标准元素,排在标准元素后面的均大于或等于标准元素。这样一次排序过程结束后,一方面将标准元素放在了未来排好序的数组中该标准元素应位于的位置上,另一方面将数组中的元素以标准元素为中心分成了两个子数组,位于标准元素左边子数组中的元素均小于标准元素,位于标准元素右边子数组中的元素均大于等于或标准元素。对这两个子数组中的元素分别再进行方法类同的递归快速排序。算法的递归出口条件是low≥high。

例、关键字序列 T=(60,55,48,37,10,90,84,36),请按从小到大的顺序,写出快速排序的具体实现过程。

快速排序算法各次快速排序过程

3、快速排序算法

public static void quickSort(int[] a, int low, int high){

int i, j; *2 int i, j, flag=1; int temp; int n = a.Length; for(i = 1; i < n && flag == 1; i++){ } flag = 0; for(j = 0; j < n-i; j++){ } if(a[j] > a[j+1]){ } flag = 1; temp = a[j]; a[j] = a[j+1]; a[j+1] = temp;

int temp; i = low; j = high; temp = a[low]; //取第一个元素为标准数据元素 while(i < j){ while(i < j && temp <= a[j]) j--; if(i < j){ } a[i] = a[j]; i++; //在数组的右端扫描

//在数组的左端扫描

while(i < j && a[i] < temp) i++;

if(i < j){ } a[j] = a[i]; j--;

}

a[i] = temp;

if(low < i) quickSort(a, low, i-1);

}

4、快速排序算法分析:

时间效率:一般情况下时间复杂度为O(nlog2n),最坏情况是数据元素已全部正序或反序有序,此时每次标准元素都把当前数组分成一个大小比当前数组小1的子数组,此时时间复杂度为O(n)

空间效率:O(log2n)—因为递归要用栈

稳 定 性: 不 稳 定 —因为有跳跃式交换。

练习:已知序列{503,87,512,61,908,170,897,275,653,462},给出采用快速排序对该序列作非递减排序时每趟的结果。

第一趟:【462 87 275 61 170】 503 【 897 908 653 512】

第二趟:【170 87 275 61 】 462 503 【512 653】 897 【908】

第三趟:【61 87】 170 【275】 462 503 512 【653】 897 908

第四趟:61 【 87】 170 275 462 503 512 653 897 908

最后排序结果:61 87 170 275 462 503 512 653 897 908

1.插入排序是稳定的,选择排序是不稳定的。

2.堆排序所需要附加空间数与待排序的记录个数无关。

3.对有n个记录的集合进行快速排序,所需时间确定于初始记录的排列情况,在初始记录无序的情况下最好。

4.直接插入排序在最好情况下的时间复杂度为( A )

A.O(n) B.O(nlog2n) C. O(log2n) D.O(n)

5.数据序列{8,9,10,4,5,6,20,1,2}只能是( C )算法的两趟排序后的结果。

A.直接选择排序 B.冒泡排序 C.直接插入排序 D.堆排序

6.用直接插入排序对下面4个序列进行递增排序,元素比较次数最少的是(C )

A.94,32,40,90,80,46,21,69 B.32,40,21,46,69,94,90,80 22//对左端子集合递归 if(i < high) quickSort(a, j+1, high); //对右端子集合递归

C.21,32,46,40,80,69,90,94 D.90,69,80,46,21,32,94,40

7. .以下排序算法中,(B )不能保证每趟排序至少能将一个元素放到其最终位置上。

A. 快速排序 B.希尔排序 C.堆排序 D.冒泡排序

8.对关键字{28,16,32,12,60,2,5,72}序列进行快速排序,第一趟从小到大一次划分结果为(B )

A.(2,5,12,16)26(60,32,72) B.(5,16,2,12)28(60,32,72)

C.(2,16,12,5)28(60,32,72) D.(5,16,2,12)28(32,60,72)

9.若用冒泡排序对关键字序列{18,16,14,12,10,8}进行从小到大的排序,所需进行的关键字比较总次数是( B )。

A.10 B.15 C.21 D.34

10. 一组记录的关键字为{45,80,55,40,42,85},则利用堆排序的方法建立的初始堆为( B )。

A.{85,80,45,40,42,55} B.{85,80,55,40,42,45} C.{85,80,55,45,42,40} D.{85,55,80,42,45,40}

更多相关推荐:
数据结构排序超级总结

一插入排序InsertionSort1基本思想每次将一个待排序的数据元素插入到前面已经排好序的数列中的适当位置使数列依然有序直到待排序数据元素全部插入完为止2排序过程示例初始关键字493865977613274...

数据结构各种排序算法总结

数据结构各种排序算法总结计算机排序与人进行排序的不同计算机程序不能象人一样通览所有的数据只能根据计算机的quot比较quot原理在同一时间内对两个队员进行比较这是算法的一种quot短视quot1冒泡排序Bubb...

数据结构算法排序总结

数据结构与算法总结姓名:**学号:**班级:12计本(2)班这个学期在老师的带领下我们学习了数据结构与算法这门课程。在本次数据结构与算法的学习中最令我深刻的是关于几种排序算法的学习,所以在这里我想对我本学期所学…

数据结构中排序总结

排序总结排序方法平均时间最坏时间辅助存储简单排序O(n2)O(n2)O(1)快速排序O(nlogn)O(n2)O(logn)堆排序O(nlogn)O(nlogn)O(1)归并排序O(nlogn)O(nlogn)…

数据结构之排序总结(C语言)

数据结构实验课程之手工执行一下排序算法写出每一趟排序结束时的关键码状态includeltstdiohgtincludeltstdlibhgtdefinemaxsize20结构体的定义typedefstructi...

数据结构排序算法总结I

数据结构排序算法总结I考研复习到数据结构排序这章了这章的内容比较经典都是一些很好的算法将来很可能会用得到总结一下加深一下印象文章篇幅有点大请点击查看更多下面是跳转链接12312三选择排序1简单选择排序2堆排序五...

数据结构中常见的排序算法总结

几种常见排序算法的比较与实现1冒泡排序BubbleSort冒泡排序方法是最简单的排序方法这种方法的基本思想是将待排序的元素看作是竖着排列的气泡较小的元素比较轻从而要往上浮在冒泡排序算法中我们要对这个气泡序列处理...

数据结构几种排序法

includeltstdiohgtincludeltstdlibhgttypedefintKeyTypeincludequot几种排序hquotvoidmainDataTypetest1043547119115...

数据结构排序

各种排序算法排序算法是一种基本并且常用的算法由于实际工作中处理的数量巨大所以排序算法对算法本身的速度要求很高而一般我们所谓的算法的性能主要是指算法的复杂度一般用O方法来表示在后面我将给出详细的说明对于排序的算法...

排序 数据结构

includequotstdiohquotincludequotstdlibhquotdefineMax100假设文件长度typedefstruct定义记录类型intkeyRecTypetypedefRecTy...

数据结构实验报告(C语言)顺序表 排序

计算机科学与技术系实验报告专业名称计算机科学与技术课程名称数据结构与算法项目名称排序班级计科一班学号姓名实验日期格式要求实验报告注意格式规范要求在word中编写文中不要有空行统一使用A4页面页边距上25cm下2...

太原理工大学数据结构实验报告

数据结构实验报告课程名称数据结构实验项目线性表树图查找内排序实验地点专业班级物联网学号学生姓名指导教师周杰伦20xx年月日实验一线性表目的与要求本次实习的主要目的是为了使学生熟练掌握线性表的基本操作在顺序存储结...

数据结构排序总结(27篇)