第8章 排序 8.1 排序的基本概念 8.2 插入排序 8.3 选择排序 8.4 交换排序 本章主要知识点:
● ● ● ●
排序的基本概念和衡量排序算法优劣的标准,其中衡量标准有算法的时间复杂度、空间复杂度和稳定性
直接插入排序,希尔排序 直接选择排序,堆排序 冒泡排序,快速排序
8.1排序的基本概念
1.排序是对数据元素序列建立某种有序排列的过程。 2.排序的目的:便于查找。
3.关键字是要排序的数据元素集合中的一个域,排序是以关键字为基准进行的。
关键字分主关键字和次关键字两种。对要排序的数据元素集合来说,如果关键字满足数据元素值不同时该关键字的值也一定不同,这样的关键字称为主关键字。不满足主关键字定义的关键字称为次关键字。
4.排序的种类:分为内部排序和外部排序两大类。
若待排序记录都在内存中,称为内部排序;若待排序记录一部分在内存,一部分在外存,则称为外部排序。
注:外部排序时,要将数据分批调入内存来排序,中间结果还要及时放入外 存,显然外部排序要复杂得多。 5.排序算法好坏的衡量标准:
(1)时间复杂度—— 它主要是分析记录关键字的比较次数和记录的移动次数。 (2)空间复杂度——算法中使用的内存辅助空间的多少。
(3)稳定性——若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。 8.2 插入排序
插入排序的基本思想是:每步将一个待排序的对象,按其关键字大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。
简言之,边插入边排序,保证子序列中随时都是排好序的。 常用的插入排序有:直接插入排序和希尔排序两种。 8.2.1 直接插入排序 1、其基本思想是:
顺序地把待排序的数据元素按其关键字值的大小插入到已排序数据元素子集合的适当位置。 例1:关键字序列T=(13,6,3,31,9,27,5,11), 请写出直接插入排序的中间过程序列。 初始关键字序列:【13】, 6, 3, 31, 9, 27, 5, 11 第一次排序: 【6, 13】, 3, 31, 9, 27, 5, 11 第二次排序: 【3, 6, 13】, 31, 9, 27, 5, 11 第三次排序: 【3, 6, 13,31】, 9, 27, 5, 11 第四次排序: 【3, 6, 9, 13,31】, 27, 5, 11 第五次排序: 【3, 6, 9, 13,27, 31】, 5, 11
第六次排序: 【3, 5, 6, 9, 13,27, 31】, 11 第七次排序: 【3, 5, 6, 9, 11,13,27, 31】
注:方括号 [ ]中为已排序记录的关键字,下划横线的 关键字 表示它对应的记录后移一个位置。 2.直接插入排序算法
public static void insertSort(int[] a){ }
初始关键字序列:【13】, 6, 3, 31, 9, 27, 5, 11 第一次排序: 【6, 13】, 3, 31, 9, 27, 5, 11 第二次排序: 【3, 6, 13】, 31, 9, 27, 5, 11 3、直接插入排序算法分析
(1)时间效率:当数据有序时,执行效率最好,此时的时间复杂度为O(n);当数据基本反序时,执行效率最差,此时的时间复杂度为O(n)。所以当数据越接近有序,直接插入排序算法的性能越好。 (2)空间效率:仅占用1个缓冲单元——O(1) (3)算法的稳定性:稳定
8.2.2 希尔(shell)排序 (又称缩小增量排序)
1、基本思想:把整个待排序的数据元素分成若干个小组,对同一小组内的数据元素用直接插入法排序;
小组的个数逐次缩小,当完成了所有数据元素都在一个组内的排序后排序过程结束。 2、技巧:小组的构成不是简单地“逐段分割”,而是将相隔某个增量d的记录组成一个小组,让增量d逐趟缩短(例如依次取5,3,1),直到d=1为止。
3、优点:让关键字值小的元素能很快前移,且序列若基本有序时,再用直接插入排序处理,时间效率会高很多。
例2:设待排序的序列中有12个记录,它们的关键字序列 T=(65,34,25,87,12,38,56,46,14,77,92,23),请写出希尔排序的具体实现过程。 public static void shellSort(int[] a, int[] d, int numOfD){
int i, j, k, m, span; int temp; int n = a.Length;
for(m = 0; m < numOfD; m ++){
span = d[m];
for(k = 0; k < span; k ++){
//共numOfD次循环 //取本次的增量值 //共span个小组
2
int i, j, temp; int n = a.Length; for(i = 0; i < n - 1; i ++){ temp = a[i + 1]; j = i;
while(j > -1 && temp < a[j]){
a[j + 1] = a[j]; j --;
}
a[j + 1] = temp; }
for(i = k; i < n-span; i = i + span){
temp = a[i+span];
}
}
}
}
j = i;
while(j > -1 && temp < a[j]){ }
a[j + span] = temp;
a[j + span] = a[j]; j = j - span;
算法分析:开始时d 的值较大,子序列中的对象较少,排序速度较快;随着排序进展,d 值逐渐变小,子序列中对象个数逐渐变多,由于前面工作的基础,大多数记录已基本有序,所以排序速度仍然很快。 时间效率:O(n(log2n))
空间效率:O(1)——因为仅占用1个缓冲单元 算法的稳定性:不稳定 练习:
1. 欲将序列(Q, H, C, Y, P, A, M, S, R, D, F, X)中的关键码按字母升序重排,则初始d为4的希尔排序一趟的结果是?
答: 原始序列: Q, H, C, Y, P, A, M, S, R, D, F, X shell一趟后: P,A,C,S,Q,D,F,X,R,H,M,Y
2. 以关键字序列(256,301,751,129,937,863,742,694,076,438)为例,写出执行希尔排序(取d=5,3,1)算法的各趟排序结束时,关键字序列的状态。
解:原始序列: 256,301,751,129,937,863,742,694,076,438 希尔排序第一趟d=5 256 301 694 076 438 863 742 751 129 937 第二趟d=3 076 301 129 256 438 694 742 751 863 937
第三趟d=1 076 129 256 301 438 694 742 751 863 937
8.3 选择排序
选择排序的基本思想是:每次从待排序的数据元素集合中选取关键字最小(或最大)的数据元素放到数据元素集合的最前(或最后),数据元素集合不断缩小,当数据元素集合为空时选择排序结束。 常用的选择排序算法: (1)直接选择排序 (2)堆排序 8.3.1直接选择排序 1、其基本思想
每经过一趟比较就找出一个最小值,与待排序列最前面的位置互换即可。
(即从待排序的数据元素集合中选取关键字最小的数据元素并将它与原始数据元素集合中的第一个数据元素交换位置;然后从不包括第一个位置的数据元素集合中选取关键字最小的数据元素并将它与原始数据集合中的第二个数据元素交换位置;如此重复,直到数据元素集合中只剩一个数据元素为止。) 2、优缺点 优点:实现简单
缺点:每趟只能确定一个元素,表长为n时需要n-1趟
例3:关键字序列T= (21,25,49,25*,16,08),请给出直接选择排序的具体实现过程。 原始序列: 21,25,49,25*,16,08 第1趟 08,25,49,25*,16,21
2
第2趟 08,16, 49,25*,25,21 第3趟 08,16, 21,25*,25,49 第4趟 08,16, 21,25*,25,49 第5趟 08,16, 21,25*,25,49 public static void selectSort(int[] a){ }
3、算法分析
时间效率: O(n)——虽移动次数较少,但比较次数仍多。 空间效率:O(1)——没有附加单元(仅用到1个temp) 算法的稳定性:不稳定 4、稳定的直接选择排序算法
例:关键字序列T= (21,25,49,25*,16,08),请给出稳定的直接选择排序的具体实现过程。 原始序列: 21,25,49,25*,16,08 第1趟08, 21 , 25 , 49 , 25 *, 16 第2趟08,16, 21,25,49 ,25 * 第3趟08,16, 21,25,49 ,25 * 第4趟08,16, 21,25,49 ,25 * 第5趟08,16, 21,25,25 * ,49 public static void selectSort2(int [] a){
int i,j,small; int temp; int n = a.Length; for(i = 0; i < n-1; i++){ small = i;
for(j = i+1; j < n; j++){ //寻找最小的数据元素
if(a[j] < a[small]) small = j; //记住最小元素的下标 }
if(small != i){
temp = a[small];
a[j] = a[j-1];
for(j = small; j > i; j--) //把该区段尚未排序元素依次后移
2
int i, j, small; int temp; int n = a.Length; for(i = 0; i < n - 1; i ++){ small = i;
//设第i个数据元素最小
for(j = i + 1; j < n; j ++) //寻找最小的数据元素
if(a[j] < a[small]) small = j; //记住最小元素的下标 if(small != i){ //当最小元素的下标不为i时交换位置 temp = a[i]; a[i] = a[small]; a[small] = temp; } }
}
}
a[i] = temp; //插入找出的最小元素
}
8.3.2 堆排序
1. 什么是堆? 2. 怎样建堆? 3. 怎样堆排序?
堆的定义:设有n个数据元素的序列 k0,k1,…,kn-1,当且仅当满足下述关系之一时,称之为堆。 解释:如果让满足以上条件的元素序列 (k0,k1,…,kn-1)顺次排成一棵完全二叉树,则此树的特点是: 树中所有结点的值均大于(或小于)其左右孩子,此树的根结点(即堆顶)必最大(或最小)。 例4:有序列T1=(08, 25, 49, 46, 58, 67)和序列T2=(91, 85, 76, 66, 58, 67, 55),判断它们是否 “堆”? 2. 怎样建堆?
步骤:从第一个非终端结点开始往前逐步调整,让每个双亲大于(或小于)子女,直到根结点为止。
终端结点(即叶子)没有任何子女,无需单独调整
例:关键字序列T= (21,25,49,25*,16,08),请建最大堆。 解:为便于理解,先将原始序列画成完全二叉树的形式: 这样可以很清晰地从(n-1-1)/2开始调整。 public static void createHeap(int[] a, int n, int h){ }
利用上述createHeap(a,n,h)函数,初始化创建最大堆的过程就是从第一个非叶子结点a[i]开始,到根结点a[0]为止,循环调用createHeap(a,n,h)的过程。 初始化创建最大堆算法如下: public static void initCreateHeap(int[] a){ }
int n = a.Length;
for(int i = (n-1-1) / 2; i >= 0; i --)
createHeap(a, n, i);
int i, j, flag; int temp; i = h;
// j为i结点的左孩子结点的下标
j = 2 * i + 1; temp = a[i];
flag = 0; while(j < n && flag != 1){
//寻找左右孩子结点中的较大者,j为其下标 }
a[i] = temp;
if(j < n - 1 && a[j] < a[j + 1]) j++; if (temp >= a[j]) } else{
i = j; j = 2 * i + 1;
//a[i]>=a[j]
flag = 1; //标记结束筛选条件
//否则把a[j]上移
a[i] = a[j];
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库数据结构-排序在线全文阅读。
相关推荐: