排序学习要点

概述

本章主要学习4类内排序方法,插入排序是将记录插入到已排序序列的正确位置上;交换排序是将两个记录进行比较,当记录反序时交换;选择排序是从未排序序列中选出最小记录放在已排序序列的一端;归并排序是将有序序列进行合并,本章知识点的组织结构如下图所示:

上面图应该是排序

重点/难点/要点

本章的重点是:

  1. 各种排序算法的基本思想;
  2. 各种排序算法的执行过程;
  3. 各种排序算法的设计;
  4. 各种排序算法时间复杂度;
  5. 各种排序算法之间的比较。

本章的难点是:

  1. 快速排序、堆排序、归并排序等算法的设计;
  2. 快速排序、堆排序算法的时间复杂度分析。

排序学习要点
本章按照:【理解排序基本思想→提出关键问题→运行实例分析问题→解决关键问题的方法→总结并写出完整算法→时间和空间性能分析→得出适用情况。】的模式介绍每种排序算法,通过分析简单排序方法(直接插入排序、起泡排序、简单选择排序等)的缺点以及产生缺点的原因,引入改进的排序方法(希尔排序、快速排序、堆排序等)。
排序算法体现了较高超的算法设计技术和算法分析技术。从技能应用的角度,本章要求:深刻理解各种排序算法的设计思想,并能应用排序算法的设计思想解决和排序相关的问题,从而提高解决问题能力;对改进的算法,分析其改进的着眼点是什么,自己能否从某一个方面改进一个排序算法,从而提高算法设计能力;对各类排序方法进行综合对比,从而得出一般性结论,在实际应用中可以根据情况选取合适的排序方法。

知识点整理

  1. 排序是将一个记录的任意序列重新排列成一个按键值有序的序列。
  2. 假定在待排序的记录序列中,存在多个具有相同键值的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中, k i = k j k_i=k_j ki=kj,且 r i r_i ri r j r_j rj之前,而在排序后的序列中, r i r_i ri仍在 r j r_j rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
  3. 基于比较的内排序,在排序过程中通常需要进行两种基本操作:
    ①比较:关键码之间的比较;
    ②移动:记录从一个位置移动到另一个位置。
  4. 直接插入排序的基本思想是:依次将待排序序列中的每一个记录插人到一个已排好序的序列中,直到全部记录都排好序。在最好情况下,待排序序列为正序,时间复杂度为 O ( n ) O(n) O(n);在最坏情况下,待排序序列为逆序,时间复杂度为 O ( n 2 ) O(n^2) O(n2);平均情况下,时间复杂度为 O ( n 2 ) O(n^2) O(n2)
  5. 希尔排序是对直接插入排序的改进,其基本思想是:先将整个待排序记录序列分割成若干个子序列,在子序列内分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序。其时间复杂度是 O ( n log ⁡ 2 n ) ∼ O ( n 2 ) O(n\log_2n)\sim O(n^2) O(nlog2n)O(n2)
  6. 起泡排序的基本思想是:两两比较相邻记录的关键码,如果反序则交换,直到没有反序的记录为止。在最好情况下,待排序序列为正序,时间复杂度为 O ( n ) O(n) O(n);在最坏情况下,待排序序列为逆序,时间复杂度为 O ( n 2 ) O(n^2) O(n2);平均情况下,时间复杂度为 O ( n 2 ) O(n^2) O(n2)
  7. 快速排序是对起泡排序的改进,其基本思想是:首先选一个轴值,将待排序记录分割成独立的两部分,左侧记录的关键码均小于或等于轴值,右侧记录的关键码均大于或等于轴值,然后分别对这两部分重复上述过程,直到整个序列有序。在最好情况下,每次划分轴值的左侧子序列与右侧子序列的长度相同,时间复杂度为 O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n);在最坏情况下,待排序序列为正序或逆序,时间复杂度为 O ( n 2 ) O(n^2) O(n2);平均情况下,时间复杂度为 O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)
  8. 简单选择排序的基本思想是:第 i i i趟通过 n − i n-i ni次关键码的比较,在 n − i + 1 ( 1 ≤ i ≤ n − 1 ) n-i+1(1≤i≤n-1) ni+1(1in1)个记录中选取关键码最小的记录,并和第 i i i个记录交换作为有序序列的第 i i i个记录。最好、最坏、平均的时间复杂度都是 O ( n 2 ) O(n^2) O(n2)
  9. 堆排序是对简单选择排序的改进,其基本思想是:首先将待排序的记录序列构造成一个堆,此时,选出了堆中所有记录的最大者即堆顶记录,然后将它从堆中移走,并将剩余的记录再调整成堆,这样又找出了次大的记录,依此类推,直到堆中只有一个记录为止。最好、最坏、平均的时间复杂度都是 O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)
  10. 二路归并排序的基本思想是:将若干个有序序列进行两两归并,直至所有待排记录都在一个有序序列为止。最好、最坏、平均的时间复杂度都是 O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)
  11. 桶式排序的基本思想是:假设待排序记录的值都在 0 ∼ m − 1 0\sim m-1 0m1之间,设置 m m m个桶,首先将值为 i i i的记录分配到第 i i i个桶中,然后再将各个桶中的记录依次收集起来。桶式排序的时间复杂度为 O ( n + m ) O(n+m) O(n+m),空间复杂度是 O ( m ) O(m) O(m),用来存储 m m m个静态队列表示的桶。
  12. 对多关键码进行排序可以有两种基本的方法:最主位优先MSD和最次位优先LSD。
  13. 基数排序的基本思想是:将关键码看成由若干个子关键码复合而成,然后借助分配和收集操作采用LSD方法进行排序。假设待排序记录的关键码由 d d d个子关键码复合而成,每个子关键码的取值范围为 m m m个,则基数排序的时间复杂度为 O ( d ( n + m ) ) O(d(n+m)) O(d(n+m)),空间复杂度为 O ( m ) O(m) O(m),用来存放 m m m个队列。
    14.直接插入排序、起泡排序和归并排序是稳定的排序方法;希尔排序、快速排序、简单选择排序和堆排序是不稳定的排序方法。

