第八章 排序
排序定义 —— 将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列叫 ~
排序分类
按待排序记录所在位置
内部排序:待排序记录存放在内存
外部排序:排序过程中需对外存进行访问的排序
按排序依据原则
插入排序:直接插入排序、折半插入排序、希尔排序
交换排序:冒泡排序、快速排序
选择排序:简单选择排序、堆排序
归并排序,2-路归并排序
基数排序
按排序所需工作量
简单的排序方法,T(n)=O(n2)
先进的排序方法,T(n)=O(logn)
基数排序,T(n)=O(d.n)
排序基本操作
比较两个关键字大小
将记录从一个位置移动到另一个位置
§ 8.1 插入排序
直接插入排序
排序过程:整个排序过程为 n-1趟插入,即先将序列中第 1个记录看成是一个有序子序列,然后从第 2个记录开始,逐个进行插入,直至整个序列有序
算法描述例 49 38 65 97 76 13 27
i=2 38 (38 49) 65 97 76 13 27
i=3 65 (38 49 65) 97 76 13 27
i=4 97 (38 49 65 97) 76 13 27
i=5 76 (38 49 65 76 97) 13 27
i=6 13 (13 38 49 65 76 97) 27
i=1 ( )
i=7 (13 38 49 65 76 97) 2727
jj j jjj
977665493827
(13 27 38 49 65 76 97)排序结果:
算法评价
时间复杂度
若待排序记录按关键字从小到大排列 (正序 )
关键字比较次数:
11
2
nn
i
记录移动次数:
)1(22
2
nn
i
若待排序记录按关键字从大到小排列 (逆序 )
关键字比较次数:
2
)1)(2(
2
nnin
i
记录移动次数:
2
)1)(4()1(
2
nnin
i
若待排序记录是随机的,取平均值
关键字比较次数:
4
2n
记录移动次数:
4
2n
T(n)=O(n2)
空间复杂度,S(n)=O(1)
Ch8_1.c
折半插入排序
排序过程:用折半查找方法确定插入位置的排序叫 ~
例 i=1 (30) 13 70 85 39 42 6 20
i=2 13 (13 30) 70 85 39 42 6 20
i=7 6 (6 13 30 39 42 70 85 ) 20
…...
i=8 20 (6 13 30 39 42 70 85 ) 20
s jm
i=8 20 (6 13 30 39 42 70 85 ) 20
s jm
i=8 20 (6 13 30 39 42 70 85 ) 20
s jm
i=8 20 (6 13 30 39 42 70 85 ) 20
sj
i=8 20 (6 13 20 30 39 42 70 85 )
算法描述
算法评价
时间复杂度,T(n)=O(n2)
空间复杂度,S(n)=O(1)
Ch8_2.c
希尔排序 (缩小增量法 )
排序过程:先取一个正整数 d1<n,把所有相隔 d1的记录放一组,组内进行直接插入排序;然后取 d2<d1,
重复上述分组和排序操作;直至 di=1,即所有记录放进一个组中排序为止取 d3=1
三趟分组,13 27 48 55 4 49 38 65 97 76
三趟排序,4 13 27 38 48 49 55 65 76 97
例 初始,49 38 65 97 76 13 27 48 55 4
一趟排序,13 27 48 55 4 49 38 65 97 76
二趟排序,13 4 48 38 27 49 55 65 97 76
取 d1=5
一趟分组,49 38 65 97 76 13 27 48 55 4
取 d2=3
二趟分组,13 27 48 55 4 49 38 65 97 76
算法描述
Ch8_3.c
例 49 38 65 97 76 13 27 48 55 4
#define T 3
int d[]={5,3,1};
j i
4913 3827
一趟排序,13 27 48 55 4 49 38 65 97 76
j ij i
274
j ij i
55
j i
38
j ij ij i
二趟排序,13 4 48 38 27 49 55 65 97 76
j ij i
6548
j i
9755
j i
764
希尔排序特点
子序列的构成不是简单的“逐段分割”,而是将相隔某个增量的记录组成一个子序列
希尔排序可提高排序速度,因为
分组后 n值减小,n2更小,而 T(n)=O(n2),所以 T(n)从总体上看是减小了
关键字较小的记录跳跃式前移,在进行最后一趟增量为 1
的插入排序时,序列已基本有序
增量序列取法
无除 1以外的公因子
最后一个增量值必须为 1
§ 8.2 交换排序
冒泡排序
排序过程
将第一个记录的关键字与第二个记录的关键字进行比较,若为逆序 r[1].key>r[2].key,则交换;然后比较第二个记录与第三个记录;依次类推,直至第 n-1个记录和第 n个记录比较为止 —— 第一趟冒泡排序,结果关键字最大的记录被安置在最后一个记录上
对前 n-1个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第 n-1个记录位置
重复上述过程,直到“在一趟排序过程中没有进行过交换记录的操作”为止例 49
38
65
97
76
13
27
30初始关键字
38
49
65
76
13
27
30
97第一趟
38
49
65
13
27
30
76
第二趟
38
49
13
27
30
65
第三趟
38
13
27
30
49
第四趟
13
27
30
38
第五趟
13
27
30
第六趟
38
49
76
9713
9727
9730
97
13
76
76
7627
30
13
6527
6530
65
13
13
49
4930
4927
3827
3830
38
算法描述
算法评价
时间复杂度
最好情况(正序)
比较次数,n-1
移动次数,0
最坏情况(逆序)
比较次数,)(
21)( 2
1
1
nninn
i
移动次数,)(
23)(3 21 nnin
n
i
空间复杂度,S(n)=O(1)
T(n)=O(n2)
Ch8_4.c
快速排序
基本思想:通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录进行排序,
以达到整个序列有序
排序过程:对 r[s……t] 中记录进行一趟快速排序,附设两个指针 i和 j,设枢轴记录 rp=r[s],x=rp.key
初始时令 i=s,j=t
首先从 j所指位置向前搜索第一个关键字小于 x的记录,并和 rp
交换
再从 i所指位置起向后搜索,找到第一个关键字大于 x的记录,
和 rp交换
重复上述两步,直至 i==j为止
再分别对两个子序列进行快速排序,直到每个子序列只含有一个记录为止例 初始关键字,49 38 65 97 76 13 27 50
i j
x
ji
完成一趟排序,( 27 38 13) 49 (76 97 65 50)
分别进行快速排序,( 13) 27 (38) 49 (50 65) 76 (97)
快速排序结束,13 27 38 49 50 65 76 97
4927
i i ji j
49 65
j
13 49
i
49 97
i j
算法描述
算法评价
时间复杂度
最好情况(每次总是选到中间值作枢轴) T(n)=O(nlog2n)
最坏情况(每次总是选到最小或最大元素作枢轴)
T(n)=O(n2)
空间复杂度:需栈空间以实现递归
最坏情况,S(n)=O(n)
一般情况,S(n)=O(log2n)
T(n)=O(n2)
Ch8_5.c
§ 8.3 选择排序
简单选择排序
排序过程
首先通过 n-1次关键字比较,从 n个记录中找出关键字最小的记录,将它与第一个记录交换
再通过 n-2次比较,从剩余的 n-1个记录中找出关键字次小的记录,将它与第二个记录交换
重复上述操作,共进行 n-1趟排序后,排序结束例 初始,[ 49 38 65 97 76 13 27 ]
k
j j j j j j
k k
i=1 13 49
一趟,13 [38 65 97 76 49 27 ]i=2
k k
j j j jj
27 38
二趟,13 27 [65 97 76 49 38 ]
三趟,13 27 38 [97 76 49 65 ]
四趟,13 27 38 49 [76 97 65 ]
五趟,13 27 38 49 65 [97 76 ]
六趟,13 27 38 49 65 76 [97 ]
排序结束,13 27 38 49 65 76 97
算法描述
算法评价
时间复杂度
记录移动次数
最好情况,0
最坏情况,3(n-1)
比较次数:
)(21)( 21
1
nninn
i
空间复杂度,S(n)=O(1)
T(n)=O(n2)
Ch8_6.c
堆排序
堆的定义,n个元素的序列 (k1,k2,……kn),当且仅当满足下列关系时,称之为堆或 (i=1,2,…..,?n/2?)ki?k2iki?k2i+1 ki?k2ik
i?k2i+1
例 ( 96,83,27,38,11,9) 例 ( 13,38,27,50,76,65,49,97)
96
27
91138
83
13
2738
49657650
97
可将堆序列看成完全二叉树,则堆顶元素(完全二叉树的根)必为序列中
n个元素的最小值或最大值
堆排序:将无序序列建成一个堆,得到关键字最小
(或最大)的记录;输出堆顶的最小(大)值后,使剩余的 n-1个元素重又建成一个堆,则可得到 n个元素的次小值;重复执行,得到一个有序序列,这个过程叫 ~
堆排序需解决的两个问题:
如何由一个无序序列建成一个堆?
如何在输出堆顶元素之后,调整剩余元素,使之成为一个新的堆?
第二个问题解决方法 —— 筛选
方法:输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”
例 13
2738
49657650
97
97
2738
49657650
13
输出,13
27
4938
97657650
13
输出,13
97
4938
27657650
13
输出,13 27
38
4950
27657697
13
输出,13 27
65
4950
27387697
13
输出,13 27 38
49
6550
27387697
13
输出,13 27 38
76
6550
27384997
13
输出,13 27 38 49
50
6576
27384997
13
输出,13 27 38 49
97
6576
27384950
13
输出,13 27 38 49 50
65
9776
27384950
13
输出,13 27 38 49 50
97
6576
27384950
13
输出,13 27 38 49 50 65
76
6597
27384950
13
输出,13 27 38 49 50 65
97
6576
27384950
13
输出,13 27 38 49 50 65 76
97
6576
27384950
13
输出,13 27 38 49 50 65 76 97
算法描述
第一个问题解决方法
方法:从无序序列的第?n/2?个元素(即此无序序列对应的完全二叉树的最后一个非终端结点)起,至第一个元素止,进行反复筛选例 含 8个元素的无序序列( 49,38,65,97,76,13,27,50)
49
6538
27137697
50
49
6538
27137650
97
49
1338
27657650
97
49
1338
27657650
97
13
2738
49657650
97
算法描述
算法评价
时间复杂度:最坏情况下 T(n)=O(nlogn)
空间复杂度,S(n)=O(1)
Ch8_7.c
§ 8.4 归并排序
归并 —— 将两个或两个以上的有序表组合成一个新的有序表,叫 ~
2-路归并排序
排序过程
设初始序列含有 n个记录,则可看成 n个有序的子序列,每个子序列长度为 1
两两合并,得到?n/2?个长度为 2或 1的有序子序列
再两两合并,…… 如此重复,直至得到一个长度为 n的有序序列为止例 初始关键字,[49] [38] [65] [97] [76] [13] [27]
一趟归并后,[38 49] [65 97] [13 76] [27]
二趟归并后,[38 49 65 97] [13 27 76]
三趟归并后,[13 27 38 49 65 76 97]
算法描述
算法评价
时间复杂度,T(n)=O(nlog2n)
空间复杂度,S(n)=O(n)
Ch8_9.c
§ 8.5 基数排序
多关键字排序
定义:
例 对 52张扑克牌按以下次序排序:
2<?3<……<?A<?2<?3<……<?A<
2<?3<……<?A<?2<?3<……<?A
两个关键字:花色(?<?<?<?)
面值( 2<3<……<A )
并且“花色”地位高于“面值”
多关键字排序方法
最高位优先法( MSD),先对最高位关键字 k1( 如花色)排序,将序列分成若干子序列,每个子序列有相同的 k1值;然后让每个子序列对次关键字 k2( 如面值)排序,又分成若干更小的子序列;依次重复,直至就每个子序列对最低位关键字 kd排序;最后将所有子序列依次连接在一起成为一个有序序列
最低位优先法 (LSD):从最低位关键字 kd起进行排序,然后再对高一位的关键字排序,…… 依次重复,直至对最高位关键字 k1排序后,便成为一个有序序列
MSD与 LSD不同特点
按 MSD排序,必须将序列逐层分割成若干子序列,然后对各子序列分别排序
按 LSD排序,不必分成子序列,对每个关键字都是整个序列参加排序;并且可不通过关键字比较,而通过若干次分配与收集实现排序
链式基数排序
基数排序:借助“分配”和“收集”对单逻辑关键字进行排序的一种方法
链式基数排序:用链表作存储结构的基数排序
链式基数排序步骤
设置 10个队列,f[i]和 e[i]分别为第 i个队列的头指针和尾指针
第一趟分配对最低位关键字(个位)进行,改变记录的指针值,将链表中记录分配至 10个链队列中,每个队列记录的关键字的个位相同
第一趟收集是改变所有非空队列的队尾记录的指针域,令其指向下一个非空队列的队头记录,重新将 10个队列链成一个链表
重复上述两步,进行第二趟、第三趟分配和收集,分别对十位、百位进行,最后得到一个有序序列例 初始状态:
278 109 063 930 589 184 505 269 008 083
109
589
269
278063930
083
184 505
008
e[0] e[1] e[2] e[3] e[4] e[5] e[6] e[7] e[8] e[9]
f[0] f[1] f[2] f[3] f[4] f[5] f[6] f[7] f[8] f[9]
一趟分配
930 063 083 184 505 278 008 109 589 269
一趟收集:
505 008 109 930 063 269 278 083 184 589
二趟收集:
083
184
589
063505
269
930
e[0] e[1] e[2] e[3] e[4] e[5] e[6] e[7] e[8] e[9]
f[0] f[1] f[2] f[3] f[4] f[5] f[6] f[7] f[8] f[9]
二趟分配
008
109
278
930 063 083 184 505 278 008 109 589 269
一趟收集:
008 063 083 109 184 269 278 505 589 930
三趟收集:
109008
184
930
e[0] e[1] e[2] e[3] e[4] e[5] e[6] e[7] e[8] e[9]
f[0] f[1] f[2] f[3] f[4] f[5] f[6] f[7] f[8] f[9]
三趟分配
063
083
269
278
505
589
505 008 109 930 063 269 278 083 184 589
二趟收集:
算法描述
算法评价
时间复杂度:
分配,T(n)=O(n)
收集,T(n)=O(rd)
T(n)=O(d(n+rd))
其中,n—— 记录数
d—— 关键字数
rd—— 关键字取值范围
空间复杂度,S(n)=2rd个队列指针 +n个指针域空间
Ch8_10.c
初始状态:
1
278 109 063 930 589 184 505 269 008 0830
2 3 4 5 6 7 8 9 10
f[0]=0 e[0]=0
f[1]=0 e[1]=0
f[2]=0 e[2]=0
f[3]=0 e[3]=0
f[4]=0 e[4]=0
f[5]=0 e[5]=0
f[6]=0 e[6]=0
f[7]=0 e[7]=0
f[8]=0 e[8]=0
f[9]=0 e[9]=0
1 1
2 2
3 3
4 4
5
66
77
8
9
10
4
930 063 083 184 505 278 008 109 589 2690
3 10 6 7 1 9 2 5 8
f[0]=0 e[0]=0
f[1]=0 e[1]=0
f[2]=0 e[2]=0
f[3]=0 e[3]=0
f[4]=0 e[4]=0
f[5]=0 e[5]=0
f[6]=0 e[6]=0
f[7]=0 e[7]=0
f[8]=0 e[8]=0
f[9]=0 e[9]=0
1
3
44
7 79
10
4
930 063 083 184 505 278 008 109 589 2690
3 10 6 7 1 9 2 5 8
一趟收集:
3
10
1
6
2
5
8
7
505 008 109 930 063 269 278 083 184 5890
9 2 4 3 8 1 10 6 5
二趟收集:
7
505 008 109 930 063 269 278 083 184 5890
9 2 4 3 8 1 10 6 5
二趟收集:
f[0]=0 e[0]=0
f[1]=0 e[1]=0
f[2]=0 e[2]=0
f[3]=0 e[3]=0
f[4]=0 e[4]=0
f[5]=0 e[5]=0
f[6]=0 e[6]=0
f[7]=0 e[7]=0
f[8]=0 e[8]=0
f[9]=0 e[9]=04 4
7
9
2
8
7
9
2
8
9
008 063 083 109 184 269 278 505 589 9300
3 10 2 6 8 1 7 5 4
三趟收集:
3
1
10
6
5
排序定义 —— 将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列叫 ~
排序分类
按待排序记录所在位置
内部排序:待排序记录存放在内存
外部排序:排序过程中需对外存进行访问的排序
按排序依据原则
插入排序:直接插入排序、折半插入排序、希尔排序
交换排序:冒泡排序、快速排序
选择排序:简单选择排序、堆排序
归并排序,2-路归并排序
基数排序
按排序所需工作量
简单的排序方法,T(n)=O(n2)
先进的排序方法,T(n)=O(logn)
基数排序,T(n)=O(d.n)
排序基本操作
比较两个关键字大小
将记录从一个位置移动到另一个位置
§ 8.1 插入排序
直接插入排序
排序过程:整个排序过程为 n-1趟插入,即先将序列中第 1个记录看成是一个有序子序列,然后从第 2个记录开始,逐个进行插入,直至整个序列有序
算法描述例 49 38 65 97 76 13 27
i=2 38 (38 49) 65 97 76 13 27
i=3 65 (38 49 65) 97 76 13 27
i=4 97 (38 49 65 97) 76 13 27
i=5 76 (38 49 65 76 97) 13 27
i=6 13 (13 38 49 65 76 97) 27
i=1 ( )
i=7 (13 38 49 65 76 97) 2727
jj j jjj
977665493827
(13 27 38 49 65 76 97)排序结果:
算法评价
时间复杂度
若待排序记录按关键字从小到大排列 (正序 )
关键字比较次数:
11
2
nn
i
记录移动次数:
)1(22
2
nn
i
若待排序记录按关键字从大到小排列 (逆序 )
关键字比较次数:
2
)1)(2(
2
nnin
i
记录移动次数:
2
)1)(4()1(
2
nnin
i
若待排序记录是随机的,取平均值
关键字比较次数:
4
2n
记录移动次数:
4
2n
T(n)=O(n2)
空间复杂度,S(n)=O(1)
Ch8_1.c
折半插入排序
排序过程:用折半查找方法确定插入位置的排序叫 ~
例 i=1 (30) 13 70 85 39 42 6 20
i=2 13 (13 30) 70 85 39 42 6 20
i=7 6 (6 13 30 39 42 70 85 ) 20
…...
i=8 20 (6 13 30 39 42 70 85 ) 20
s jm
i=8 20 (6 13 30 39 42 70 85 ) 20
s jm
i=8 20 (6 13 30 39 42 70 85 ) 20
s jm
i=8 20 (6 13 30 39 42 70 85 ) 20
sj
i=8 20 (6 13 20 30 39 42 70 85 )
算法描述
算法评价
时间复杂度,T(n)=O(n2)
空间复杂度,S(n)=O(1)
Ch8_2.c
希尔排序 (缩小增量法 )
排序过程:先取一个正整数 d1<n,把所有相隔 d1的记录放一组,组内进行直接插入排序;然后取 d2<d1,
重复上述分组和排序操作;直至 di=1,即所有记录放进一个组中排序为止取 d3=1
三趟分组,13 27 48 55 4 49 38 65 97 76
三趟排序,4 13 27 38 48 49 55 65 76 97
例 初始,49 38 65 97 76 13 27 48 55 4
一趟排序,13 27 48 55 4 49 38 65 97 76
二趟排序,13 4 48 38 27 49 55 65 97 76
取 d1=5
一趟分组,49 38 65 97 76 13 27 48 55 4
取 d2=3
二趟分组,13 27 48 55 4 49 38 65 97 76
算法描述
Ch8_3.c
例 49 38 65 97 76 13 27 48 55 4
#define T 3
int d[]={5,3,1};
j i
4913 3827
一趟排序,13 27 48 55 4 49 38 65 97 76
j ij i
274
j ij i
55
j i
38
j ij ij i
二趟排序,13 4 48 38 27 49 55 65 97 76
j ij i
6548
j i
9755
j i
764
希尔排序特点
子序列的构成不是简单的“逐段分割”,而是将相隔某个增量的记录组成一个子序列
希尔排序可提高排序速度,因为
分组后 n值减小,n2更小,而 T(n)=O(n2),所以 T(n)从总体上看是减小了
关键字较小的记录跳跃式前移,在进行最后一趟增量为 1
的插入排序时,序列已基本有序
增量序列取法
无除 1以外的公因子
最后一个增量值必须为 1
§ 8.2 交换排序
冒泡排序
排序过程
将第一个记录的关键字与第二个记录的关键字进行比较,若为逆序 r[1].key>r[2].key,则交换;然后比较第二个记录与第三个记录;依次类推,直至第 n-1个记录和第 n个记录比较为止 —— 第一趟冒泡排序,结果关键字最大的记录被安置在最后一个记录上
对前 n-1个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第 n-1个记录位置
重复上述过程,直到“在一趟排序过程中没有进行过交换记录的操作”为止例 49
38
65
97
76
13
27
30初始关键字
38
49
65
76
13
27
30
97第一趟
38
49
65
13
27
30
76
第二趟
38
49
13
27
30
65
第三趟
38
13
27
30
49
第四趟
13
27
30
38
第五趟
13
27
30
第六趟
38
49
76
9713
9727
9730
97
13
76
76
7627
30
13
6527
6530
65
13
13
49
4930
4927
3827
3830
38
算法描述
算法评价
时间复杂度
最好情况(正序)
比较次数,n-1
移动次数,0
最坏情况(逆序)
比较次数,)(
21)( 2
1
1
nninn
i
移动次数,)(
23)(3 21 nnin
n
i
空间复杂度,S(n)=O(1)
T(n)=O(n2)
Ch8_4.c
快速排序
基本思想:通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录进行排序,
以达到整个序列有序
排序过程:对 r[s……t] 中记录进行一趟快速排序,附设两个指针 i和 j,设枢轴记录 rp=r[s],x=rp.key
初始时令 i=s,j=t
首先从 j所指位置向前搜索第一个关键字小于 x的记录,并和 rp
交换
再从 i所指位置起向后搜索,找到第一个关键字大于 x的记录,
和 rp交换
重复上述两步,直至 i==j为止
再分别对两个子序列进行快速排序,直到每个子序列只含有一个记录为止例 初始关键字,49 38 65 97 76 13 27 50
i j
x
ji
完成一趟排序,( 27 38 13) 49 (76 97 65 50)
分别进行快速排序,( 13) 27 (38) 49 (50 65) 76 (97)
快速排序结束,13 27 38 49 50 65 76 97
4927
i i ji j
49 65
j
13 49
i
49 97
i j
算法描述
算法评价
时间复杂度
最好情况(每次总是选到中间值作枢轴) T(n)=O(nlog2n)
最坏情况(每次总是选到最小或最大元素作枢轴)
T(n)=O(n2)
空间复杂度:需栈空间以实现递归
最坏情况,S(n)=O(n)
一般情况,S(n)=O(log2n)
T(n)=O(n2)
Ch8_5.c
§ 8.3 选择排序
简单选择排序
排序过程
首先通过 n-1次关键字比较,从 n个记录中找出关键字最小的记录,将它与第一个记录交换
再通过 n-2次比较,从剩余的 n-1个记录中找出关键字次小的记录,将它与第二个记录交换
重复上述操作,共进行 n-1趟排序后,排序结束例 初始,[ 49 38 65 97 76 13 27 ]
k
j j j j j j
k k
i=1 13 49
一趟,13 [38 65 97 76 49 27 ]i=2
k k
j j j jj
27 38
二趟,13 27 [65 97 76 49 38 ]
三趟,13 27 38 [97 76 49 65 ]
四趟,13 27 38 49 [76 97 65 ]
五趟,13 27 38 49 65 [97 76 ]
六趟,13 27 38 49 65 76 [97 ]
排序结束,13 27 38 49 65 76 97
算法描述
算法评价
时间复杂度
记录移动次数
最好情况,0
最坏情况,3(n-1)
比较次数:
)(21)( 21
1
nninn
i
空间复杂度,S(n)=O(1)
T(n)=O(n2)
Ch8_6.c
堆排序
堆的定义,n个元素的序列 (k1,k2,……kn),当且仅当满足下列关系时,称之为堆或 (i=1,2,…..,?n/2?)ki?k2iki?k2i+1 ki?k2ik
i?k2i+1
例 ( 96,83,27,38,11,9) 例 ( 13,38,27,50,76,65,49,97)
96
27
91138
83
13
2738
49657650
97
可将堆序列看成完全二叉树,则堆顶元素(完全二叉树的根)必为序列中
n个元素的最小值或最大值
堆排序:将无序序列建成一个堆,得到关键字最小
(或最大)的记录;输出堆顶的最小(大)值后,使剩余的 n-1个元素重又建成一个堆,则可得到 n个元素的次小值;重复执行,得到一个有序序列,这个过程叫 ~
堆排序需解决的两个问题:
如何由一个无序序列建成一个堆?
如何在输出堆顶元素之后,调整剩余元素,使之成为一个新的堆?
第二个问题解决方法 —— 筛选
方法:输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”
例 13
2738
49657650
97
97
2738
49657650
13
输出,13
27
4938
97657650
13
输出,13
97
4938
27657650
13
输出,13 27
38
4950
27657697
13
输出,13 27
65
4950
27387697
13
输出,13 27 38
49
6550
27387697
13
输出,13 27 38
76
6550
27384997
13
输出,13 27 38 49
50
6576
27384997
13
输出,13 27 38 49
97
6576
27384950
13
输出,13 27 38 49 50
65
9776
27384950
13
输出,13 27 38 49 50
97
6576
27384950
13
输出,13 27 38 49 50 65
76
6597
27384950
13
输出,13 27 38 49 50 65
97
6576
27384950
13
输出,13 27 38 49 50 65 76
97
6576
27384950
13
输出,13 27 38 49 50 65 76 97
算法描述
第一个问题解决方法
方法:从无序序列的第?n/2?个元素(即此无序序列对应的完全二叉树的最后一个非终端结点)起,至第一个元素止,进行反复筛选例 含 8个元素的无序序列( 49,38,65,97,76,13,27,50)
49
6538
27137697
50
49
6538
27137650
97
49
1338
27657650
97
49
1338
27657650
97
13
2738
49657650
97
算法描述
算法评价
时间复杂度:最坏情况下 T(n)=O(nlogn)
空间复杂度,S(n)=O(1)
Ch8_7.c
§ 8.4 归并排序
归并 —— 将两个或两个以上的有序表组合成一个新的有序表,叫 ~
2-路归并排序
排序过程
设初始序列含有 n个记录,则可看成 n个有序的子序列,每个子序列长度为 1
两两合并,得到?n/2?个长度为 2或 1的有序子序列
再两两合并,…… 如此重复,直至得到一个长度为 n的有序序列为止例 初始关键字,[49] [38] [65] [97] [76] [13] [27]
一趟归并后,[38 49] [65 97] [13 76] [27]
二趟归并后,[38 49 65 97] [13 27 76]
三趟归并后,[13 27 38 49 65 76 97]
算法描述
算法评价
时间复杂度,T(n)=O(nlog2n)
空间复杂度,S(n)=O(n)
Ch8_9.c
§ 8.5 基数排序
多关键字排序
定义:
例 对 52张扑克牌按以下次序排序:
2<?3<……<?A<?2<?3<……<?A<
2<?3<……<?A<?2<?3<……<?A
两个关键字:花色(?<?<?<?)
面值( 2<3<……<A )
并且“花色”地位高于“面值”
多关键字排序方法
最高位优先法( MSD),先对最高位关键字 k1( 如花色)排序,将序列分成若干子序列,每个子序列有相同的 k1值;然后让每个子序列对次关键字 k2( 如面值)排序,又分成若干更小的子序列;依次重复,直至就每个子序列对最低位关键字 kd排序;最后将所有子序列依次连接在一起成为一个有序序列
最低位优先法 (LSD):从最低位关键字 kd起进行排序,然后再对高一位的关键字排序,…… 依次重复,直至对最高位关键字 k1排序后,便成为一个有序序列
MSD与 LSD不同特点
按 MSD排序,必须将序列逐层分割成若干子序列,然后对各子序列分别排序
按 LSD排序,不必分成子序列,对每个关键字都是整个序列参加排序;并且可不通过关键字比较,而通过若干次分配与收集实现排序
链式基数排序
基数排序:借助“分配”和“收集”对单逻辑关键字进行排序的一种方法
链式基数排序:用链表作存储结构的基数排序
链式基数排序步骤
设置 10个队列,f[i]和 e[i]分别为第 i个队列的头指针和尾指针
第一趟分配对最低位关键字(个位)进行,改变记录的指针值,将链表中记录分配至 10个链队列中,每个队列记录的关键字的个位相同
第一趟收集是改变所有非空队列的队尾记录的指针域,令其指向下一个非空队列的队头记录,重新将 10个队列链成一个链表
重复上述两步,进行第二趟、第三趟分配和收集,分别对十位、百位进行,最后得到一个有序序列例 初始状态:
278 109 063 930 589 184 505 269 008 083
109
589
269
278063930
083
184 505
008
e[0] e[1] e[2] e[3] e[4] e[5] e[6] e[7] e[8] e[9]
f[0] f[1] f[2] f[3] f[4] f[5] f[6] f[7] f[8] f[9]
一趟分配
930 063 083 184 505 278 008 109 589 269
一趟收集:
505 008 109 930 063 269 278 083 184 589
二趟收集:
083
184
589
063505
269
930
e[0] e[1] e[2] e[3] e[4] e[5] e[6] e[7] e[8] e[9]
f[0] f[1] f[2] f[3] f[4] f[5] f[6] f[7] f[8] f[9]
二趟分配
008
109
278
930 063 083 184 505 278 008 109 589 269
一趟收集:
008 063 083 109 184 269 278 505 589 930
三趟收集:
109008
184
930
e[0] e[1] e[2] e[3] e[4] e[5] e[6] e[7] e[8] e[9]
f[0] f[1] f[2] f[3] f[4] f[5] f[6] f[7] f[8] f[9]
三趟分配
063
083
269
278
505
589
505 008 109 930 063 269 278 083 184 589
二趟收集:
算法描述
算法评价
时间复杂度:
分配,T(n)=O(n)
收集,T(n)=O(rd)
T(n)=O(d(n+rd))
其中,n—— 记录数
d—— 关键字数
rd—— 关键字取值范围
空间复杂度,S(n)=2rd个队列指针 +n个指针域空间
Ch8_10.c
初始状态:
1
278 109 063 930 589 184 505 269 008 0830
2 3 4 5 6 7 8 9 10
f[0]=0 e[0]=0
f[1]=0 e[1]=0
f[2]=0 e[2]=0
f[3]=0 e[3]=0
f[4]=0 e[4]=0
f[5]=0 e[5]=0
f[6]=0 e[6]=0
f[7]=0 e[7]=0
f[8]=0 e[8]=0
f[9]=0 e[9]=0
1 1
2 2
3 3
4 4
5
66
77
8
9
10
4
930 063 083 184 505 278 008 109 589 2690
3 10 6 7 1 9 2 5 8
f[0]=0 e[0]=0
f[1]=0 e[1]=0
f[2]=0 e[2]=0
f[3]=0 e[3]=0
f[4]=0 e[4]=0
f[5]=0 e[5]=0
f[6]=0 e[6]=0
f[7]=0 e[7]=0
f[8]=0 e[8]=0
f[9]=0 e[9]=0
1
3
44
7 79
10
4
930 063 083 184 505 278 008 109 589 2690
3 10 6 7 1 9 2 5 8
一趟收集:
3
10
1
6
2
5
8
7
505 008 109 930 063 269 278 083 184 5890
9 2 4 3 8 1 10 6 5
二趟收集:
7
505 008 109 930 063 269 278 083 184 5890
9 2 4 3 8 1 10 6 5
二趟收集:
f[0]=0 e[0]=0
f[1]=0 e[1]=0
f[2]=0 e[2]=0
f[3]=0 e[3]=0
f[4]=0 e[4]=0
f[5]=0 e[5]=0
f[6]=0 e[6]=0
f[7]=0 e[7]=0
f[8]=0 e[8]=0
f[9]=0 e[9]=04 4
7
9
2
8
7
9
2
8
9
008 063 083 109 184 269 278 505 589 9300
3 10 2 6 8 1 7 5 4
三趟收集:
3
1
10
6
5