第八章 排序
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
61 68
68
72
77
75
72
80
77
87
80
88
87
92
88
96
92 96
最终排序结果:
61
68
72
75
77
80
87
88
92
96
练习
• 已知序列{503,17,512,908,170,897,
275,653,426,154,509,612,677,675} ,采用希尔排序法,对该序列进行升序排序时的 每一趟的结果
21 1
25 2
25* 3
49 08 4 5 08 temp
完成
08 0
16 1
21 2
25 3
25* 4
49 5
插入排序
void InsertSort( LineList R[] , int n ) { int i , j ; LineList tmp ; for( i = 1 ; i < n; i ++ ) { tmp = R[i] ; j = i – 1 ; while( j >= 0 && tmp.key < R[j].key ) { R[j + 1 ] = R[ j ] ;j-- ; } R[ j +1 ] = tmp ; } }
初始状态: i=1,k=1,j=2
49 2
08 0
25 1
25* 3
16 4
21 5
i k
j
08 0
25 1 i k
49
25* 3
16 4
21 5
2 j
49 25
08 0 i
25
1
49
25*
3
16 4
21 5
2
k
j
25* 25
08 0
25
49
25*
16 4 k j
21 5 16 < 25
第八章 排序
Review
• 二叉排序树查找
• 哈希查找
练习
• 试将二分查找改写成递归算法
排序
• 排序
–把一组无序的记录按其关键字的某种次序排序
起来,使具有一定的顺序,便于进行数据查找
• 两大基本操作
–比较关键字的大小
–从一个位置移动到另一个位置
排序的基本概念
• 排序为升序排序,并假定要排序的记录均存储在 线性表中,该线性表由关键字和数据域组成,其 类型定义如下
4
5
temp
比较操作,对有序表是从后 向前比较还是从前向后比较?
i = 2
21 0
25 1
49 2
25* 3
16
4
49 08 5 temp
i = 3
21 0
25 1
49 2
25* 3
16 4
08 5
25* temp
i = 4
21 0
25
1
25*
2
49 3
16 4
08 5
16 temp
i = 5
16 0
希尔排序
[例] 已知有6个待排序的记录,它们的关键字序列为{21,25,49, 25*,16,08},给出用希尔排序法进行排序的过程
初始状态:
21 0
25 1
49 2
25* 3
16 4
08 5
初始状态:
21 0 25 1 49 2 25* 3
16
4
08 5
i=1 d=3
21 25 49 25* 16 08
25* 3
16 4
08 5
[例] 已知有6个待排序的记录,它们的关键字序列为{21,25,49,
25*,16,08},给出用直接插入排序法进行排序的过程 各趟排序结果:
初始状态:
比较与移动 操作次数?
21 0
25 1
49 2
25* 3
16 4
08 5
i = 1
21
25
49
25*
16
08
25
0
1
2
3
直接选择排序
[例] 已知有6个待排序的记录,它们的关键字序列为{21,25,49,
25*,16,08},给出用直接选择排序法进行排序的过程
初始状态:
49 2
21 0
25
1
25*
3
16 4
08 5
最小者 08
i = 0
21 25 49 25* 16 08
交换21,08
0
1
2
3
4
5
i = 1
25
1 49
有序序列R[0..i-1] 无序序列 R[i..n-1]
R[i]
有序序列R[0..i]
无序序列 R[i+1..n-1]
插入排序
[例] 已知有6个待排序的记录,它们的关键字序列为{21,25,49, 25*,16,08},给出用直接插入排序法进行排序的过程
各趟排序结果: 初始状态:
21 0
25 1
49 2
最小者 16
交换25,16
08 0
25*
3 4
16
21 5
2
最小者 21
i = 2
08 0 16 1 49 25* 25 21 5
交换49,21
2
3
4
最小者25* 无交换 i = 3
08 0 1 16 2 21 3 25* 25 49
4
5
最小者25 i = 4
08 0 1 16 2 21 3
25*
#define MaxSize typedef int KeyType; typedef char ElemType[10]; typedef struct { KeyType key ; ElemType data; }LineList; •排序是稳定的 •排序是不稳定的
插入排序
插入排序过程:整个排序过程为n-1趟插入,即先将序列中第1个 记录看成是一个有序子序列,然后从第2个记录开始,逐个进行插 入,直至整个序列有序
希尔排序
[例8.2] 已知有10个待排序的记录,它们的关键字序列为{75,87, 68,92,88,61,77,96,80,72},给出用希尔排序法的过程
初始 第二次 75 61 87 75 68 68 92 77 88 72 61 80 77 87 96 88 80 92 72 96
第三次d=1: 61 75
– 关键字顺序有序: key1 key2 ... keyn – 关键字逆序有序:
堆排序
• 堆是一棵顺序存储的完全二叉树
–每个结点的关键字都不小于其孩子结点的关键
字称为大根堆
–若堆中每个结点的关键字都不大于其孩子结点
的关键字,称为小根堆
堆排序
• 大根堆&小根堆
14 32 87 29 48 34 57 14 29 48
1 i
2
3
08 0
25 1
49 2
25* 3
16 4
21 5
i
k
j
21 16
k 指示当前序列中最小者
08 0
16 1
49
25* 3
25 4
21 5
2
直接选择排序
[例8.3] 已知有10个待排序的记录,它们的关键字序列为{75,87,
68,92,88,61,77,96,80,72},给出直接选择排序法的过程
75
75 92 92
77
77 77 88
96
96 96 96
80
80 80 80
72
87 87 87
直接选择排序
[例8.3] 已知有10个待排序的记录,它们的关键字序列为{75,87,
68,92,88,61,77,96,80,72},给出直接选择排序法的过程
初始序列 75 87 68 92 88 61 77 96 80 72
1
2 50 30 60 86 10 40 50
调整的原则是将左右孩
子中值较小的上调为双亲, 双亲向下调整到结点位置
30
3 60 50 2 40
30
10 60 40
4
35
35
86
10
35
86
45
(a) iBaidu Nhomakorabea4不调整
45
45
(b) i=3需要调整
(c) i=2调整
30 35 2 50 86 10 35 2
i = 4 j = 0
21 0
16 25 1 25 2 25* 3
49 08 4 5
16 temp
i = 4的排序过程完成之后的关键字序列
21 16 0
21 1
25 2
25* 3
49 08 4 5
16 temp
插入排序
• 时间复杂度
– T(n)=O(n2)
• 空间复杂度
–S(n)=O(1)
希尔排序
30 10 35
1 30 10 86
60
40
45
86
60
40
45
60
40
45
(d) i=2调整 1 10 35 45 86 60 30 40
50
(e) i=2调整结果
最后结果 [61
break
Review
• 排序的基本概念
• 插入排序
– 直接插入排序
– 希尔排序
• 选择排序
– 直接选择排序
练习
• 对初始状态如下(长度为n)的各序列进行直接
插入排序时,至多需进行多少次关键字间的比较 (要求排序后的序列按关键字自小至大顺序有序 )
key1 key2 ... keyn
• 缩小增量排序方法
–把记录按下标的一定增量d分组
–对每组记录采用直接插入排序方法进行排序
–随着增量逐渐减少,所分成的组包含的记录越
来越多,到增量的值减少到1时,整个数据合
成为一组,构成一组有序记录,则完成排序
希尔排序
void ShellSort( LineList R[] , int n ) { int i , j , gap = n / 2 ; LineList tmp ; while( gap > 0 ) { for( i = gap ; i < n ; i++ ) { tmp = R[i]; j = i – gap ; while( j>=0 && tmp.key < R[j.key]) R[j+gap] = R[j] ; j = j – gap ; R[j+gap] = tmp;j = j – gap ; } gap = gap / 2 ; } }
直到d = 1
希尔排序
void ShellSort( LineList R[] , int n ) { int i , j , gap = n / 2 ; LineList tmp ; while( gap > 0 ) { for( i = gap ; i < n ; i++ ) { tmp = R[i]; j = i – gap ; while( j>=0 && tmp.key < R[j.key]) R[j+gap] = R[j] ; j = j – gap ; R[j+gap] = tmp;j = j – gap ; } gap = gap / 2 ; } }
解:直接选择排序过程如下图所示 初始序列 第一次 75 [61] 87 87 68 68 92 92 88 88 61 75 77 77 96 96 80 80 72 72
第二次
第三次 第四次 第五次
[61
[61 [61 [61
68]
68 68 68
87
72] 72 72
92
92 75] 75
88
88 88 77]
选择排序
• 每趟从待排序的记录中选出关键字最小的记录,
顺序放在已排序的记录序列的最后,直到全部排 序完为止 –直接选择排序 –堆排序
直接选择排序
• 依次从无序表中选出Key值最小的记录,放在有序
表的最后 –第i次从1- n-i-1中选取min,和第i个记录进 行交换 –n个记录需通过n-1次选择,得到一个按关键字 从小到大排列的有序序列
4 5
25
49
无交换
结果
08 16 21
25*
25
49
0
1
2
3
4
5
各趟排序后的结果
直接选择排序
void SelectSort( LineList R[] , int n ) { int i , j , k ; LineList tmp ; for( i = 0 ; i < n -1 ; i ++ ) { k = i ; for( j = i + 1 ; j < n ; j++ ) if( R[j].key < R[k].key ) k = j ; tmp = R[i] ; R[i]=R[k] ; R[k]=tmp ; } }
93 87 32 57 34
93
建堆
• n个结点的完全二叉树,则最后一个结点是i=
n / 2个结点的子女
• 对以i个结点为根的子树进行比较,调整,使该子 树成为小根堆(大根堆) • 之后依次减少i的值,对以i为根的子树进行比较 ,调整,使之分别成为小根堆,直到i=1,到达根 结点
建堆
• 例 n=8,关键字为 {30,50,60,35,86,10,40,45} 调整成小根堆
21
25
49
25*
16
08
21
25
49
25*
16
08
21
16
08
25*
25
49
i = 1
d = 1
08
16
21
25*
25
49
开始时,d的值较大,子序列中的对象较少,排序速度较快
随着排序进展,d值逐渐变小,子序列中对象个数逐渐变多
d的取法有多种。最初 shell 提出取 d = n/2,d = d/2,
第六次
第七次 第八次 第九次
[61
[61 [61 [61
68
68 68 68 68
72
72 72 72 72
75
75 75 75 75
77
77 77 77 77
80]
80 80 80 80
88
87] 87 87 87
96
96 88] 88 88
92
92 96 92] 92
87
88 92 96 96]
i等于4时,排序的过程:
i = 4 j = 3
21
0 16 16 4
25 1
25* 2
49
3
08
5
16 temp
i = 4 j = 2
21 0
25
1
25*
2
16 49 3
49 08 4 5
16 temp
i = 4 j = 1
21 0 25 1
16
25* 2
25* 3
49 08 4 5
16 temp
68
72
77
75
72
80
77
87
80
88
87
92
88
96
92 96
最终排序结果:
61
68
72
75
77
80
87
88
92
96
练习
• 已知序列{503,17,512,908,170,897,
275,653,426,154,509,612,677,675} ,采用希尔排序法,对该序列进行升序排序时的 每一趟的结果
21 1
25 2
25* 3
49 08 4 5 08 temp
完成
08 0
16 1
21 2
25 3
25* 4
49 5
插入排序
void InsertSort( LineList R[] , int n ) { int i , j ; LineList tmp ; for( i = 1 ; i < n; i ++ ) { tmp = R[i] ; j = i – 1 ; while( j >= 0 && tmp.key < R[j].key ) { R[j + 1 ] = R[ j ] ;j-- ; } R[ j +1 ] = tmp ; } }
初始状态: i=1,k=1,j=2
49 2
08 0
25 1
25* 3
16 4
21 5
i k
j
08 0
25 1 i k
49
25* 3
16 4
21 5
2 j
49 25
08 0 i
25
1
49
25*
3
16 4
21 5
2
k
j
25* 25
08 0
25
49
25*
16 4 k j
21 5 16 < 25
第八章 排序
Review
• 二叉排序树查找
• 哈希查找
练习
• 试将二分查找改写成递归算法
排序
• 排序
–把一组无序的记录按其关键字的某种次序排序
起来,使具有一定的顺序,便于进行数据查找
• 两大基本操作
–比较关键字的大小
–从一个位置移动到另一个位置
排序的基本概念
• 排序为升序排序,并假定要排序的记录均存储在 线性表中,该线性表由关键字和数据域组成,其 类型定义如下
4
5
temp
比较操作,对有序表是从后 向前比较还是从前向后比较?
i = 2
21 0
25 1
49 2
25* 3
16
4
49 08 5 temp
i = 3
21 0
25 1
49 2
25* 3
16 4
08 5
25* temp
i = 4
21 0
25
1
25*
2
49 3
16 4
08 5
16 temp
i = 5
16 0
希尔排序
[例] 已知有6个待排序的记录,它们的关键字序列为{21,25,49, 25*,16,08},给出用希尔排序法进行排序的过程
初始状态:
21 0
25 1
49 2
25* 3
16 4
08 5
初始状态:
21 0 25 1 49 2 25* 3
16
4
08 5
i=1 d=3
21 25 49 25* 16 08
25* 3
16 4
08 5
[例] 已知有6个待排序的记录,它们的关键字序列为{21,25,49,
25*,16,08},给出用直接插入排序法进行排序的过程 各趟排序结果:
初始状态:
比较与移动 操作次数?
21 0
25 1
49 2
25* 3
16 4
08 5
i = 1
21
25
49
25*
16
08
25
0
1
2
3
直接选择排序
[例] 已知有6个待排序的记录,它们的关键字序列为{21,25,49,
25*,16,08},给出用直接选择排序法进行排序的过程
初始状态:
49 2
21 0
25
1
25*
3
16 4
08 5
最小者 08
i = 0
21 25 49 25* 16 08
交换21,08
0
1
2
3
4
5
i = 1
25
1 49
有序序列R[0..i-1] 无序序列 R[i..n-1]
R[i]
有序序列R[0..i]
无序序列 R[i+1..n-1]
插入排序
[例] 已知有6个待排序的记录,它们的关键字序列为{21,25,49, 25*,16,08},给出用直接插入排序法进行排序的过程
各趟排序结果: 初始状态:
21 0
25 1
49 2
最小者 16
交换25,16
08 0
25*
3 4
16
21 5
2
最小者 21
i = 2
08 0 16 1 49 25* 25 21 5
交换49,21
2
3
4
最小者25* 无交换 i = 3
08 0 1 16 2 21 3 25* 25 49
4
5
最小者25 i = 4
08 0 1 16 2 21 3
25*
#define MaxSize typedef int KeyType; typedef char ElemType[10]; typedef struct { KeyType key ; ElemType data; }LineList; •排序是稳定的 •排序是不稳定的
插入排序
插入排序过程:整个排序过程为n-1趟插入,即先将序列中第1个 记录看成是一个有序子序列,然后从第2个记录开始,逐个进行插 入,直至整个序列有序
希尔排序
[例8.2] 已知有10个待排序的记录,它们的关键字序列为{75,87, 68,92,88,61,77,96,80,72},给出用希尔排序法的过程
初始 第二次 75 61 87 75 68 68 92 77 88 72 61 80 77 87 96 88 80 92 72 96
第三次d=1: 61 75
– 关键字顺序有序: key1 key2 ... keyn – 关键字逆序有序:
堆排序
• 堆是一棵顺序存储的完全二叉树
–每个结点的关键字都不小于其孩子结点的关键
字称为大根堆
–若堆中每个结点的关键字都不大于其孩子结点
的关键字,称为小根堆
堆排序
• 大根堆&小根堆
14 32 87 29 48 34 57 14 29 48
1 i
2
3
08 0
25 1
49 2
25* 3
16 4
21 5
i
k
j
21 16
k 指示当前序列中最小者
08 0
16 1
49
25* 3
25 4
21 5
2
直接选择排序
[例8.3] 已知有10个待排序的记录,它们的关键字序列为{75,87,
68,92,88,61,77,96,80,72},给出直接选择排序法的过程
75
75 92 92
77
77 77 88
96
96 96 96
80
80 80 80
72
87 87 87
直接选择排序
[例8.3] 已知有10个待排序的记录,它们的关键字序列为{75,87,
68,92,88,61,77,96,80,72},给出直接选择排序法的过程
初始序列 75 87 68 92 88 61 77 96 80 72
1
2 50 30 60 86 10 40 50
调整的原则是将左右孩
子中值较小的上调为双亲, 双亲向下调整到结点位置
30
3 60 50 2 40
30
10 60 40
4
35
35
86
10
35
86
45
(a) iBaidu Nhomakorabea4不调整
45
45
(b) i=3需要调整
(c) i=2调整
30 35 2 50 86 10 35 2
i = 4 j = 0
21 0
16 25 1 25 2 25* 3
49 08 4 5
16 temp
i = 4的排序过程完成之后的关键字序列
21 16 0
21 1
25 2
25* 3
49 08 4 5
16 temp
插入排序
• 时间复杂度
– T(n)=O(n2)
• 空间复杂度
–S(n)=O(1)
希尔排序
30 10 35
1 30 10 86
60
40
45
86
60
40
45
60
40
45
(d) i=2调整 1 10 35 45 86 60 30 40
50
(e) i=2调整结果
最后结果 [61
break
Review
• 排序的基本概念
• 插入排序
– 直接插入排序
– 希尔排序
• 选择排序
– 直接选择排序
练习
• 对初始状态如下(长度为n)的各序列进行直接
插入排序时,至多需进行多少次关键字间的比较 (要求排序后的序列按关键字自小至大顺序有序 )
key1 key2 ... keyn
• 缩小增量排序方法
–把记录按下标的一定增量d分组
–对每组记录采用直接插入排序方法进行排序
–随着增量逐渐减少,所分成的组包含的记录越
来越多,到增量的值减少到1时,整个数据合
成为一组,构成一组有序记录,则完成排序
希尔排序
void ShellSort( LineList R[] , int n ) { int i , j , gap = n / 2 ; LineList tmp ; while( gap > 0 ) { for( i = gap ; i < n ; i++ ) { tmp = R[i]; j = i – gap ; while( j>=0 && tmp.key < R[j.key]) R[j+gap] = R[j] ; j = j – gap ; R[j+gap] = tmp;j = j – gap ; } gap = gap / 2 ; } }
直到d = 1
希尔排序
void ShellSort( LineList R[] , int n ) { int i , j , gap = n / 2 ; LineList tmp ; while( gap > 0 ) { for( i = gap ; i < n ; i++ ) { tmp = R[i]; j = i – gap ; while( j>=0 && tmp.key < R[j.key]) R[j+gap] = R[j] ; j = j – gap ; R[j+gap] = tmp;j = j – gap ; } gap = gap / 2 ; } }
解:直接选择排序过程如下图所示 初始序列 第一次 75 [61] 87 87 68 68 92 92 88 88 61 75 77 77 96 96 80 80 72 72
第二次
第三次 第四次 第五次
[61
[61 [61 [61
68]
68 68 68
87
72] 72 72
92
92 75] 75
88
88 88 77]
选择排序
• 每趟从待排序的记录中选出关键字最小的记录,
顺序放在已排序的记录序列的最后,直到全部排 序完为止 –直接选择排序 –堆排序
直接选择排序
• 依次从无序表中选出Key值最小的记录,放在有序
表的最后 –第i次从1- n-i-1中选取min,和第i个记录进 行交换 –n个记录需通过n-1次选择,得到一个按关键字 从小到大排列的有序序列
4 5
25
49
无交换
结果
08 16 21
25*
25
49
0
1
2
3
4
5
各趟排序后的结果
直接选择排序
void SelectSort( LineList R[] , int n ) { int i , j , k ; LineList tmp ; for( i = 0 ; i < n -1 ; i ++ ) { k = i ; for( j = i + 1 ; j < n ; j++ ) if( R[j].key < R[k].key ) k = j ; tmp = R[i] ; R[i]=R[k] ; R[k]=tmp ; } }
93 87 32 57 34
93
建堆
• n个结点的完全二叉树,则最后一个结点是i=
n / 2个结点的子女
• 对以i个结点为根的子树进行比较,调整,使该子 树成为小根堆(大根堆) • 之后依次减少i的值,对以i为根的子树进行比较 ,调整,使之分别成为小根堆,直到i=1,到达根 结点
建堆
• 例 n=8,关键字为 {30,50,60,35,86,10,40,45} 调整成小根堆
21
25
49
25*
16
08
21
25
49
25*
16
08
21
16
08
25*
25
49
i = 1
d = 1
08
16
21
25*
25
49
开始时,d的值较大,子序列中的对象较少,排序速度较快
随着排序进展,d值逐渐变小,子序列中对象个数逐渐变多
d的取法有多种。最初 shell 提出取 d = n/2,d = d/2,
第六次
第七次 第八次 第九次
[61
[61 [61 [61
68
68 68 68 68
72
72 72 72 72
75
75 75 75 75
77
77 77 77 77
80]
80 80 80 80
88
87] 87 87 87
96
96 88] 88 88
92
92 96 92] 92
87
88 92 96 96]
i等于4时,排序的过程:
i = 4 j = 3
21
0 16 16 4
25 1
25* 2
49
3
08
5
16 temp
i = 4 j = 2
21 0
25
1
25*
2
16 49 3
49 08 4 5
16 temp
i = 4 j = 1
21 0 25 1
16
25* 2
25* 3
49 08 4 5
16 temp