8.1 概概 8.2 概概概概 8.3 概概概概概概概概概概概概概 8.4 概概概概概概概概 8.5 概概概概概概概概概 8.6 概概概概 8.7 概概概概 概概概 概概
Mar 19, 2016
8.1 概述 8.2 合并排序 8.3 用比较法进行排序的时间下界 8.4 选择排序和堆排序8.5 插入排序和希尔排序8.6 快速排序8.7 基数排序
第八章 排序
概述• 排序:内部排序:全部记录都可以同时调入内存进行的排序。 外部排序:文件中的记录太大,无法全部将其同时调入内存进行的排序。• 定义:设有记录序列: { R1 、 R2 ……….. Rn } 其相应的关键字序列为: { K1 、 K2 ……….. Kn }; 若存在一种确定的关系: Kx <= Ky <= … <= Kz
则将记录序列 { R1 、 R2 ……….. Rn } 排成按该关键字有序的序列: { Rx 、 Ry ……….. Rz } 的操作,称之为排序。
关系是任意的,如通常经常使用的小于、大于等关系或任意的关系。
• 稳定与不稳定:若记录序列中的任意两个记录 Rx 、 Ry 的关键字 Kx = Ky ;如果在 排序之前和排序之后,它们的相对位置保持不变,则这种排序方法 是稳定的,否则是不稳定的。
3
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
4
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7
5
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7
6
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13
7
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13
8
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13 49
9
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13 49
10
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13 49 65
11
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13 49 65
12
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13 49 65 76
13
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13 49 65 76
14
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13 49 65 76 80
15
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13 49 65 76 80
16
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
0 1 2 3 4
4913 65 97
767 80
A
B
0 1 2 3 4 5 6 7
C
i
j
k
7 13 49 65 76 80 97
17
合并排序• Merging sort: 思想来源于合并两个已排序的顺序表。•e.g: 将下面两个已排序的顺序表合并成一个已排序表。顺序比较两者的相应元素,小者移入另一表中,反复如此,直至其中任一表都移入另一表为止。
1 2 3 4 54913 65 97A 7
6 7 8 9767 80B 3
1 2 3 4 5 6 7 8 9
C 4913 65 97767 803 7
• 实现很简单, A 、 B 、 C 三表分别设置指针 p 、 q 、 k 初值为 1,6,1 ,比较 p 、 q 指针指向 的结点,将小者放入 C 表 , 相应指针加 1 。如 A 、 B 表中一张表为空,则将另一张表的结点 全部移入 C 表。• 比较次数和移动次数和两张表的结点总数成正比。
18
合并排序• 合并二张有序表:
template < class EType>
void Merge( EType a[ ], EType aa[ ], int n, int low, int up, int m ) {
int p=low, q=low+m, r;
// 合并 a[low] 至 a[low+m-1] 和 a[low+m] 至 a[up] 。
for (r=1; r<=n; r++) aa[r] = a[r];
r = low; // 数组 a 的初始指针。while ( p < low + m && q < up + 1 ) {
while ( p < low + m && aa[p] <= aa[q] ) a[r++] = aa[p++];
if ( p < low + m ) while ( q < up + 1 && aa[q] < aa[p] ) a[r++] = aa[q++];
}
if ( p = = low + m ) for ( ; q <=up; q++ ) a[r++] = aa[q];
if ( q = = up + 1 ) for ( ; p <=low + m -1; p++ ) a[r++] = aa[p];
}
19
合并排序 合并排序的基本思想:
1 2 3 4 5 6 7
9 7 8 3 52 4 16
7 9 3 8 4 52 16
3 7 8 9 4 16 52
3 4 7 8 9 16 52
子表长 m : 1
子表长 m : 2
子表长 m : 4
子表长 m : 8
分析: 1 、被合并的二张表中第一张表的起始地址设为 low, 那么第二张表的起始地址应为 low + m, 而 终止地 址通常为 up = low +2m -1 。如果 up>n( 表长 ) ,那么 up = n ;即取二者小者作第二张表的终止地址。 2 、当 up + m >= n 时,意味着下一次被合并的两张表中的第一张表的末地址大于等于最大下标 n ,这说 明被合并的第二张表不存在,意味着本趟结束。要过渡到下一趟合并; m 应增大一倍。 3 、 当子表长 m >= n 时,合并排序结束。
i j
20
合并排序• 合并排序法:
template < class EType> void MergeSort( EType a[ ], int n ) { // 待排序的表为数组 a, a[0] 不用,待排序的数组元素为 a[1], a[2], ……, a[n] 。 int low=1; // 被合并的二个表中的第一个表的首地址。 int up; // 被合并的二个表中的第二个表的末地址。 int m=1; // 被合并的二个表中的第一个表的表长。初始时为 1 。 EType * aa; aa = new EType[n+1] ; // 用于合并的辅助数组, aa[0] 仍然不用。 while ( m < n ) {
up = min(low + 2 * m –1, n);Merge(a, aa, n, low, up,m); // 将 a[low] 至 a[low+m-1] 和 a[low+m] 至 a[up] 进行合并。if ( up+m < n ) low = up + 1; // up+m ≥ n 意味着被合并的另一张表不存在。else { m *= 2; low = 1; } // 过渡到下一次合并,子表长度增大一倍。
} delete [ ]aa;}
21
合并排序· 时间复杂性分析:
• 合并的过渡趟数: logn
• 每趟进行时,调用 merge 过程 n / ( 2 * len ) 次, 每次比较和数据移动都是 (2 * len )
次,因此每趟代价为 O( n ) 。• 总的代价为 T(n) = O ( nlogn )
• 在递归的情况下:可通过下式求出时间复杂性的级别。2 for n=2
T(n) =
T( n/2 ) + T( n/2 ) + n else
解上述的递归式,可知时间复杂性为 T(n) = O ( nlogn ) 。当 n 是 2 的整数幂时简化为T(n) = O ( nlogn )
22
用比较法进行排序的时间下界• 判定树模型:如下图所示,说明对 a,b,c 进行分类的一颗判定树。当判断条件成立时, 转向左,否则转向右。
• 比较法分类的下界: Ω ( n logn )
a < b
a < b < c a < c b < c
a < cb < c
a < c < b c < a < b c < b < a
b < a < c
b < c < a
no yes
yes
yes
yes
yes
no
no
no
no
• 注意:树高代表比较的代价。因此只要知道了树高和结点数 n 的关系,就可以求出 用比较法进行排序时的时间代价。另外, n 个结点的分类序列,其叶子结点 共有 n! 片。
23
用比较法进行排序的时间下界• 定理:高为 H 的二叉树,最多具有 2(H-1) 片叶子。 证明: H = 1 时,最多 1 片叶子,定理成立。 H = 2 时,最多 2 片叶子,定理成立。
设 H = n-1 时公式成立。则当 H = n 时, 根结点有具有叶子结点最多的高为 n-1 的左 右子树;叶子结点总数为:
2(n-2)×2 = 2(n-1) 公式成立。 推论:具有 N 片叶子的二叉树,高最少为 logN + 1。 或比较次数最少为 logN 。
• 比较法分类的下界: Ω ( n logn )
高为 n-1
具有 2(n-2)
片叶子高为 n-1
具有 2(n-2)
片叶子
H = 1
H = 2
H = n
• 定理:把 n 个不同的结点进行分类的任一判定树的高 度至少为 log(n!) + 1 。 证明: n 个元素的排序序列的总数共有 n! 种,因此在 相应的判定树上至少有 n! 片叶子。根据上述定 理,可证。
推论:用比较的方法对 n 个元素的进行分类。在最坏 情况下至少需要 cnlogn 次比较, 或 Ω(nlogn)
24
用比较法进行排序的时间下界• 比较法分类的下界: Ω ( n logn )
X 轴
Y 轴 求 y=logx 的积分的示意图
2
1
1 2 3 4
y=logn
1
1 n-1 n
j=1
n n
1
log n! = Σ logj ≥ logx dx 不难求出上述积分的值为: nlogn-nloge+loge ≥ nlogn-nloge 。注意到: loge = 1.44 。
25
简单选择排序• 执行过程:1 2
25 49 25 16 83 4 5
21
25 49 25 16 821
25 49 258 16 21
25 49 25 16 218
rp
rp
16 49 25 25 218
16 49 25 25 218rp
16 21 25 25 498
16 21 25 25 498r p
16 21 25 25 498
16 21 25 25 498r p
16 21 25 25 498
6
26
template < class EType>
void SelectSort( EType a[ ], int n ) {
int p, q; // p 、 q 为数组 a 的指针。 a[1], a[2 ], …,a[n] 为待排序序列。 a[0] 不用。
int r; // 暂时保存具有最小键值的结点地址。 for ( p = 1; p < n; p++) {
r = p;
for ( q = p+1; q <= n; q++) if ( a[q ] < a[r] ) r = q; // 记住本趟挑选的最小结点的地址。 if ( r !=p ) { a[0] = a[p]; a[p] = a[r]; a[r] = a[0]; } // 若最小结点不是 a[p], 则交换。 }
}
简单选择排序
27
• 有 n 个元素的数组的比较次数 (n-1) + (n-2) + . . . + 2 + 1 = O(n2)
交换次数 3 (n-1) 因循环执行 (n-1) 次,最坏情况下每次都交换。 总的时间复杂性代价为: O(n2)
简单选择排序
树型选择排序• 改进:简单选择排序没有利用上次选择的结果,是造成速度慢的主要原因。如果能够 加以改进,将会提高排序的速度。
38 1376 276549 97 49
38 65 13 27
1338
13 选出最小者 13
树型选择排序• 改进:简单选择排序没有利用上次选择的结果,是造成速度慢的重要原因。如果,能 够加以改进,将会提高排序的速度。由于有更好的排序方法,所以本方法用的 很少。
38 1376 276549 97 49
38 65 13 27
1338
13
选出次最小者,应利用上次比较的结果。从被 13 打败的结点 27 、 76 、 38 中加以确定。
堆排序• 堆的定义: n 个元素的序列 { k1 , k2 ,…… , kn } ,当且仅当满足以下关系时,称 之为堆:
ki <= k2i ki >= k2i
ki <= k2i+1 ki >= k2i+1
( i = 1,2, …… , n/2 )
且分别称之为最小化堆和最大化堆。 e.g: 序列 { 96,83,27,11,9} 最大化堆
{ 12 , 36 , 24 , 85 , 47 , 30 , 53 , 91} 最小化堆
或
96
911
83 27 2 3
1
4 5
12
4785
91
36 24
53306
2
7
3
1
8
4 5
31
70
60
40 30
12
8
最大堆的最大元素
10
32
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
70
60
12
40
30
8
10
70
1
60
240 4
30
5
12
3
8
6
root
10
7
数组表示的堆
33
建堆
30
1 60
240 4
70
5
8
3 12
6
10
7
root[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
30
60
8
40
70
12
10 叶子结点,符合堆的的定义。
不合堆的定义,需调整。即建立小堆。建立的第一个小堆的堆顶的下标为 n/2
数组 h
34
建堆
30
1
60
240 4
70
5
8
3 12
6
10
7
root[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
30
60
12
40
70
8
10 叶子结点,符合堆的的定义。
不合堆的定义,需调整。即建立大堆。建立的第一个小堆的堆顶的下标为 n/2
数组 h
35
建堆
30
1
60
240 4
70
5
12
3 8
6
10
7
root[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
30
60
12
40
70
8
10
不合堆的定义,需调整。建立以 h[ 2 ] 为堆顶的正确的最大化小堆。
数组 h
36
建堆
30
1
70
240 4
60
5
12
3 8
6
10
7
root[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
30
70
12
40
60
8
10
不合堆的定义,需调整。建立以 h[ 2 ] 为堆顶的正确的最大化小堆。
数组 h
37
建堆
30
1
70
240 4
60
5
12
3 8
6
10
7
root[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
30
70
12
40
60
8
10
不合堆的定义,需调整。建立以 h[ 1 ] 为堆顶的正确的最大化堆。数组 h
38
建堆
70
1
30
240 4
60
5
12
3 8
6
10
7
root[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
70
30
12
40
60
8
10
不合堆的定义,需调整。建立以 h[ 1 ] 为堆顶的正确的最大化堆。数组 h
39
建堆
70
1
60
240 4
30
5
12
3 8
6
10
7
root[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
70
60
12
40
30
8
10
不合堆的定义,需调整。建立以 h[ 1 ] 为堆顶的正确的最大化堆。数组 h
40
建堆时间复杂性的分析:
• 时间耗费的代价:建堆的时间耗费 + 排序的时间耗费• 建堆的时间耗费:设树根处于第 1 层,该堆共有 h 层。建堆从第 h-1 层开始进行。只 要知道了每一层的结点数(建的小堆的个数),和每建一个小堆所 需的比较次数,就可以求得建堆的时间耗费。• 建的小堆的性质:第 i 层上小堆的个数 = 第 i 层上结点的个数 = 最多 2i-1
第 i 层上小堆的高度 = h-i+1
建第 i 层上每个小堆最多所需的比较次数 = 2×( h-i)次• 建堆的时间耗费: T(n) <= ∑ 2i-1×(h-i)×2 = ∑ 2i×(h-i) = ∑ 2h-j×j
= 2h ∑ j/2j
注意:即使是 高度为 h 的完全的二叉树,满足以下式子: 2h - 1 <= n <= 2h-1 故: 2h <= 2n
又: ∑ j/2j < 2
所以:建堆的时间复杂性为 4n=O(n) 级
i=h-1i=h-1 j=1
h-1 1 1
j=1
h-1
j=1
∞
10
3040
60 12
7086
2 3
1
74 5
1
2
3=h
41
建堆• 建立最大化堆
template < class EType> void MaxHeap<EType> :: SiftDown( int j ) { // heap[1], …,heap[n] 为存储堆的数组。 heap[0] 不用。 int MaxSon; // 用于记录大儿子的下标地址。 heap[0] = heap[j]; for ( ; j*2 <= CurrentSize; j = MaxSon) { MaxSon = 2 * j; if ( MaxSon != CurrentSize && heap [MaxSon+1 ] > heap [MaxSon ] ) MaxSon++; if ( heap[MaxSon] > heap[0] ) heap[j] = heap[MaxSon]; else break;}heap [j ] = heap[0];} template < class EType> void MaxHeap<EType> :: BuildHeap( ) { for ( int j= CurrentSize/2; j >0; j-- ) SiftDown(j);}
30 1
70 2
40 4
60 5
12 3
8 6
10 7
j
42
堆排序
70
60
12
40
30
8
10
values
70
1
60
240 4
30
5
12
3
8
6
root
10
7
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
43
堆排序values
10
60
12
40
30
8
70 不需再考虑!
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
10
1
60
240 4
30
5
12
3
8
6
root
70
7
44
堆排序values
60
40
12
10
30
8
70
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
60
1
40
210 4
30
5
12
3
8
6
root
70
7
45
堆排序
60
40
12
10
30
8
70
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
60
1
40
210 4
30
5
12
3
8
6
root
70
7
46
堆排序values
8
40
12
10
30
60
70 不需再考虑!
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
8
1
40
210 4
30
5
12
3
60
6
root
70
7
47
values
40
30
12
10
8
60
70
堆排序[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
40
1
30
210 4
8
5
12
3
60
6
root
70
7
48
values
40
30
12
10
8
60
70
堆排序[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
40
1
30
210 4
8
5
12
3
60
6
root
70
7
49
values
堆排序
8
30
12
10
40
60
70 不需再考虑!
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
8
1
30
210 4
40
5
12
3
60
6
root
70
7
50
values
30
10
12
8
40
60
70
堆排序[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
30
1
10
2 8 4
40
5
12
3
60
6
root
70
7
51
values
30
10
12
8
40
60
70
堆排序[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
30
1
10
2 8 4
40
5
12
3
60
6
root
70
7
52
values
堆排序
8
10
12
30
40
60
70 不需再考虑!
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
8
1
10
230 4
40
5
12
3
60
6
root
70
7
53
values
12
10
8
30
40
60
70
堆排序[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
12
1
10
230 4
40
5
8
3
60
6
root
70
7
54
values
12
10
8
30
40
60
70
堆排序[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
12
1
10
230 4
40
5
8
3
60
6
root
70
7
55
values
堆排序
不需再考虑!
8
10
12
30
40
60
70
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
8
1
10
230 4
40
5
12
3
60
6
root
70
7
56
values
10
8
12
30
40
60
70
堆排序[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
10
1
8
230 4
40
5
12
3
60
6
root
70
7
57
values
10
8
12
30
40
60
70
堆排序[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
30
5
10
7
10
1
8
230 4
40
5
12
3
60
6
root
70
7
58
数组 h
堆排序
8
10
12
30
40
60
70 不需再考虑!
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]
[ 6 ]
[ 7 ]
10
230 4
40
5
12
3
60
6
root
70
7
8
1
59
堆排序template < class EType> void SiftDown( EType a[ ], int j, int Size ) { // a[1], …,a[Size] 为存储堆的数组。 a[0] 不用。注意:本函数用于排序。 Size 为堆的最 // 大下标, j 为当前要建立的最大化堆的堆顶结点的地址。 int MaxSon; // 用于记录大儿子的下标地址。 a[0] = a[j]; for ( ; j*2 <= Size; j = MaxSon) {
MaxSon = 2 * j;if ( MaxSon != Size && a[MaxSon+1 ] > a[MaxSon ] ) MaxSon++;if ( a[MaxSon] > a[0] ) a[j] = a[MaxSon]; else break;
} a[j] = a[0];}template < class EType> void MaxHeapSort( EType a[ ], int Size ) { for ( int j = Size/2; j > 0; j- - ) // a[1], a[2 ], …,a[Size] 为待排序的数组。 a[0] 不用。 SiftDown(a, j, Size); // 将 a[1], a[2 ], …,a[Size]建成最大化堆。 for ( int k = Size; k > 1; k- - ) { Swap( a[1], a[k]); // 交换 a[1] 和 a[k ], 最大元素放在 a[k] 。 SiftDown(a, 1, k-1); // 将 a[1], a[2 ], …,a[k-1] 调整为最大化堆。 }}
60
堆排序• 时间复杂性的分析:建堆的时间耗费 + 排序的时间耗费• 排序的时间耗费:
结点个数 高度 比较次数最多 j=2 log2 + 1 <= 2× log2
j=3 log3 + 1 <= 2× log3
j=4 log4 + 1 <= 2× log4
j=n-1 log(n-1) + 1 <= 2× log(n-1)
所以: T(n) <= 2( log2 + log3 + …….. + log(n-1) ) = 2log(n-1)!
T(n) = O ( nlogn )
时间复杂性的分析:
10
3040
60 12
7086
2 3
1
74 5
1
2
3=h
61
堆排序• 堆排序的时间代价分析推导: O ( n logn )
X 轴
Y 轴 求 y=logx 的积分的示意图
2
1
2 3 4
y=logx
1
1 n-1 n
j=2
n-1n
1
log(n-1)! = Σ logj ≤ logx dx 不难求出上述积分的值为: nlogn-nloge+loge ≥ nlogn-nloge 。注意到: loge = 1.44 。
62
堆排序• 最大化堆(最小化堆亦可)用以表示优先队列 常用的基本操作: 1 、 Inset :将具有给定优先数的新结点插入优先队列 ,代价 O(logn) 若分二步: 1 、先找出新结点的插入位置,代价为 O(logn) 2 、通过移动,将新结点插入,代价 O(logn) 总代价仍未改进,仍是 O(logn) 2 、 DeleteMax :删除具有最大优先数的结点, 代价 O(logn) 3 、 Delete :删除任一结点, 代价 O(logn) 4 、 IncreasePriority(DecreasePriority): 增大(或减少)结点的优先数 代价 O(logn) 5 、 Merge : 合并二个优先队列为一个 无法在 O(logn) 内做到 改进:使用最左树
96
5951
83 77 2 3
1
4 5
3931
48 47 6 7
8 9
91114 15
494110 11
292112 13
79316 17
1层2层3层
4层5层
63
最左树•最左树:是一棵二叉树。结点的 s 值。设二叉树的外部结点 ( 空的儿子结点 ) 的 s 值为 0 ,其他 每个结点的 s 值为左、右儿子结点的 s 值的小者加 1 。如果每个结点的左儿子的 s
值不小于右儿子的 s 值,而且父结点之值大于等于 ( 或小于等于 )儿子结点 ( 如果存在的 话 ) 之值的话,那末这棵二叉树被称之为最左树。
3
1
111 1
2 2 11
3 2
90
153
2
20 10
75
121
10
13
18
2
3
1 1 11
2
25
68
16 12
24
64
最左树• 定理:设 x 是最左树的内部结点,那么
1. 以 x 为根的子树的结点的数目至少为 2S(x) – 1 。2. 如果子树 x 有 m 个结点, S(x) 至多为 log2(m+1) 。3. 通过最右路径,从 x 到达外部结点的路径长度为 S(x) 。
•证明: 1. 设 x 所在结点的层次为 0 ,儿子结点的层次为 1 ,以下依次加 1 ,……。 则根据定义, x 的后代中,如果 s 值为 1 ,则该后代的儿子中必有一个为 空。换句话说,由 x 到达最“近”的且 s 值为 1 的后代之前, x 及其后代的 儿子数 2 都为两个。因此,直至第 S(x ) - 1 层没有外部结点,第 S(x ) 层 才有外部结点。所以,结点总数至少为: 1+2+……+2 S(x ) - 1 = 2S(x) -1 个结 点。注意,从第 S(x )层起,开始出现外部结点,同时还可能有内部结 点。 2.由 1. 可知,结点数至少为 2s(x) -1 = m 时, x 的 s 值为 S(x) 。 S(x) 的值至多 为 log(m +1) 。 3. 根据定义,每个内部结点的右儿子的 s 值小于等于左儿子的 s 值,故 通过最右路径,从 x 到达外部结点的路径长度为 S(x) 。很明显,任意结 点的 s 值为本结点到它的度为 0 或 1 的子孙结点的最短路径长度加 1 。
2
3
1 1 11
2
25
68
16 12
24
3
1
111 1
2 2 11
3 2
90
153
2
20 10
75
121
10
13
18
65
最左树
2
3
1 1 11
2
25
68
16 12
24
3
1
111 1
2 2 11
3 2
90
153
2
20 10
75
121
10
13
18
• 合并最左树示例
11
210
7511
212
24 7 2
• 合并中涉及到的最左树
66
最左树• 合并过程
1
17
21
17
21
210
5
a、 b、1
17
2
c、
1
212
4
1
210
5
1
17
2
1
212
4
1
210
5
1
17
2
2
3
2 2
25
68
16
1
212
4
1
210
5
1
17
2
d、
f、
e、
67
最左树• 最后得到的最左树
1
4
1
111 1
2 2
3
90
153
2
20
121
10
8
302
3
1 1
25
68
16
1
212
4
1
110
5
1
17
2
g、
68
a[0] 用作哨兵。共执行 5 遍操作。每遍操作:先将元素复制内容放入 a[0] ,再将本元素同已排序的序列,从尾开始进行比较。在已排序的序列中寻找自己的位置,进行插入。或者寻找不到,则一直进行到哨兵为止。意味着本元素最小,应该放在 a[1] 。每一遍,排序的序列将增加一个元素。如果序列中有 n 个元素,那么最多进行 n 遍即可。
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
69
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
3624
j
70
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
3624
j
71
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
3624
j
24
72
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
3610
j
24
73
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
3610
j
24
74
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
3610
j
24
75
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
3610
j
2410
76
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
36246
j
10
77
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
36246
j
10
78
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
36246
j
10
79
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6 12
3 4 5
36246
j
106
80
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6
3 4 5
362412
j
106
12
81
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6
3 4 5
362412
j
106
12
82
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6
3 4 5
362412
j
106
12
83
直接插入排序e.g: 36 、 24 、 10 、 6 、 12 存放在 r 数组的下标为 1 至 5 的元素之中,用直接插入法将 其排序。结果仍保存在下标为 1 至 5 的元素之中。
0 1 236 24 10 6
3 4 5
362412
j
106
12
12
84
直接插入排序0 1 2
10 20 30 403 4 5
10204050
50
30
分析 : 移动、比较次数可作为衡量时间复杂性的标准 正序时:比较次数 ∑ 1 = n-1
i=2 移动次数 2( n-1)
n
逆序时:比较次数 ∑ i = (n+2)(n-1)/2 i=2
移动次数 ∑ (i+1) = (n+4)(n-1)/2 i=2
n
n
85
直接插入排序0 1 2
36 24 10 63 4 5
362412 106
12
12
template < class EType> void SimpleInsertSort( EType a[ ], int Size ) { int j, q; // a[1], a[2 ], …,a[Size] 为待排序的数组。 a[0] 为哨兵单元。 EType temp=a[0]; for ( j = 2; j <= Size; j++ ) { a[0] = a[j]; for ( q = j; a[0] < a[q-1]; q- -) a[q] =a[q-1]; a[q] = a[0]; } a[0] = temp; // 恢复 a[0] 原来的值,用于快速排序法中。}
程序实现 :
3624106 12j
86
折半插入排序
template < class EType> void BinaryInsertSort( EType a[ ], int Size ) { int low , high; // a[1], a[2 ], …,a[Size] 为待排序的数组。 a[0] 用哨兵单元。 int j, k, mid; for ( j = 2; j <= Size; j++ ) { a[0]=a[j]; low = 1; high = j -1; // 在 a[ 1 ], …, a[ j-1] 范围内寻找 a[j] 的插入位置。 while ( low <= high ) { mid = ( low + high ) / 2; // 中点下标。 if ( a[0] < a[mid] ) high = mid – 1; // 小于中点,查找左段。 else low = mid +1; // 大于等于中点,查找右段。 } for ( k = j-1; k >= low; - - k ) a[ k+1] = a[ k ]; a[ low ] = a[0]; } }
程序实现 :
36 24 10 6
12362412
high
106
12
12
方法:在已排序的序列中查找下一元素的插入位置,将该元素插入进去,形成更长的排序序列。如: 12 的插入位置为下标 3 。 减少了比较次数,未降低时间复杂性。
jlow
87
折半插入排序36 24 10 6
12362412
high
106
12
12
方法:在已排序的序列中查找下一元素的插入位置,将该元素插入进去,形成更长的排序序列。如: 12 的插入位置为下标 3 。 减少了比较次数,未降低时间复杂性。
jlow
12362412
high
106 12
jlow
12362412
high
106 12
jlow
m
m
注意 : low 指针总是指向新关键字的下标地址 ,如: 12 的插入位置为 3 。
88
希尔 (shell) 排序e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 4 用 shell 排序的方法进行排序 1 、选定步长序列,如选为 8 、 4 、 2 、 1
2 、针对步长序列进行排序,从最大的步长开始,逐步减少步长,最后一次选择的 的步长肯定为 1 。
步长 8 : 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 4
步长 8 : 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 4
89
希尔排序e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 4 用 shell 排序的方法进行排序 1 、选定步长序列,如选为 8 、 4 、 2 、 1
2 、针对步长序列进行排序,从最大的步长开始,逐步减少步长,最后一次选择的 的步长肯定为 1 。
步长 8 : 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 4
步长 8 : 49 、 4 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 38
步长为 8 的序列的排序结束。
90
希尔排序e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 4 用 shell 排序的方法进行排序 1 、选定步长序列,如选为 8 、 4 、 2 、 1
2 、针对步长序列进行排序,从最大的步长开始,逐步减少步长,最后一次选择的 的步长肯定为 1 。
步长 4 : 49 、 4 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 38
步长 4 : 49 、 4 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 38
91
希尔排序e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 4 用 shell 排序的方法进行排序 1 、选定步长序列,如选为 8 、 4 、 2 、 1
2 、针对步长序列进行排序,从最大的步长开始,逐步减少步长,最后一次选择的 的步长肯定为 1 。
步长 4 : 49 、 4 、 27 、 49 、 55 、 13 、 65 、 97 、 76 、 38
步长 4 : 49 、 4 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 38
92
希尔排序e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 4 用 shell 排序的方法进行排序 1 、选定步长序列,如选为 8 、 4 、 2 、 1
2 、针对步长序列进行排序,从最大的步长开始,逐步减少步长,最后一次选择的 的步长肯定为 1 。
步长 2 : 49 、 4 、 27 、 49 、 55 、 13 、 65 、 97 、 76 、 38
步长 2 : 49 、 4 、 27 、 49 、 55 、 13 、 65 、 97 、 76 、 38
93
希尔排序e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 4 用 shell 排序的方法进行排序 1 、选定步长序列,如选为 8 、 4 、 2 、 1
2 、针对步长序列进行排序,从最大的步长开始,逐步减少步长,最后一次选择的 的步长肯定为 1 。
步长 2 : 27 、 4 、 49 、 13 、 55 、 38 、 65 、 49 、 76 、 97
步长 2 : 49 、 4 、 27 、 49 、 55 、 13 、 65 、 97 、 76 、 38
94
希尔排序e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 、 55 、 4 用 shell 排序的方法进行排序 1 、选定步长序列,如选为 8 、 4 、 2 、 1
2 、针对步长序列进行排序,从最大的步长开始,逐步减少步长,最后一次选择的 的步长肯定为 1 。
步长 1 : 27 、 4 、 49 、 13 、 55 、 38 、 65 、 49 、 76 、 97
步长 1 : 4 、 13 、 27 、 38 、 49 、 49 、 55 、 65 、 76 、 97最后的排序结果分析 : shell 排序的分析非常困难,原因是何种步长序列最优难以断定。通常认为时间 复杂性为: O( n3/2) .
较好的步长序列:…… 121 、 40 、 13 、 4 、 1; 可由递推公式 Si= 3Si-1 + 1 产生。程序实现 : 类似于直接插入排序的程序。 注意修改步长。
95
快速排序• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。
0 1 2 3 4 5 6 7 8
49
38
38 65
65 97
97 76
76 13
13 27
27 49
49
highlow
49
界点
96
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
65 97
97 76
76 13
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
97
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
65 97
97 76
76 13
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
98
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
65 97
97 76
76 13
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
99
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
65 97
97 76
76 13
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
100
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
6597
97 76
76 13
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
101
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
6597
97 76
76 13
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
102
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
6597
97 76
7613
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
103
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
6597
97 76
7613
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
104
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
6597
97 76
7613
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
105
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
6597
97 76
7613
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
106
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
6597
97 76
7613
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
107
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
49
38
38 65
6597
97 76
7613
13 27
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
108
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
27
38
38 13
6597
49 76
7613
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
109
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
27
38
38 13
6597
49 76
7613
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
110
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
27
38
38 13
6597
49 76
7613
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
111
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
27
38
38 13
6597
49 76
7613
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
112
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
27
38
38 13
6597
49 76
7613
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
113
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
6597
49 76
76 13
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
114
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
6597
49 76
76 13
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
115
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
6597
49 76
76 13
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
116
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
65 97
49 76
76 13
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
117
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
65 97
49 76
76 13
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
118
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
65 97
49 76
76 13
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
119
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
65 97
49 76
76 13
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
120
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
65 97
49 76
7613
97 65
27 49
49
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
121
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
65 97
49 49
7613
65 76
2749
97
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
122
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
65 97
49 49
7613
65 76
2749
97
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
123
快速排序
e.g: 将序列 49 、 38 、 65 、 97 、 76 、 13 、 27 、 49 用 快速排序的方法进行排序。0 1 2 3 4 5 6 7 8
13
38
27 38
65 97
49 49
7613
65 76
27 49
97
highlow
49
界点
• 原理 : 若序列中有 n 个元素,任选一个关键字作为界点,将序列分成两部分。其 中左半部分的结点的关键字小于等于界点,右半部分的结点的关键字大于等于界 点。然后,对左右两部分分别进行类似的处理,直至排好序为止。
124
快速排序• 时间复杂性分析 : 考虑平均情况下的时间复杂性。设元素个数为 n ,则所有的排列形式 共有 n! 种。每一个元素都可以充当界点,所以充当界点的情况共计有 n 种。故平均时 间复杂性 T 应为:
T (n) = cn + ∑ [ T (k-1) + T (n-k) ] / n
设: T (0) = T (1) = b
T (n) = cn + 2∑ T (i) / n
nT (n) = cn2 + 2∑ T (i)
(n-1)T (n-1) = c(n-1)2 + 2∑ T (i)
上二式相减的结果为: nT (n) - (n-1)T (n-1) = c(2n-1) + 2T (n-1)
nT (n) = c(2n-1) + (n+1) T (n-1)
K=1
n
n-1
i=0n-1
i=0
i=0n-2
125
快速排序所以: T (n) / ( n + 1) = c(2n-1) /( n(n+1)) + T (n-1) / n
T (n) / ( n + 1) < = 2c/(n+1) + T (n-1) / n
T (n-1) / n < = 2c/n + T (n-2) / (n-1)
T (2) / 3 < = 2c/3 + T (1) / 2
于是: T (n) < = 2c(n+1)ln(n+1) + b(n+1) / 2
注意:这里使用了一个公式:∑1/k < = ∫ 1/x dx < ln( n + 1)
结论:快速排序在平均情况下的时间复杂性为 O(nlogn) 级或阶的,通常认为是在平 均情况下最佳的排序方法。
K=3
n+1
2 n+1
2
1
1 2 3 4
y=1/x
n n+1
126
快速排序• 快速排序使用的额外空间 考虑最坏情况,栈的空间为 O(n) 。可以考虑降低到 O(logn) 。
观察以下情况,每次优先处理短的那一段。则空间的使用可以降低到最小。
均匀分段 非均匀分段
在均匀分段的情况下,系统每一次将其中一段的上界、下界的下标压入堆栈,而去处 理另外一段。当处理的一段的元素个数为 1 时,将不必保存另一段的上下界
。这样堆 栈的总的层数为 logn 。在非均匀分段时,由于优先处理短的一段,下降到只有 1 个元 素的段的速度将更快,所以不会超过 logn 。
127
快速排序• 快速排序的不足和克服方法
1 、在序列差不多排好序时,采用直接插入排序、起泡排序等排序方法。序列的 个数通常取为 10 左右。本书使用了插入排序法用于差不多排好序时。 2 、将递归改成非递归算法。避免进出栈、恢复断点等工作。速度加快。
128
快速排序• 快速排序的不足和克服方法 3 、最坏情况下 时间复杂性为 O(n2) 级。如:在序列已是正序的情况下。
10 、 20 、 30 、 40 、 50 、 60 、 70 、 80 界点 10 ,共进行 7 次比较。
10 、 20 、 30 、 40 、 50 、 60 、 70 、 80 界点 20 ,共进行 6 次比较。
10 、 20 、 30 、 40 、 50 、 60 、 70 、 80 界点 60 ,共进行 2 次比较。
10 、 20 、 30 、 40 、 50 、 60 、 70 、 80 界点 70 ,共进行 1 次比较。 在最坏情况下:总的比较次数为: (n-1) + (n-2) + …+ 2 + 1 = n2 /2=O(n2)
或
原因:界点选择不当。改进:随机选取界点或最左、最右、中间三个元素中 的值处于中间的作为界点,通常可以避免最坏情况。
T(n) =1 当 n = 1 时T(n - 1) + (n-1) 当 n > 1 时
129
快速排序• 快速排序的不足和克服方法原因:界点选择不当。改进:随机选取界点或最左、最右、中间三个元素中 的值处于中间的作为界点,通常可以避免最坏情况。 本书采用的就是吸取了这一改进方法的算法。
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
130
快速排序· 快速排序 : 本书实现方法的示意图
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
k j
81 67 4 290
k j
131
快速排序· 快速排序 : 本书实现方法的示意图
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
j
81 67 4 290
k j
k
132
快速排序· 快速排序 : 本书实现方法的示意图
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
j
81 67 4 290
k j
k
133
快速排序· 快速排序 : 本书实现方法的示意图
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
j
81 67 4 290
k j
k
134
快速排序· 快速排序 : 本书实现方法的示意图
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
j
81 67 4 920
k j
k
135
快速排序· 快速排序 : 本书实现方法的示意图
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
j
81 67 4 920
k j
k
136
快速排序· 快速排序 : 本书实现方法的示意图
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
j
81 67 4 920
k j
k
137
快速排序· 快速排序 : 本书实现方法的示意图
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
j
81 64 7 920
k j
k
138
快速排序· 快速排序 : 本书实现方法的示意图
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
j
81 64 7 920
k j
k
139
快速排序· 快速排序 : 本书实现方法的示意图
0 1 2
8 1 9 6 4
3 4 5
1. 初始状态6 7 8
2 7 0
81 67 4 2902. 取中值后
low high
j
81 74 6 920
k j
k
140
快速排序• 程序实现 :
template < class EType>
void QuickSort( EType arr[ ], int low, int high ) {// arr[low], arr[low+1 ], ……, arr[high] 为待分段的序列。
if ( low + insertnum > high ) SimpleInsertSort( &arr[low], high-low +1); else { int k, j, mid = ( low + high ) / 2;
if ( arr[mid] < arr[low] ) Swap(arr[mid], arr[low]); // 将 arr[low] 、 arr[mid] 、 arr[high]if ( arr[high] < arr[low] ) Swap(arr[high], arr[low]); // 进行排序,找出中间值。if ( arr[high] < arr[mid] ) Swap(arr[mid], arr[high]);
EType milestone = arr[mid]; Swap( arr[mid], arr[high-1]);for ( k = low, j = high – 1; ; ) // 分成小于等于界点的左段,和大于等于界点的右段。 { while ( arr[++k] < milestone ) ; while ( arr[ - -j ] > milestone ) ;
if (k < j ) Swap(arr[k], arr[j]); else break; }
Swap(arr[k], arr[high-1]); // 将界点放置在正确的位置上。QuickSort(arr, low, k-1); // 对左段进行快速排序。QuickSort(arr, k+1, high); // 对右段进行快速排序。
} }
141
快速排序• 程序实现 :
const int insertnum = 10;template < class EType> void Swap( EType & x, EType & y ) { EType temp = x; x = y; y = temp; }template < class EType> void SimpleInsertSort( EType a[ ], int Size ) { int j, q; // a[1], a[2 ], …,a[Size] 为待排序的数组。 a[0] 为哨兵单元。 EType temp=a[0]; for ( j = 2; j <= Size; j++ ) { a[0] = a[j]; for ( q = j; a[0] < a[q-1]; q- -) a[q] =a[q-1]; a[q] = a[0]; } a[0] = temp; // 恢复 a[0] 原来的值,用于快速排序法中。 }template < class EType> void QuickSort( EType arr[ ], int n ) { // a[1], a[2 ], …,a[n] 为待排序的数组。 a[0] 不用。 QuickSort( arr, 1, n); }
142
基数排序• 多关键字排序技术: 多关键字:( K1 ,K2, …… Kt ) ; 例如:关键字 K1 小的结点排在前面。如关键字 K1
相同,则比较关键字 K2 ,关键字 K2 小的结点排在前面,依次类推…… 基数排序实例:假定给定的是 t = 2 位十进制数,存放在数组 B 之中。现要求通过基数排 序法将其排序。
方法:设置 十个口袋,因十进制数分别有数字: 0 , 1 , 2 ,…… 9 ,分别 用 B0 、 B1 、 B2 、 …… B9 进行标识。 执行 j = 1..t ( 这里 t = 2 ) 次循环,每次进行一次分配动作,一次收集动作。
分配:将右起第 j 位数字相同的数放入同一口袋。比如数字为 1 者, 则放入口袋 B1 ,余类推 …… 收集:按 B0 、 B1 、 B2 、 …… B9 的顺序进行收集。
e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数技术进行排序。
143
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
144
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
145
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
146
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
147
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
148
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
9
149
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
9
150
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
9
7
151
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
9
7
152
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
9
7
18
153
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
9
7
18
154
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
9
7
18
17
155
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
9
7
18
17
156
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。
5
2
9
7
18
17
52
157
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 5 、 2 、 9 、 7 、 18 、 17 、 52
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋 分配完毕,按照红色 箭头所指的方向进行 收集动作。注意:收集后的序列已经按照右起第一位(个位数字)排好序了。
5
2
9
7
18
17
52
收集后的序列: 2 、 52 、 5 、 7 、 17 、 18 、9 、
158
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
159
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
160
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
161
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
162
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
163
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
5
164
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
5
165
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
5 7
166
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
5 7
167
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
5 717
168
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
5 717
169
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
5 717 18
170
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
5 717 18
171
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋根据 所指向的 数,进行分配动作,将其分配到相应的口 袋。和第一次不同,这次根据右起第二位数字(十位数字)进分配。
2
52
5 717 18
9
172
基数排序 e.g: B = 5 、 2 、 9 、 7 、 18 、 17 、 52 用基数排序法进行排序。
B = 2 、 52 、 5 、 7 、 17 、 18 、 9 (第一次收集的结果)
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋2
52
5 717 18
9
分配完毕,按照红色 箭头所指的方向进行 第二次收集动作。注意:收集后的序列已经按照右起第一位(个位数字)、右起第二位(十位数字)排好序了。
收集后的序列: 2 、 5 、 7 、 9 、 17 、 18、 52
已是排好序的序列。
173
基数排序• 空间:采用顺序分配,显然不合适。由于每个口袋都有可能存放所有的待排序的整数。 所以,额外空间的需求为 10n ,太大了。 采用链接分配是合理的。额外空间的需 求为 n ,通常再增加指向每个口袋的首尾指针就可以了。
在一般情况下,设结点的指针场个数 n, 首尾指针共计 2×radix 个 ,总 的空间为 O( n+2×radix ) 。• 时间:上例中每个数计有 t = 2 位,因此执行 t = 2 次分配和收集就可以了。在一般情况 下,每个结点有 d 位关键字,必须执行 t = d 次分配和收集操作。 每次分配的代价: O(n)
每次收集的代价: O( radix )
总的代价为: O( d ×(n + radix))
174
基数排序
B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
口袋2
52
5 717 18
9
f0 r0
• 首尾指针的使用:
175
基数排序const int radix = 10; // 如:十进制整数的基为 10 。
const int t = 5; // 整数的最大位数。
typedef struct node { int data ;
int next; // 给出下一个结点的下标地址。 } node;
void BucketSort( node arr[ ], int max ) {
// arr[1], arr[2 ], …,arr[max] 为待排序的整数数组, a[0] 用作头结点。 for ( int j=0; j < max; j++ ) arr[j].next = j + 1; arr[max].next = 0; // 生成静态链表。 RadixSort( arr, max);
}
176
基数排序void RadixSort( node a[ ], int max ) {
int front[radix], tail[radix]; // 口袋的首尾指针。int p, last, j, k, d = 1;for ( j=1; j<= t; j++) { for ( k = 0; k < 10; ++k ) { front[ k ]=0; tail[ k ]=0; } // 口袋的首尾指针置初值。 p = a[0].next; // 头结点 a[0] 给出链中的第一个整数结点的下标地址。
while ( p ) { // 分配过程。 k = a[p].data / d % radix; // 取出右起第 j 位数字,将结点放入口袋 Bk 。
if ( ! tail[k] ) front[ k ] = p; else a[ tail[k] ].next = p; tail[k] = p; p = a[p].next;
} last=0; for ( k = 0; k < radix; ++k) { // 收集过程,从口袋 B0 到最后一个口袋。
if ( tail[k] ) { a[last].next = front[k]; last = tail[k];} a[last].next = 0;
} d *= radix;
} }