练习

概述
如果某种排序算法是不稳定的,则该排序方法没有实际应用价值。(×)
排序算法只能在顺序存储结构上进行,链接存储无法对记录进行排序。(×)
所有排序算法在排序过程中的基本操作都是比较和移动。(×)
排序码是排序的依据,排序码通常是关键码。(√)
如果待排序记录序列是正序,则无须进行排序操作。(×)

直接插入排序
直接插入排序的主要操作是将元素插入到某个子序列中。(×)
无论待排序序列的初始状态如何,直接插入排序都会执行n-1趟。(√)
直接插入排序在最好情况下需要较少的比较次数和移动次数,时间复杂度是 O ( n 2 ) O(n^2) O(n2)。(×)
对于直接插入排序,最后一趟有可能改变每个元素的存储位置。(√)
对于待排序记录序列{12,25,18,15,10},给出直接插入每一趟的结果。
初始序列12,25,18,15,10
第1趟结果12,25,18,15,10
第2趟结果12,18,25,15,10
第3趟结果12,15,18,25,10
第4趟结果10,12,15,18,25
直接插入排序算法如下,请在横线处填入适当的语句或表达式。

void InsertSort(int data[],int n)
{
	int i,j,temp; 
	for(i=1;i<n;i++)
	{
		temp=data[i]; //暂存待插记录
		j=i-1; 
		while(____①____)//寻找插入位置
		{
			data[j+1]=data[j]; 
			j--;			
		}
		____②____
	}
}

j>=0&&temp<data[j]

data[j+1]=temp;

希尔排序
希尔排序将待排序序列逐段分割成若干个子序列,在子序列内部分别进行直接插入排序。(×)
在希尔排序过程中,每一趟待排序的子序列长度逐渐增大。(√)
希尔排序最后一趟的增量是(A)。
A.1 B.2 C.3 D.不一定
希尔排序本质上属于插入排序,插入最后一个记录时,可能会改变所有元素的存储位置。(×)
希尔排序是不稳定的排序方法。(√)
对于待排序记录序列{25,18,30,20,15,12,18,45,35,10},给定增量为4、2、1,写出希尔排序每一趟的结果。
初始序列25,18,30,20,15,12,18,45,35,10
第1趟结果15,10,18,20,25,12,30,45,35,18
第2趟结果15,10,18,12,25,18,30,20,35,45
第3趟结果10,12,15,18,18,20,25,30,35,45

