第十章排序(2)交换排序
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
重复(1)(2)直到 low>=high的时候停止,此时,将枢 轴记录到位,返回枢轴位置。
按照快速排序的基本思想,在一趟快速排序之后,需要 重复(1),(2),直到找到所有记录的相应位置。显然, 快速排序是一个递归的过程。
快速排序
int Partition(SqList &L, int low, int high) { // 算法10.6(b)
(3)比较序列中始端、终端及中间位置上记录的关键字值, 并取这三个值中居中的一个作为基准关键字。
为了叙述方便,在下面的快速排序中,选取第一个记录的关 键字作为基准关键字。
快速排序
快速排序的实现
算法中记录的比较和交换是从待排记录序列的两端向中 间进行的。设置两个变量low和high,其初值分别是n 个待排序记录中第一个记录的位置号和最后一个记录 的位置号。在扫描过程中,变量low,high的值始终表 示当前所扫描分组序列的第一个和最后一个记录的位 置号。设枢轴记录的关键字为pivotkey,并把该位置保 存在R[0]中,然后每趟快速排序,进行如下操作:
// 从表的两端交替地向中间扫描
while (low<high && L.r[high].key>=pivotkey) --high; L.r[low] = L.r[high]; // 将比枢轴记录小的记录移到低端
while (low<high && L.r[low].key<=pivotkey) ++low;
快速排序算法是不稳定排序,对于有相同关键字的记录,排序后 有可能颠倒位置。
课堂练习
• 设要将序列(Q, H, C, Y, P, A, M, S, R, D, F, X)中的
关键码按字母序的升序重新排列。快速排序一趟扫描
的结果是
。
z ( F, H, C, D, P, A, M, Q, R, S, Y, X )
L.r[high] = L.r[low]; // 将比枢轴记录大的记录移到高端
} L.r[low] = L.r[0]; return low;
// 枢轴记录到位 // 返回枢轴位置
} // Partition
快速排序
Eg10-6 关键字序列 T=(21,25,49,25*,16,08),请写出 快速排序算法的一趟实现过程。
快速排序
(3)一般情况下,序列中各记录关键字的分布是随机的,因而可以 认为快速排序算法的平均时间复杂度为O(nlog2n)。实验证明,当n 较大时,快速排序是目前被认为最好的一种内部排序方法。
在算法实现中需设置一个栈的存贮空间来实现递归,栈的大小取 决于递归深度,最多不会超过n。若每次都选较长的分组序列进栈, 而处理较短的分组序列,则递归深度最多不会超过log2n,因此快 速排序需要的辅助存贮空间为O(log2n)。
(1)从序列最后位置的记录Rj开始依次往前扫描,若 存在pivotkey≤Rj.key ,则令high前移一个,即high--, 如此直到pivotkey >Rj.key或high=low为止。若low<h igh,将比枢轴记录小的记录移到低端。
快速排序
(2)从序列最前位置的记录Ri开始依次往后扫描,若存 在pivotkey ≥R[i].key,则令low后移一个位置,即low++, 如此比较直到pivotkey <Ri.key或high=low为止。若lo w<high,将比枢轴记录大的记录移到高端。
2
时间效率:O(n2) —因为要考虑最坏情况 空间效率:O(1) —只在交换时用到一个缓冲单元 稳 定 性: 稳定
快速排序
快速排序的基本思想是:
从待排记录序列中任取一个记录Ri作为基准(通常取序列 中的第一个记录),将所有记录分成两个序列分组, 使排在Ri之前的序列分组的记录关键字都小于等于基 准记录的关键字值Ri.key,排在Ri之后的序列分组的记 录关键字都大于Ri.key,形成以Ri为分界的两个分组, 此时基准记录Ri的位置就是它的最终排序位置。此趟 排序称为第一趟快速排序。
假设这6个数依次为: 9,8,5,4,2,0 最后排序的结果为:0,2,4,5,8,9
第
二 趟
85 5 5 5 58 4 4 4 44 8 2 2 22 2 8 0
倒数第二大 数沉到倒数
第二位
00 0 0 8
99 999
第
第
第
第结
一
二
三
四
次
次
次
次果
2011-5-17
6
Eg10-5 用冒泡法对6个整数进行排序(从小到大)
假设这6个数依次为: 9,8,5,4,2,0 最后排序的结果为:0,2,4,5,8,9
54 4 4
倒数第三
Hale Waihona Puke Baidu
第
三 趟
45 22 00
22 50 05
大数沉到 倒数第三
位
88 99
88 99
第
第
第
结
一
二
三
次
次
次
果
2011-5-17
7
Eg10-5 用冒泡法对6个整数进行排序(从小到大)
假设这6个数依次为: 9,8,5,4,2,0 最后排序的结果为:0,2,4,5,8,9
果
其他数都就位 后,最后一位 的位置也就确
定了!
倒数第五大 数沉到倒数
第五位
2011-5-17
9
冒泡排序
void bubbleSort(SqList &L) // 冒泡排序算法 {
int i,j; for(i=1;i<=L.length -1;i++) for(j=1;j<=L.length-i;j++)
第十章 内部排序
交换排序 主讲:刘春
学习目标
① 排序的概念及种类 ② 插入法排序的各种具体实现方法及算法分析 ③ 选择法排序的各种具体方法的实现及时间性
能分析 ④ 交换法排序的具体实现及性能分析 ⑤ 归并排序和基数排序的各自实现算法
10.2 交换排序
主要思想:两两比较待排序记录的关键码,如果发生逆序 (即排列顺序与排序后的次序正好相反),则交换之,直 到所有记录都排好序为止。
最坏情形:初始排列逆序,算法要执行n-1趟起泡,第 i 趟 (1≤ i< n) 做了n- i 次关键码比较,执行了n-i 次对象交换。 此时的总比较次数KCN和记录移动次数RMN为:
KCN RMN
∑ =
n −1
(n
−
i)
=
1
n(n
− 1)
i=1
2
∑ =
n −1
3 (n
−
i)
=
3
n(n
− 1)
i=1
若n为2的幂次值且每次分解都是等长的,则分解过程可用一棵满 二叉树描述,分解次数等于树的深度k=log2n,因此有: C(n)≤nlog2n+nC(1)=O(nlog2n) 整个算法的时间复杂度为O(nlog2n)。
快速排序
(2) 在极端情况下,即每次选取的“基准”都是当前分组序列中关 键字最小(或最大)的值,划分的结果是基准的前边(或右边)为空, 即把原来的分组序列分解成一个空序列和一个长度为原来序列 长度减1的子序列。总的比较次数达到最大值:
if(L.r[j].key >L.r[j+1].key ) {
L.r[0]=L.r[j]; L.r[j]=L.r[j+1]; L.r[j+1]=L.r[0]; } }// end of bubbleSort
冒泡排序
冒泡排序的算法分析
最好情况:初始排列已经有序,只执行一趟起泡,做 n-1 次关键码比较,不移动对象。
// 交换顺序表L中子序列L.r[low..high]的记录,使枢轴记录到位, // 并返回其所在位置
KeyType pivotkey;
L.r[0] = L.r[low];
// 用子表的第一个记录作枢轴记录
pivotkey = L.r[low].key; // 枢轴记录关键字
while (low<high) {
快速排序
快速排序算法的执行时间取决于基准记录的选择。一趟快速排序算 法的时间复杂度为O(n)。下面分几种情况讨论整个快速排序算 法需要排序的趟数:
(1)在理想情况下,每次排序时所选取的记录关键字值都是当前 待排序列中的“中值”记录,那么该记录的排序终止位置应在 该序列的中间,这样就把原来的子序列分解成了两个长度大致 相等的更小的子序列,在这种情况下,排序的速度最快。设完 成n个记录待排序列所需的比较次数为C(n),则有: C(n)≤n+2C(n/2)≤2n+4C(n/4)≤kn+nC(1)(k是序列的分解次数)
快速排序
设以首元素为枢轴中心
Eg10-7 关键字序列 T=(21,25,49,25*,16,08),请 写出快速排序的算法步骤。
初 态: 21, 25, 49, 25*,16, 08 第1趟:( 08, 16), 21 ,( 25*, 49, 25 ) 第2趟: 08,(16),21, (25), 25*, (49) 第3趟: 08,16,21,25, 25*,49
第 四
4 22 2 40 0 04
倒数第四大 数沉到倒数
第四位
趟
555
888
999
第
第
结
一
二
次
次
果
2011-5-17
8
Eg10-5 用冒泡法对6个整数进行排序(从小到大)
假设这6个数依次为: 9,8,5,4,2,0 最后排序的结果为:0,2,4,5,8,9
20
第
02
五 趟
44 55
88
99
第
结
一
次
∑ Cmax
=
n−1 i =1
(n
−
i)
=
n(n −1) 2
=
O(n2 )
如果初始记录序列已为升序或降序排列,并且选取的基准记录 又是该序列中的最大或最小值,这时的快速排序就变成了“慢 速排序”。整个算法的时间复杂度为O(n2)。 为了避免这种情况的发生,可修改上面的排序算法,在每趟排 序之前比较当前序列的第一、最后和中间记录的关键字,取关 键字居中的一个记录作为基准值调换到第一个记录的位置。
然后分别对两个序列分组重复上述过程,直到所有记录排 在相应的位置上为止。
快速排序
选取基准
在快速排序中,选取基准常用的方法有:
(1)选取序列中第一个记录的关键字值作为基准关键字。 这种选择方法简单。但是当序列中的记录已基本有序时, 这种选择往往使两个序列分组的长度不均匀,不能改进排 序的时间性能。
(2)选取序列中间位置记录的关键字值作为基准关键字。
假设这6个数依次为: 9,8,5,4,2,0 最后排序的结果为:0,2,4,5,8,9
第 一 趟
9888 8 8 8955 5 5 5594 4 4 4449 2 2 2222 9 0
最大数 沉到最 下面
0000 0 9
第
一 次
第
二 次
第 三 次
第第
四五 次次
结 果
Eg10-5 用冒泡法对6个整数进行排序(从小到大)
课后作业
以关键字序列(256,301,751,129,937,863,742, 694,076,438)为例,分别写出执行以下算法的各趟 排序结束时,关键字序列的状态。
① 冒泡排序 ② 快速排序
交换排序主要的实现算法: 冒泡排序 快速排序
冒泡排序
基本思路:每趟不断将记录两两比较,并按“前小后大” (或“前大后小”)规则交换。
优点:每趟结束时,不仅能挤出一个最大值到最后面位置, 还能同时部分理顺其他元素;一旦下趟没有交换发 生,还可以提前结束排序。
前提:顺序存储结构
Eg10-5 用冒泡法对6个整数进行排序(从小到大)
low
pivotkey=21
high
r[i] 0 1
2
3
4
5
6
初态
21 25 49 25* 16 08
第1趟 21 08
2156 4291
25*
1469 0285
( 08 ,16 ) 21 ( 25* ,49, 25 )
Low=high=3,本趟停止,将枢轴 定位并返回位置信息
25*跑到了前面,不稳定!
按照快速排序的基本思想,在一趟快速排序之后,需要 重复(1),(2),直到找到所有记录的相应位置。显然, 快速排序是一个递归的过程。
快速排序
int Partition(SqList &L, int low, int high) { // 算法10.6(b)
(3)比较序列中始端、终端及中间位置上记录的关键字值, 并取这三个值中居中的一个作为基准关键字。
为了叙述方便,在下面的快速排序中,选取第一个记录的关 键字作为基准关键字。
快速排序
快速排序的实现
算法中记录的比较和交换是从待排记录序列的两端向中 间进行的。设置两个变量low和high,其初值分别是n 个待排序记录中第一个记录的位置号和最后一个记录 的位置号。在扫描过程中,变量low,high的值始终表 示当前所扫描分组序列的第一个和最后一个记录的位 置号。设枢轴记录的关键字为pivotkey,并把该位置保 存在R[0]中,然后每趟快速排序,进行如下操作:
// 从表的两端交替地向中间扫描
while (low<high && L.r[high].key>=pivotkey) --high; L.r[low] = L.r[high]; // 将比枢轴记录小的记录移到低端
while (low<high && L.r[low].key<=pivotkey) ++low;
快速排序算法是不稳定排序,对于有相同关键字的记录,排序后 有可能颠倒位置。
课堂练习
• 设要将序列(Q, H, C, Y, P, A, M, S, R, D, F, X)中的
关键码按字母序的升序重新排列。快速排序一趟扫描
的结果是
。
z ( F, H, C, D, P, A, M, Q, R, S, Y, X )
L.r[high] = L.r[low]; // 将比枢轴记录大的记录移到高端
} L.r[low] = L.r[0]; return low;
// 枢轴记录到位 // 返回枢轴位置
} // Partition
快速排序
Eg10-6 关键字序列 T=(21,25,49,25*,16,08),请写出 快速排序算法的一趟实现过程。
快速排序
(3)一般情况下,序列中各记录关键字的分布是随机的,因而可以 认为快速排序算法的平均时间复杂度为O(nlog2n)。实验证明,当n 较大时,快速排序是目前被认为最好的一种内部排序方法。
在算法实现中需设置一个栈的存贮空间来实现递归,栈的大小取 决于递归深度,最多不会超过n。若每次都选较长的分组序列进栈, 而处理较短的分组序列,则递归深度最多不会超过log2n,因此快 速排序需要的辅助存贮空间为O(log2n)。
(1)从序列最后位置的记录Rj开始依次往前扫描,若 存在pivotkey≤Rj.key ,则令high前移一个,即high--, 如此直到pivotkey >Rj.key或high=low为止。若low<h igh,将比枢轴记录小的记录移到低端。
快速排序
(2)从序列最前位置的记录Ri开始依次往后扫描,若存 在pivotkey ≥R[i].key,则令low后移一个位置,即low++, 如此比较直到pivotkey <Ri.key或high=low为止。若lo w<high,将比枢轴记录大的记录移到高端。
2
时间效率:O(n2) —因为要考虑最坏情况 空间效率:O(1) —只在交换时用到一个缓冲单元 稳 定 性: 稳定
快速排序
快速排序的基本思想是:
从待排记录序列中任取一个记录Ri作为基准(通常取序列 中的第一个记录),将所有记录分成两个序列分组, 使排在Ri之前的序列分组的记录关键字都小于等于基 准记录的关键字值Ri.key,排在Ri之后的序列分组的记 录关键字都大于Ri.key,形成以Ri为分界的两个分组, 此时基准记录Ri的位置就是它的最终排序位置。此趟 排序称为第一趟快速排序。
假设这6个数依次为: 9,8,5,4,2,0 最后排序的结果为:0,2,4,5,8,9
第
二 趟
85 5 5 5 58 4 4 4 44 8 2 2 22 2 8 0
倒数第二大 数沉到倒数
第二位
00 0 0 8
99 999
第
第
第
第结
一
二
三
四
次
次
次
次果
2011-5-17
6
Eg10-5 用冒泡法对6个整数进行排序(从小到大)
假设这6个数依次为: 9,8,5,4,2,0 最后排序的结果为:0,2,4,5,8,9
54 4 4
倒数第三
Hale Waihona Puke Baidu
第
三 趟
45 22 00
22 50 05
大数沉到 倒数第三
位
88 99
88 99
第
第
第
结
一
二
三
次
次
次
果
2011-5-17
7
Eg10-5 用冒泡法对6个整数进行排序(从小到大)
假设这6个数依次为: 9,8,5,4,2,0 最后排序的结果为:0,2,4,5,8,9
果
其他数都就位 后,最后一位 的位置也就确
定了!
倒数第五大 数沉到倒数
第五位
2011-5-17
9
冒泡排序
void bubbleSort(SqList &L) // 冒泡排序算法 {
int i,j; for(i=1;i<=L.length -1;i++) for(j=1;j<=L.length-i;j++)
第十章 内部排序
交换排序 主讲:刘春
学习目标
① 排序的概念及种类 ② 插入法排序的各种具体实现方法及算法分析 ③ 选择法排序的各种具体方法的实现及时间性
能分析 ④ 交换法排序的具体实现及性能分析 ⑤ 归并排序和基数排序的各自实现算法
10.2 交换排序
主要思想:两两比较待排序记录的关键码,如果发生逆序 (即排列顺序与排序后的次序正好相反),则交换之,直 到所有记录都排好序为止。
最坏情形:初始排列逆序,算法要执行n-1趟起泡,第 i 趟 (1≤ i< n) 做了n- i 次关键码比较,执行了n-i 次对象交换。 此时的总比较次数KCN和记录移动次数RMN为:
KCN RMN
∑ =
n −1
(n
−
i)
=
1
n(n
− 1)
i=1
2
∑ =
n −1
3 (n
−
i)
=
3
n(n
− 1)
i=1
若n为2的幂次值且每次分解都是等长的,则分解过程可用一棵满 二叉树描述,分解次数等于树的深度k=log2n,因此有: C(n)≤nlog2n+nC(1)=O(nlog2n) 整个算法的时间复杂度为O(nlog2n)。
快速排序
(2) 在极端情况下,即每次选取的“基准”都是当前分组序列中关 键字最小(或最大)的值,划分的结果是基准的前边(或右边)为空, 即把原来的分组序列分解成一个空序列和一个长度为原来序列 长度减1的子序列。总的比较次数达到最大值:
if(L.r[j].key >L.r[j+1].key ) {
L.r[0]=L.r[j]; L.r[j]=L.r[j+1]; L.r[j+1]=L.r[0]; } }// end of bubbleSort
冒泡排序
冒泡排序的算法分析
最好情况:初始排列已经有序,只执行一趟起泡,做 n-1 次关键码比较,不移动对象。
// 交换顺序表L中子序列L.r[low..high]的记录,使枢轴记录到位, // 并返回其所在位置
KeyType pivotkey;
L.r[0] = L.r[low];
// 用子表的第一个记录作枢轴记录
pivotkey = L.r[low].key; // 枢轴记录关键字
while (low<high) {
快速排序
快速排序算法的执行时间取决于基准记录的选择。一趟快速排序算 法的时间复杂度为O(n)。下面分几种情况讨论整个快速排序算 法需要排序的趟数:
(1)在理想情况下,每次排序时所选取的记录关键字值都是当前 待排序列中的“中值”记录,那么该记录的排序终止位置应在 该序列的中间,这样就把原来的子序列分解成了两个长度大致 相等的更小的子序列,在这种情况下,排序的速度最快。设完 成n个记录待排序列所需的比较次数为C(n),则有: C(n)≤n+2C(n/2)≤2n+4C(n/4)≤kn+nC(1)(k是序列的分解次数)
快速排序
设以首元素为枢轴中心
Eg10-7 关键字序列 T=(21,25,49,25*,16,08),请 写出快速排序的算法步骤。
初 态: 21, 25, 49, 25*,16, 08 第1趟:( 08, 16), 21 ,( 25*, 49, 25 ) 第2趟: 08,(16),21, (25), 25*, (49) 第3趟: 08,16,21,25, 25*,49
第 四
4 22 2 40 0 04
倒数第四大 数沉到倒数
第四位
趟
555
888
999
第
第
结
一
二
次
次
果
2011-5-17
8
Eg10-5 用冒泡法对6个整数进行排序(从小到大)
假设这6个数依次为: 9,8,5,4,2,0 最后排序的结果为:0,2,4,5,8,9
20
第
02
五 趟
44 55
88
99
第
结
一
次
∑ Cmax
=
n−1 i =1
(n
−
i)
=
n(n −1) 2
=
O(n2 )
如果初始记录序列已为升序或降序排列,并且选取的基准记录 又是该序列中的最大或最小值,这时的快速排序就变成了“慢 速排序”。整个算法的时间复杂度为O(n2)。 为了避免这种情况的发生,可修改上面的排序算法,在每趟排 序之前比较当前序列的第一、最后和中间记录的关键字,取关 键字居中的一个记录作为基准值调换到第一个记录的位置。
然后分别对两个序列分组重复上述过程,直到所有记录排 在相应的位置上为止。
快速排序
选取基准
在快速排序中,选取基准常用的方法有:
(1)选取序列中第一个记录的关键字值作为基准关键字。 这种选择方法简单。但是当序列中的记录已基本有序时, 这种选择往往使两个序列分组的长度不均匀,不能改进排 序的时间性能。
(2)选取序列中间位置记录的关键字值作为基准关键字。
假设这6个数依次为: 9,8,5,4,2,0 最后排序的结果为:0,2,4,5,8,9
第 一 趟
9888 8 8 8955 5 5 5594 4 4 4449 2 2 2222 9 0
最大数 沉到最 下面
0000 0 9
第
一 次
第
二 次
第 三 次
第第
四五 次次
结 果
Eg10-5 用冒泡法对6个整数进行排序(从小到大)
课后作业
以关键字序列(256,301,751,129,937,863,742, 694,076,438)为例,分别写出执行以下算法的各趟 排序结束时,关键字序列的状态。
① 冒泡排序 ② 快速排序
交换排序主要的实现算法: 冒泡排序 快速排序
冒泡排序
基本思路:每趟不断将记录两两比较,并按“前小后大” (或“前大后小”)规则交换。
优点:每趟结束时,不仅能挤出一个最大值到最后面位置, 还能同时部分理顺其他元素;一旦下趟没有交换发 生,还可以提前结束排序。
前提:顺序存储结构
Eg10-5 用冒泡法对6个整数进行排序(从小到大)
low
pivotkey=21
high
r[i] 0 1
2
3
4
5
6
初态
21 25 49 25* 16 08
第1趟 21 08
2156 4291
25*
1469 0285
( 08 ,16 ) 21 ( 25* ,49, 25 )
Low=high=3,本趟停止,将枢轴 定位并返回位置信息
25*跑到了前面,不稳定!