数据结构 第十章 内部排序
{(99006,16),(99003,17),(99001,18), (99004,18),(99002,19),(99005,20)}; 也可能为: {(99006,16),(99003,17),(99004,18), (99001,18),(99002,19),(99005,20)};
这两个结果都是表9-1按年龄的递增排序结果。
10.2.3希尔排序
3.希尔排序的效率分析
希尔排序的时间复杂性在O(nlog2n)和O(n2 )之间,
大致为O(n1.3 )。
希尔排序在比较时进行元素移动,有可能改变相同排序码
元素的原始顺序,因此希尔排序是不稳定的。
10.3 交换排序
10.3.1 冒泡排序 1.冒泡排序的基本思想 通过对待排序序列从前向后,依次比较相邻元素的排序码, 若发现逆序则交换,使排序码较小的元素逐渐从后部移向前 部,就象水底下的气泡一样逐渐向上冒
38 49 49 38 65
38
38
49 13 65 65 27 13 30 65 27 65 30 76
38
38 13 27 30 49
13 27 30 38
13 27 30
13 27
49
65
49
13 27 30 65
76 97
13 97 76 27 97 13 30 27 97 97 30 初 始 时 n=8
直接插入算法的元素移动是顺序的,该方法是稳定的。
2、折半插入排序
排序过程:用折半查找方法确定插入位置的排序
例:有6个记录,前5 个已排序的基础上,对第6个记录排序。
1 2 3 4 5 6
[ 15 27 low
36 53 69 ] 42 mid high high low low high mid
L.r[0]=L.r[i];
L.r[i]=L.r[i-1]; for(j=i-2;LT(L.r[0].key,L.r[j].key); --j) L.r[j+1]=L.r[j]; L.r[j+1]=L.r[0];
}}
10.2 插入排序
3.直接插入排序的效率分析
从空间来看,它只需要一个元素的辅助空间,用于元素的位置交换。 直接插入排序的时间复杂度为O(n2)。
10.3.2 快速排序 1.快速排序的基本思想 任取待排序序列中的某个元素作为基准(一般取第一个 元素),通过一趟排序,将待排元素分为左右两个子序列, 左子序列元素的排序码均小于或等于基准元素的排序码, 右子序列的排序码则大于基准元素的排序码,然后分别对 两个子序列继续进行排序,直至整个序列有序。
10.1.1 排序介绍
10.1 基本概念 表1 学生档案表
学号 99001 99002
姓名 王晓佳 林一鹏
年龄 18 19
性别 男 男
99003
99004 99005
谢宁
张丽娟 周涛
17
18 20
女
女 男
99006
李小燕
16
女
10.1.1 排序介绍
例如,在表中,若以每个记录的学号为关键字,按排序码年龄的递 增(由小到大)排序0.3.1 冒泡排序 2.冒泡排序的效率分析
若待排序的元素为正序,则只需进行一趟排序,比较次数为(n-1)
次,移动元素次数为0
若待排序的元素为逆序,则需进行n-1趟排序,比较次数为(n2-n)/2,
因此冒泡排序算法的时间复杂度为O(n2)
元素移动较多,属于内排序中速度较慢的一种 冒泡排序算法只进行元素间的顺序移动,是稳定的
第一趟结果 step=2 29
57 35 67
62 67 57 46 91 67 72 91 46 72 62
57 46 35 57 62 62 46 第二趟结果 Step=1 29 35 57
67 72 91 91 72
第三趟结果
29
35
46
57
62
67
72
91
希尔排序的完整算法如下
: void shellsort(sqlist &L, int dlta[],int t) void { ShellInsert(sqlist &L, int dk) { for(k=0; k< t;++k) for(i=dk+1; i<=L.length;++i) ShellInsert(L, dlta[k],); if( } LT(L.r[i].key,L.r[i-dk].key)) { L.r[0]=L.r[i]; for(j=i-dk; j>0&<(L.r[0].key,L.r[j].key) ;j-=dk ) L.r[j+dk]=L.r[j]; L.r[j+dk]=L.r[0]; } }
因为直接插入排序在元素基本有序的情况下(接近最好情况),效率
是很高,因此希尔排序在时间效率上比前两种方法有较大提高。
希尔排序算法的执行过程
例如,n=8,数组array[ ]的八个元素分别为: 91 ,67,35,62 , 29,72,46,57。
91 setp =4 29 初始状态, 67 35 62 29 57 91 72 46 57 62
10.1基本概念
1.排序码(Sort Key)
作为排序依据的记录中的一个属性。它可以是任何一种可比的有序数据 类型,它可以是记录的主关键字,也可以是任何次关键字。
2.有序表与无序表
一组记录按排序码的递增或递减次序排列得到的结果被称之为有序表, 相应地,把排序前的状态称为无序表。
3.正序表与逆序表
若有序表是按排序码升序排列的,则称为升序表或正序表,否则称为 降序表或逆序表。不失普遍性,我们一般只讨论正序表。
}
快速排序的过程为:
例如,给定排序码为:(46,55,13,42,94,05,17,70) Pivotkey: 46
[ 17 46 05 55 13 42 94 05 17 46 94 55 70 ]
high high high high low low low low low
2.快速排序的算法实现
13 76
76 27 13 30 76 27 76 30 97 第 一 趟
if(a[i]>a[i+1]) { t=a[i]; a[i]=a[i+1]; a[i+1]=t; }
第 二 趟
第 三 趟
第 四 趟
第 五 趟
第 六 趟
第 七 趟
#include <stdio.h> main() { int a[11],i,j,t; printf("Input 10 numbers:\n"); for(i=1;i<=10;i++) scanf("%d",&a[i]); printf("\n"); for(j=1;j<=9;j++) for(i=1;i<=10-j;i++) if(a[i]>a[i+1]) {t=a[i]; a[i]=a[i+1]; a[i+1]=t;} printf("The sorted numbers:\n"); for(i=1;i<=10;i++) printf("%d ",a[i]); }
[ 15
27
36
42
53
69 ]
(high<low ,查找结束,插入位置为low 或high+1 )
void BInsertSort (SqList &L)
{ for(i=2; i<= L.length; ++i)
{ L.r[0]=L.r[i];
low=1; high=i-1;
While(low<=high) {
快速排序的过程为:
例如,给定排序码为:(46,55,13,42,94,05,17,70) Pivotkey: 46
05 13 42 46 94 46 [ 17 46 46 55 94 46 05 55 17 70 ]
low high high high low low low low high
2.快速排序的算法实现
6 9 9 9 9 9 25)
第一次插入 25 第二次插入 14 第三次插入 20 第四次插入 9 第五次插入
2.直接插入的算法实现
void InsertSort (SqList &L)
{ for ( i=2; i<=L.length; i++) if(LT(L.r[i].key,L.r[i-1].key)) {
直接插入排序示例
例如,n=6,数组R的六个排序码分别为:17,3,25,14,20,9。它的 直接插入排序的执行过程
初始状态
0 3
1 (17 ) (3 (3 (3 (3 (3
2 3 17 ) 17 14 14 9
3 25 25 25 ) 17 17 14
4 14 14 14 25 ) 20 17
5 20 20 20 20 25 ) 20
10.1基本概念
4.稳定与不稳定
对于具有同一排序码的多个记录来说,若采用的排序方法使排序后记 录的相对次序不变,则称此排序方法是稳定的,否则称为不稳定的。
10.2 插入排序
10.2.1直接插入排序 1.直接插入排序的基本思想 直接插入排序(Straight Insertion Sorting)的基本思想是: 把 n 个待排序的元素看成为一个有序表和一个无序表,开始 时有序表中只包含一个元素,无序表中包含有 n-1 个元素, 排序过程中每次从无序表中取出第一个元素,把它的排序码 依次与有序表元素的排序码进行比较,将它插入到有序表中 的适当位置,使之成为新的有序表。
数据结构-内排序
Shell排序的性能分析
Shell排序的时间复杂度在O(nlog2n)和O(n2)间, Knuth的 统计结论是,平均比较次数和记录平均移动次数在n1.25与 1.6n1.25之间
Shell排序是一种不稳定的排序方法
最后谈一下delta的取法。 Shell最初的方案是delta=n/2, delta=delta/2,直到delta=1。Knuth的方案是delta=delta/3 +1。其它方案有:都取奇数为好;或delta互质为好等等。 而使用象1, 2, 4, 8, …或1, 3, 6, 9, …这样的增量序列就不太 合适,因为这样会使几个元素多次被分到一组中,从而造 成重复排序,产生大量无用的比较操作
另外,在无序子表中向前移动的过程中,如果没 有交换元素,则说明无序子表已有序,无须再做 排序
24
冒泡排序算法实现
1 void bubble_sort(RecType R[ ], int n) { 2 //待排序元素用一个数组R表示,数组有n个记录
3 int i, j; 4 bool swap=TRUE; //判断无序子表是否已有序的变量
内排序和外排序 按照排序过程中使用内、外存的不 同将排序方法分为内排序和外排序。若待排序记录全 部在内存中,称为内排序;若待排序记录的数量很大, 以致内存一次不能容纳全部记录,在排序过程中需要 进行内、外存交换,称为外排序。本章仅讨论内排序
内排序可分为五大类:插入排序、交换排序、选择排 序、归并排序和基数排序
直接插入排序(straight insert sort) 折半插入排序(binary insert sort) Shell排序(Shell sort)
10
10.2.1 直接插入排序举例
数据结构-第十章-内部排序
0
1
2
3
4
5
6
7
8
i=5
MAXINT 49 2 3
MAXINT 49 6 3 MAXINT 49 6 3 MAXINT 49 6 8
38 1
38 1 38 1 38 1
65 97 5 0
65 5 65 5 65 5 97 0 97 0 97 0
76 4
76 4 76 4 76 4
13
27
49
i=6
最坏情况下,待排记录按关键字非递增有序 排列(逆序)时,第 i 趟时第 i+1 个对象 必须与前面 i 个对象都做排序码比较, 并且 每做1次比较就要做1次数据移动。总比较 次 数 为 (n+2)(n-1)/2 次 , 总 移 动 次 数 为 (n+4)(n-1)/2。 在平均情况下的排序码比较次数和对象移 动次数约为 n2/4。因此,直接插入排序的 时间复杂度为 O(n2)。 直接插入排序是一种稳定的排序方法。
折半插入排序 (Binary Insertsort)
基本思想 既然每个要插入记录之前的纪录 已经按关键字有序排列,在查找插入位 臵时就没有必要逐个关键字比较,可以 使用折半查找来实现。由此进行的插入 排序称之为折半插入排序。
折半插入排序的算法
void BInsertSort (SqList &L){ for (i=2;i<=L.length;++i){ L.r[0]=L.r[i]; low=1;high=i-1; //查找范围由1到i-1 while(low<=high){ m=(low+high)/2; if LT(L.r[0].key,L.r[m].key) high=m-1; else low=m+1; }//while 折半查找 for (j=i-1;j>=high+1;--j) L.r[j+1]=L.r[j]; //折半查找结束后high+1位臵即为插入位臵 L.r[high+1]=L.r[0]; }//for }//BInsertSort
数据结构答案 第10章 排序学习与指导
第10章排序10.1 知识点分析1.排序基本概念:(1)排序将数据元素的任意序列,重新排列成一个按关键字有序(递增或递减)的序列的过程称为排序。
(2)排序方法的稳定和不稳定若对任意的数据元素序列,使用某个排序方法,对它按关键字进行排序,若对原先具有相同键值元素间的位置关系,排序前与排序后保持一致,称此排序方法是稳定的;反之,则称为不稳定的。
(3)内排序整个排序过程都在内存进行的排序称为内排序,本书仅讨论内排序。
(4)外排序待排序的数据元素量大,以致内存一次不能容纳全部记录,在排序过程中需要对外存进行访问的排序称为外排序。
2.直接插入排序直接插入排序法是将一个记录插到已排序好的有序表中,从而得到一个新的,记录数增1的有序表。
3.二分插入排序二分插入排序法是用二分查找法在有序表中找到正确的插入位置,然后移动记录,空出插入位置,再进行插入的排序方法。
4.希尔排序希尔排序的基本思想是:先选取一个小于n的整数d1作为第一个增量,把待排序的数据分成d1个组,所有距离为d1的倍数的记录放在同一个组内,在各组内进行直接插入排序,每一趟排序会使数据更接近于有序。
然后,取第二个增量d2,d2< d1,重复进行上述分组和排序,直至所取的增量d i=1(其中d i< d i-1 < ……< d2< d1),即所有记录在同一组进行直接插入排序后为止。
5.冒泡排序冒泡法是指每相邻两个记录关键字比大小,大的记录往下沉(也可以小的往上浮)。
每一遍把最后一个下沉的位置记下,下一遍只需检查比较到此为止;到所有记录都不发生下沉时,整个过程结束。
6.快速排序快速排序法是通过一趟排序,将待排序的记录组分割成独立的两部分,其中前一部分记录的关键字均比枢轴记录的关键字小;后一部分记录的关键字均比枢轴记录的关键字大,枢轴记录得到了它在整个序列中的最终位置并被存放好。
第二趟再分别对分割成两部分子序列,再进行快速排序,这两部分子序列中的枢轴记录也得到了最终在序列中的位置而被存放好,并且它们又分别分割出独立的两个子序列……。
第十章_排序方法(数据结构ppt-严蔚敏)
第二个问题解决方法——筛选
方法:输出堆顶元素之后,以堆中最后一个元素替代之;然 后将根结点值与左、右子树的根结点值进行比较,并与其中 小者进行交换;重复上述操作,直至叶子结点,将得到新的 堆,称这个从堆顶至叶子的调整过程为“筛选”
例 38 50 97 76
13 27 65 49 13 38
97 27 38 50 76
2 (n 4)(n 1) 记录移动次数: (i 1) 2 i 2
i 2 n
若待排序记录是随机的,取平均值 n2 关键字比较次数: T(n)=O(n² ) 4 记录移动次数:
空间复杂度:S(n)=O(1)
n2 4
折半插入排序
排序过程:用折半查找方法确定插入位置的排序叫~
初始时令i=s,j=t 首先从j所指位置向前搜索第一个关键字小于x的记录,并和rp 交换 再从i所指位置起向后搜索,找到第一个关键字大于x的记录, 和rp交换 重复上述两步,直至i==j为止 再分别对两个子序列进行快速排序,直到每个子序列只含有 一个记录为止
x 例 初始关键字: 27 49 i 完成一趟排序: ( 27 38 13 49 65 i 13) 49 97 76 j 97 49 13 j 97 65 49 27 50 j 50)
13 38
76 65 27 49
堆排序:将无序序列建成一个堆,得到关键字最小 (或最大)的记录;输出堆顶的最小(大)值后,使 剩余的n-1个元素重又建成一个堆,则可得到n个元素 的次小值;重复执行,得到一个有序序列,这个过程 叫~ 堆排序需解决的两个问题:
如何由一个无序序列建成一个堆? 如何在输出堆顶元素之后,调整剩余元素,使之成为一个新 的堆?
按排序所需工作量
《数据结构》陈慧南 第10章
n( n − 1 ) ∑i = 2 i =1
南京邮电大学计算机学院 陈慧南 2006年9月 2006年
9.2.3 冒泡排序
(48,36,68,72,12,48,02) 48,36,68,72,12,48,02) 36,48,68,72,12,48,02) (36,48,68,72,12,48,02) 36,48,68,72,12,48,02) (36,48,68,72,12,48,02) 36,48,68,72,12,48,02) (36,48,68,72,12,48,02) 36,48,68,12,72,48,02) (36,48,68,12,72,48,02) (36,48,68,12,48,72,02) 36,48,68,12,48,72,02) 36,48,68,12,48,02,72) (36,48,68,12,48,02,72)
南京邮电大学计算机学院 陈慧南 2006年9月 2006年
序列中两个元素R 序列中两个元素 i和Rj (i<j),且Ki=Kj,若 , 排序后仍保持p(i)<p(j),即Ri 仍然排在 j之 仍然排在R 排序后仍保持 , 则称所用的排序算法是稳定的。 反之, 前 , 则称所用的排序算法是稳定的 。 反之 , 称该排序算法是不稳定的。 称该排序算法是不稳定的。 如果待排序元素总数相对于内存而言较小, 如果待排序元素总数相对于内存而言较小 , 整个排序过程可以在内存中进行, 整个排序过程可以在内存中进行 , 则称之 为内部排序; 反之, 为内部排序 ; 反之 , 如果待排序元素总数 较多, 不能全部放入内存, 较多 , 不能全部放入内存 , 排序过程中需 访问外存, 则称之为外部排序。 访问外存 , 则称之为外部排序 。 本章讨论 内部排序。 内部排序。
第10章-内部排序PPT课件
-
算法效率
时间复杂度
待排序记录按关键字从小到大排列(正序)
n
比较次数: 1 n 1 i2
移动次数: 0
待排序记录按关键字从大到小排列(逆序)
比较次数:
n i (n2)(n1)
i2
2
移动次数:
n
(n4)(n1)
(i1)
i2
2
待排序记录随机,取平均值
比较次数: n 2
4
移动次数: n 2
4
总的时间复杂度:T(n)=O(n2)
空间复杂度:S(n)=O(1)
-
3. 折半插入排序
排序过程:用折半查找方法确定插入位置。 举例:
i=1: (38) (49) 38 65 97 76 13 27 49
5 R’={5}
R={10,2}
2 R’={2,5}
R={2}
10 R’={2,5,10}
R={ }
2 R’={2,2,5,10}
-
2. 直接插入排序
排序过程:整个排序过程为n-1趟插入
将序列中第1个记录看成是一个有序子序列 从第2个记录开始,逐个进行插入,直至整个序列有序
R1 R2 …… Rn
内部排序适用于记录个数不很多的小文件; 外部排序则适用于记录个数太多,不能一次 将其全部放入内存的大文件
排序依据策略
插入排序:直接插入排序, 折半插入排序, 希尔排序 交换排序:冒泡排序, 快速排序 选择排序:简单选择排序, 堆排序 归并排序:2-路归并排序 基数排序
数据结构 排序
2019/9/7
30
10.4.1 简单选择排序
待排记录序列的状态为:
有序序列R[1..i-1] 无序序列 R[i..n]
有序序列中所有记录的关键字均小于无序序列中记 录的关键字,第i趟简单选择排序是从无序序列 R[i..n]的n-i+1记录中选出关键字最小的记录加入 有序序列
2019/9/7
5
排序的类型定义
#define MAXSIZE 20 // 待排序记录的个数
typedef int KeyType;
typedef struct
{ KeyType key;
InfoType otherinfo; ∥记录其它数据域
} RecType;
typedef struct {
RecType r[MAXSIZE+1];
分别进行快速排序:[17] 28 [33] 结束 结束
[51 62] 87 [96] 51 [62] 结束
结束
快速排序后的序列: 17 28 33 51 51 62 87 96
2019/9/7
26
自测题 4 快速排序示例
对下列一组关键字 (46,58,15,45,90,18,10,62) 试写出快速排序的每一趟的排序结果
final↑ ↑first
i=8
[51 51 62 87 96 17 28 33]
final↑ ↑first
2019/9/7
14
希尔(shell )排序
基本思想:从“减小n”和“基本有序”两 方面改进。
将待排序的记录划分成几组,从而减少参 与直接插入排序的数据量,当经过几次分 组排序后,记录的排列已经基本有序,这 个时候再对所有的记录实施直接插入排序。
数据结构教程李春葆课后答案第10章内排序
void fun(int a[],int n,int b[]) { int i; for (i=0;i<n;i++) b[a[i]-1]=a[i]; }
11. 设计一个双向冒泡排序的算法,即在排序过程中交替改变扫描方向。 解:置 i 的初值为 0,先从后向前从无序区 R[i..n-i-1]归位一个最小元素 R[i]到有序区 R[0..i-1],再从前向后从无序区 R[i..n-i-1]归位一个最大元素到有序区 R[n-i..n-1]。当某趟 没有元素交换时,则结束;否则将 i 增加 1。对应的算法如下:
第 10 章 内排序
教材中练习题及参考答案
1. 直接插入排序算法在含有n个元素的初始数据正序、反序和数据全部相等时,时间 复杂度各是多少? 答:含有n个元素的初始数据正序时,直接插入排序算法的时间复杂度为O(n)。 含有n个元素的初始数据反序时,直接插入排序算法的时间复杂度为O(n2)。 含有n个元素的初始数据全部相等时,直接插入排序算法的时间复杂度为O(n)。 2. 回答以下关于直接插入排序和折半插入排序的问题: (1)使用折半插入排序所要进行的关键字比较次数,是否与待排序的元素的初始状态 有关? (2)在一些特殊情况下,折半插入排序比直接插入排序要执行更多的关键字比较,这 句话对吗? 答:(1)使用折半插入排序所要进行的关键字比较次数,与待排序的元素的初始状态 无关。无论待排序序列是否有序,已形成的部分子序列是有序的。折半插入排序首先查找 插入位置,插入位置是判定树失败的位置(对应外部节点),失败位置只能在判定树的最 下两层上。 (2)一些特殊情况下,折半插入排序的确比直接插入排序需要更多的关键字比较,例 如,在待排序序列正序的情况下便是如此。 3. 有以下关于排序的算法:
数据结构chapter_10
typedef struct { //定义每个记录 数据元素) 定义每个记录( //定义每个记录(数据元素)的结构 KeyType key ; //关键字 //关键字 InfoType otherinfo; //其它数据项 //其它数据项 }RedType; //记录类型 //记录类型 typedef struct { //定义顺序表 定义顺序表L //定义顺序表L的结构 RecordType r [ MAXSIZE +1 ]; //存储顺序表的向量 //存储顺序表的向量 //r[0] r[0]一般作哨兵或缓冲区 //r[0]一般作哨兵或缓冲区 int length ; //顺序表的长度 //顺序表的长度 }SqList; //顺序表类型 //顺序表类型
void BInsertSort (SqList &L) {
// 对顺序表 作折半插入排序 对顺序表L作折半插入排序 for ( i=2; i<=L.length; ++i ) { L.r[0] = L.r[i]; // 将L.r[i]暂存到 暂存到L.r[0] 暂存到 low = 1; high = i-1; while (low<=high) { // 在r[low..high]中折半查找有序插入的位置 中折半查找有序插入的位置 m = (low+high)/2; // 折半 if (L.r[0].key < L.r[m].key) high = m-1; // 插入点在低半区 else low = m+1; // 插入点在高半区 } // while for ( j=i-1; j>=low; --j ) L.r[j+1] = L.r[j]; // 记录后移 // 插入 L.r[high+1] = L.r[0]; } } // BInsertSort
数据结构Ch10习题答案
第十章内部排序一、择题1.用直接插入排序法对下面四个表进行(由小到大)排序,比较次数最少的是(B)。
A.(94,32,40,90,80,46,21,69)插32,比2次插40,比2次插90,比2次插80,比3次插46,比4次插21,比7次插69,比4次B.(21,32,46,40,80,69,90,94)插32,比1次插46,比1次插40,比2次插80,比1次插69,比2次插90,比1次插94,比1次C.(32,40,21,46,69,94,90,80)插40,比1次插21,比3次插46,比1次插69,比1次插94,比1次插90,比2次插80,比3次D.(90,69,80,46,21,32,94,40)插69,比2次插80,比2次插46,比4次插21,比5次插32,比5次插94,比1次插40,比6次2.下列排序方法中,哪一个是稳定的排序方法(BD)。
A.希尔排序B.直接选择排序C.堆排序D.冒泡排序下列3题基于如下代码:for(i=2;i<=n;i++){ x=A[i];j=i-1;while(j>0&&A[j]>x){ A[j+1]=A[j];j--;}A[j+1]=x}3.这一段代码所描述的排序方法称作(A)。
A.插入排序B.冒泡排序C.选择排序D.快速排序4.这一段代码所描述的排序方法的平均执行时间为(D)A.O(log2n) B.O(n) C.O(nlog2n) D.O(n2)5.假设这段代码开始执行时,数组A中的元素已经按值的递增次序排好了序,则这段代码的执行时间为(B)。
A.O(log2n) B.O(n) C.O(nlog2n) D.O(n2)6.在快速排序过程中,每次被划分的表(或了表)分成左、右两个子表,考虑这两个子表,下列结论一定正确是(B)。
A.左、右两个子表都已各自排好序B.左边子表中的元素都不大于右边子表中的元素C.左边子表的长度小于右边子表的长度D.左、右两个子表中元素的平均值相等7.对n个记录进行堆排序,最坏情况下的执行时间为(C)。