冒泡排序
在起泡排序过程中,交换记录在相邻单元中进行。(√)
起泡排序在最好情况下,没有发生交换记录的操作。(√)
每一趟起泡排序只能确定一个记录的最终位置。(×)
对于待排序序列{5,4,3,2,1},起泡排序移动记录的次数是(D)。
A.10 B.15 C.20 D.30
对于待排序记录序列(30,25,10,12,15,20,35},写出起泡排序每一趟的结果。
初始序列30,25,10,12,15,20,35
第1趟结果25,10,12,15,20,30,35
第2趟结果10,12,15,20,25,30,35
第3趟结果10,12,15,20,25,30,35
起泡排序算法如下,请在横线处填入适当的语句或表达式。

void BubbleSort(int data[],int n)
{
	int j,exchange,bound,temp; 
	exchange=n-1; 
	while(____①____)
	{
		____②____
		exchange=0; 
		for(j=0;j<bound;j++)
		if(data[j]>data[j+1])
		{
			temp=data[jl;
			data[j]=data[j+1];
			data[j+1]=temp; 
			exchange=j;
		}
	}
}

exchange!=0

bound=exchange;

快速排序
对n个记录的集合进行快速排序,所需要的辅助空间是 O ( n ) O(n) O(n)。(×)
快速排序每次只能确定一个记录的最终位置,因而时间性能较低。(×)
当轴值是(C)时,快速排序达到最好情况。
A.第一个记录
B.随机选取
C.区间中值
D.最后一个记录
快速排序一次划分的时间复杂度是 O ( n ) O(n) O(n)。(√)
在快速排序的一次划分算法中,记录的比较次数取决于待排序的初始状态。(×)
对于待排序记录序列{20,15,25,18,20*,12,30},写出一次划分的过程。
以元素20为轴值,一次划分过程如下:
初始序列20,15,25,18,20*,12,30
第1次交换12,15,25,18,20*,20,30
第2次交换12,15,20,18,20*,25,30
第3次交换12,15,18,20,20*,25,30
以下为快速排序的递归算法,请在横线处填入适当的语句或表达式。

void QuickSort(int data[],____①____)
{
	if(____②____)//递归结束条件
		return; 
	else
	{
		int pivot=Partition(data,first,last); //一次划分
		QuickSort(data,first,pivot-1);//对左侧子序列进行快速排序
		____③____//对右侧子序列进行快速排序
	}
}

int first,int last

first>=last

QuickSort(pivot+1,last);

简单选择排序
每一趟简单选择排序只能确定一个记录的最终位置。(√)
无论待排序序列的初始状态如何,简单选择排序都执行n-1趟。(√)
简单选择排序是稳定的排序方法。(×)
相对于其他基于比较的内排序,简单选择排序记录的比较次数较多,但是移动次数较少。(√)
对于待排序记录序列{25,,30,18,10,15,35},给出简单选择每一趟的结果。
初始序列25,15,10,18,35
第1趟结果10,15,25,18,35
第2趟结果10,15,25,18,35
第3趟结果10,15,18,25,35
第4趟结果10,15,18,25,35
简单排序算法如下,请在横线处填入适当的语句或表达式。

void SelectSort(int data[],int n)
{
	int i,j,index,temp; 
	for(i=0;i<n-1;i++)
	{
		____①____
		for(j=i+1;j<n;j++)
			if (data[j]<data[index])
				____②____
		temp=data[il;
		data[i]=data[index];
		data[index]=temp;
}

index=i;

index=j;

堆排序
设有键值序列 ( k 1 , k 2 , . . . , k n (k_1,k_2,...,k_n (k1,k2,...,kn,当 i > n / 2 i>n/2 i>n/2时,任何一个子序列 ( k i , k i + 1 , . . . , k n (k_i,k_{i+1},...,k_n (ki,ki+1,...,kn一定是堆。(√)
在大根堆中,最小值结点一定是叶子结点。(√)A.
在大根堆中,某结点的值一定大于其所有子孙结点的值。(√)
堆排序执行的趟数取决于待排序序列的初始状态。(×)
堆排序所需的时间与待排序的记录个数无关。(×)
堆排序将整个待排序序列划分为无序区和有序区,每一趟的关键是将无序区调整为堆。(√)
对于堆排序算法,初始建堆的时间性能是(C)。
A. O ( n ) O(n) O(n)
B. O ( n 2 ) O(n^2) O(n2)
C. O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)
D. O ( log ⁡ 2 n ) O(\log_2n) O(log2n)
对于待排序记录序列{10,25,15,18,35,20,30,12,20*},写出初始建堆的结果(写出结果序列并画出大根堆)。
初始建堆的结果是{35,25,30,20*,10,20,15,12,18}
在这里插入图片描述

将待排序序列对应的完全二叉树存储到data[0]~data[n-1]中,假设除根结点外均满足大根堆的条件,调整根结点的算法如下,请在横线处填入适当的语句或表达式。

void Sift(int data[],int n)
{
	inti,j,temp; 
	i=0;
	j=1;
	while(____①____)//还没有进行到叶子
	{
		if(j<n-1 && data[j]<data[j+1])
			j++;
		if(____②____)
			break;//已经是堆
		else
		{
			temp=data[i];
			data[i]=data[j];
			data[j]=temp; 
			i=j;
			____③____
		}
	}
}

j<n

data[i]>data[j]

j=2*i+1;

归并排序
合并两个长度为n的有序子序列,时间复杂度是 O ( n ) O(n) O(n),空间复杂度是 O ( 1 ) O(1) O(1)。(×)
归并排序执行的趟数与待排序序列的初始状态无关。(√)
在一趟二路归并排序过程中,需要两两合并相邻的有序子序列,合并有(C)种情况。
A.1 B.2 C.3 D.不确定
二路归并排序的时间性能较好,是不稳定的排序算法。(×)
对于待排序记录序列{25,10,8,20,35,15},写出二路归并排序每一趟的结果。
初始序列25,10,8,20,35,15
第1趟结果10,25,8,20,15,35
第2趟结果8,10,20,25,15,35
第3趟结果8,10,15,20,25,35
设数组data[n]的前h个元素和后n-h个元素分别有序,合并两个子序列的算法如下,请在横线处填入适当的语句或表达式。

void Merge(int data[],int h,int n)
{
	int *temp=new int[n]; //数组temp作为合并的辅助空间
	int i,j,k; 
	i=0;
	j=h;
	k=0; 
	while(____①____)
	{
		if (data[i]<=data[jl)
			temp[k++]=data[i++]; 
		else 
			____②____
	}
	while(i<h)
		temp[k++]=data[i++]; 
	while(j<n)
		temp[k++]=data[j++]; 
	for(i=0;i<n;i++)
		____③____
	delete[] temp;
}

i<h&&j<n

temp[k++]=data[j++];

data[i]=temp[i];

排序方法比较
如果待排序记录个数较多且随机排列,应该采用(C)方法。
A.直接插入排序
B.起泡排序
C.快速排序
D.堆排序
如果待排序记录个数较多,有些情况下是基本有序,应该采用(D)方法。
A.归并排序
B.起泡排序
C.快速排序
D.堆排序
如果不断产生待排序记录,随时需要当前记录集合的排序结果,应该采用(A)方法。
A.直接插入排序
B.起泡排序
C.快速排序
D.堆排序
如果待排序序列每个记录的存储量很大,不应该采用(D)方法。
A.直接插入排序
B.起泡排序
C.快速排序
D.简单选择排序
如果待排序记录个数不多且基本有序,应该采用(B)方法。
A.直接插入排序
B.起泡排序
C.快速排序
D.堆排序

参考资料:《数据结构(从概念到C++实现)》清华大学出版社,王红梅

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oldmao_2000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值