8.3交换排序

一、单项选择题
01,对n个不同的元素利用冒泡法从小到大排序,在()情况下元素交换的次数最多。
A.从大到小排列好的
B.从小到大排列好的
C.元素无序
D.元素基本有序
01.A 通常情况下,冒泡排序最少进行1次冒泡,最多进行n-1次冒泡。初始序列为逆序时,需进行n-1次冒泡,并且需要交换的次数最多。初始序列为正序时,进行1次冒泡(无交换)就可以终止算法。

02.若用冒泡排序算法对序列{10,14,26,29,41,52}从大到小排序,则需进行()次比较。
A.3
B.10
C.15
D.25
02.C 冒泡排序始终在调整“逆序”,因此交换次数为排列中逆序的个数。对逆序序列进行冒泡排序,每个元素向后调整时都需要进行比较,因此共需要比较5+4+3+2+1=15次。

03.用某种排序方法对线性表{25,84,21,47, 15, 27,68,35,20}进行排序时,元素序列的变化情况如下:
1)25,84, 21,47,15,27,68,35,20
2)20,15,21,25,47,27,68,35,84
3)15,20,21,25,35, 27,47,68,84
4)15,20,21,25, 27.35,47,68,84
则所采用的排序方法是()。
A.选择排序
B.插入排序
C.2路归并排序
D.快速排序
03.D 选择排序在每趟结束后可以确定一个元素的最终位置,不对。插入排序,第i趟后前i+1个元素应该是有序的,不对。第2趟{20,15}和{21,25}是反序的,因此不是归并排序。快速排序每趟都将基准元素放在其最终位置,然后以它为基准将序列划分为两个子序列。观察题中的排序过程,可知是快速排序。

04.一组记录的关键码为(46, 79,56,38,40,84),则利用快速排序的方法,以第一个记录为基准,从小到大得到的一次划分结果为()。
A.(38,40,46,56, 79, 84)
B.(40,38,46,79, 56,84)
C.(40,38,46,56, 79,84)
D.(40,38,46,84,56,79)
04.C 以46为基准元素,首先从后向前扫描比46小的元素,并与之进行交换,而后从前向后扫描比46大的元素并将46与该元素交换,得到(40,46,56, 38, 79,84)。此后,继续重复从后向前扫描与从前往后扫描的操作,直到46处于最终位置,答案选择选项C.

05.快速排序算法在()情况下最不利于发挥其长处。
A.要排序的数据量太大
B.要排序的数据中含有多个相同值
C.要排序的数据个数为奇数
D.要排序的数据已基本有序
05.D 当待排序数据为基本有序时,每次选取第n个元素为基准,会导致划分区间分配不均匀,不利于发挥快速排序算法的优势。相反,当待排序数据分布较为随机时,基准元素能将序列划分为两个长度大致相等的序列,这时才能发挥快速排序的优势。

06.就平均性能而言,目前最好的内部排序方法是().
A.冒泡排序
B.直接插入排序
C.希尔排序
D.快速排序
06.D 这里问的是平均性能,选项A、B的平均性能都会达到O(n^2),而希尔排序虽然大大降低了直接插入排序的时间复杂度,但其平均性能不如快速排序。另外,虽然众多排序算法的平均时间复杂度也是O(nlog2n),但快速排序算法的常数因子是最小的。

07.数据序列F={2, 1,4,9,8, 10, 6,20}只能是下列排序算法中的()两趟排序后的结果。
A.快速排序
B.冒泡排序
C.选择排序
D.插入排序
07.A 若为插入排序,则前三个元素应该是有序的,显然不对。而冒泡排序和选择排序经过两趟排序后应该有两个元素处于最终位置(最左/右端),无论是按从小到大还是从大到小排序,数据序列中都没有两个满足这样的条件的元素,因此只可能选择选项A。
[另解]先写出排好序的序列,并和题中的序列做对比。
题中序列:2 1 4 9 8 10 6 20
已排好序序列:1 2 4 6 8 9 10 20
在已排好序的序列中,与题中序列相同元素的有4、8和20,最左和最右两个元素与题中的序列不同,故不可能是冒泡排序、选择排序或插入排序.

08.对数据序列{8,9,10,4,5, 6,20,1,2}采用冒泡排序(从后向前次序进行,要求升序),需要进行的趟数至少是().
A.3
B.4
C.5
D.8
08.C 从后向前“冒泡”的过程为,第1趟{1,8,9, 10, 4, 5, 6, 20,2},第2趟{1,2, 8, 9, 10, 4, 5,6, 20},第3趟{1,2,4, 8, 9, 10,5, 6, 20},第4趙{1,2,4,5,8, 9, 10, 6, 20},第5 趟{1,2,4,5,6,8,9,10,20}.经过第5趟冒泡后,序列已经全局有序,故选择选项C。实际每趟冒泡发生交换后可以判断是否会导致新的逆序对,如果不会产生,则本趟冒泡之后序列全局有序,所以最少5趟即可。

09.对下列关键字序列用快排进行排序时,速度最快的情形是(),速度最慢的情形是().
A.{21,25,5,17,9,23,30}
B.{25,23,30,17, 21,5,9}
C.{21,9,17,30,25,23,5}
D.{5,9,17,21,23,25,30}
09、A、D 当每次的枢轴都把表等分为长度相近的两个子表时,速度是最快的;当表本身已经有序或逆序时,速度最慢。选项D中的序列已按关键字排好序,因此它是最慢的,而选项A中第一趟枢轴值21将表划分为两个子表{9,17,5}和{25,23,30},而后对两个子表划分时,枢轴值再次将它们等分,所以该序列是快速排序最优的情况,速度最快。其他选项可以类似分析。

10.对下列4个序列,以第一个关键字为基准用快速排序算法进行排序,在第一趟过程中移动记录次数最多的是().
A.92,96,88,42,30,35,110,100
B.92.96,100,110,42,35,30,88
C.100,96,92,35,30,110,88,42
D.42,30, 35,92,100,96,88.110
10.B 对各序列分别执行一趟快速排序,可做如下分析(以选项A为例);由于枢轴值为92,因此35移动到第一个位置,96移动到第六个位置,30移动到第二个位置,再将枢轴值移动到30所在的单元,即第五个位置,所以选项A中序列移动的次数为4。同样,可以分析出选项B中序列的移动次数为8,选项C中序列的移动次数为4,选项D中序列的移动次数为2.

11.下列序列中,()可能是执行第一趟快速排序后所得到的序列,
L.{68,11,18,69,23,93,73}
IL. {68,11,69, 23,18, 93,73}
IIl.{93, 73, 68, 11, 69, 23,18}
IV.{68, 11,69,23,18,73,93)
A.I、IV
B.Ⅱ、III
C.II、IV
D.只有IV
11.C 显然,若按从小到大排序,则最终有序的序列是{11, 18,23, 68, 69, 73, 93};若按从大到小排序,则最终有序的序列是{93,73,69,68,23,18,11}。对比可知选项I、Ⅱ中没有处于最终位置的元素,故选项I、Ⅱ都不可能。选项III中73和93处于从大到小排序后的最终位置,而且73将序列分割成大于73和小于73的两部分,故选项III是有可能的。选项IV中73和93处于从小到大排列后的最终位置,73也将序列分割成大于73和小于73的两部分。

12.对n个关键字进行快速排序,最大递归深度为(),最小递归深度为()
A,1
B,n
C,log2n
D.,nlog2n
12.B、C 快速排序过程构成一个递归树,递归深度即递归树的高度。枢轴值每次都将子表等分时,递归树的高为log2n;枢轴值每次都是子表的最大值或最小值时,递归树退化为单链表,树高为n。

13.【2010统考真题]对一组数据(2,12,16,88,5,10)进行排序,若前3趟排序结果如下:
第一趟排序结果:2,12,16,5,10,88
第二趟排序结果:2,12,5,10, 16,88
第三趟排序结果:2,5,10,12,16.88
则采用的排序方法可能是().
A.冒泡排序
B.希尔排序
C.归并排序
D.基数排序
13.A 分别用其他3种排序算法执行数据,归并排序第一趟后的结果为(2,12,16,88,5,10),基数排序第一趟后的结果为(10,2,12,5,16,88),希尔排序显然是不符合的。只有冒泡排序符合条件。
[另解]由题千可以看出每趟都产生一个最大的数排在后面,可直接定位冒泡排序。

14.【2010统考真题]采用递归方式对顺序表进行快速排序。下列关于递归次数的叙述中,
正确的是().
A.递归次数与初始数据的排列次序无关
B.每次划分后,先处理较长的分区可以减少递归次数
C.每次划分后,先处理较短的分区可以减少递归次数
D.递归次数与每次划分后得到的分区的处理顺序无关
14.D 递归次数与各元素的初始排列有关。若每次划分后分区比较平衡,则递归次数少;若分区不平衡,递归次数多。递归次数与处理顺序是没有关系的。

15.【2011统考真题]为实现快速排序算法,待排序序列宜采用的存储方式是().
A.顺序存储
B.散列存储
C.链式存储
D.索引存储
15.A 绝大部分内部排序只适用于顺序存储结构。快速排序在排序的过程中,既要从后向前查找,又要从前向后查找,因此宜采用顺序存储。

16.【2014统考真题]下列选项中,不可能是快速排序第2趟排序结果的是()
A.2, 3, 5,4, 6, 7,9
B.2, 7,5, 6, 4, 3,9
C.3, 2, 5,4,7, 6,9
D.4, 2,3, 5, 7,6,9
16.C 快排的阶段性排序结果的特点是,第i趟完成时,会有i个以上的数出现在它最终将要出现的位置,即它左边的数都比它小,它右边的数都比它大。题目问第二趟排序的结果,即要找不存在两个这样的数的选项。选项A中2,3,6,7,9均符合,所以选项A排除:选项B中,2,9均符合,所以选项B排除:选项D中5,9均符合,所以选项D排除;最后看选项C,只有9一个数符合,所以选项C不可能是快速排序第二趟的结果。

17.【2019统考真题]排序过程中,对尚未确定最终位置的所有元素进行一遍处理称为一“趟”,下列序列中,不可能是快速排序第二趟结果的是().
A.5,2, 16,12,28,60,32,72
B.2,16,5,28,12,60,32,72
C.2,12,16,5,28, 32,72,60
D.5,2,12,28,16,32,72,60
17.D 要清晰掌握排序过程中“趟”的含义,题干也进行了解释一一对尚未确定最终位置的所有元素都处理一遍才是一趟,所以此时要对前后两块子表各做一次快速排序才是一“趟”,如果只对一块子表进行了排序,而未处理另一块子表,就不能算是完整的一趟。选项A,第一趟匹配72,只余一块无序序列,第二趟匹配28、选项A可能。选项B,第一趟匹配2,第二趟匹配72,B可能。选项C,第一趟匹配2,第二趟匹配28或32,选项C可能。选项D,无论是先匹配12还是先匹配32,都会将序列分成两块,那么第二趟必须有两个元素匹配,所以选项D不可能。

二、综合应用题
01.在使用非递归方法实现快速排序时,通常要利用一个栈记忆待排序区间的两个端点。能否用队列来实现这个栈?为什么?
01.[解答] 可以用队列来代替栈。在快速排序的过程中,通过一趟划分,可以把一个待排序区间分为两个子区间,然后分别对这两个子区间施行同样的划分。栈的作用是在处理一个子区间时,保存另一个子区间的上界和下界(排序过程中可能产生新的左、右子区间),待该区间处理完后再从栈中取出另一子区间的边界,对其进行处理。这个功能用队列也可以实现,只不过处理子区间的顺序有所变动而已。

02.编写双向冒泡排序算法,在正反两个方向交替进行扫描,即第一趟把关键字最大的元素放在序列的最后面,第二趟把关键字最小的元素放在序列的最前面,如此反复进行。

02.[解答]
这种排序方法又称双向起泡。奇数趟时,从前向后比较相邻元素的关键字,遇到逆序即交换,直到把序列中关键字最大的元素移动到序列尾部。偶数趟时,从后往前比较相邻元素的关键字,遇到逆序即交换,直到把序列中关键字最小的元素移动到序列前端。

程序代码如下:
void BubbleSort (ElemType A[],int n){
//双向起泡排序,交替进行正反两个方向的起泡过程
int low=0,high=n-1;
bool flag=true;
//一趟冒泡后记录元素是否交换标志
while(low<high&&flag){//循环跳出条件,当flag为false说明已没有逆序
flag=false;
//每趟初始置flag为false
for(i=low;i<high;i++)
//从前向后起泡
if(A[i]>A [i+1]){
//发生逆序
swap(A[i],A[i+1]);//交换
flag=true;
//置flag
high--;
//更新上界
for(i=high;i>low;i--)
//从后往前起泡
if(A[i]<A[i-1]){
//发生逆序
swap(A[i],A[i-1]);//交换
flag=true;
//置flag
}
low++;
//修改下界
}
}

03.已知线性表按顺序存储,且每个元素都是不相同的整数型元素,设计把所有奇数移动到所有偶数前边的算法(要求时间最少,辅助空间最少)。
03.【解答]本题可采用基于快速排序的划分思想来设计算法,只需遍历一次即可,其时间复杂度为0(n),空间复杂度为0(1)。假设表为L[1..n],基本思想是:先从前向后找到一个偶数元素L(i),再从后向前找到一个奇数元素L(j),将二者交换;重复上述过程直到i大于j。

算法的实现如下:
void move (ElemType All,int len) {
//对表A按奇偶进行一趟划分
int i-0,j=len-1;
//i表示左端偶数元素的下标;j表示右端奇数元素的下标
while(i<j){
while(i<j&&A[i]*2!=0) i++;//从前向后找到一个偶数元素
while(i<j&&A[j]%2!=1) j--; //从后向前找到一个奇数元素
if(i<j){
Swap (A[i],A[j]);
//交换这两个元素
}

04.试重新编写考点精析中的快速排序的划分算法,使之每次选取的枢轴值都是随机地从当前子表中选择的。

04.【解答]
这类题目比较简单,为方便起见,可直接先随机地求出枢轴的下标,然后将枢轴值与A[low]交换,而后的思想就与前面的划分算法一样。算法的实现如下:
int Partition2(ElemType A[],int low,int high){
int rand_Index=low+rand()*(high-low+1):
Swap(A[rand_Index],A[low];
//将枢轴值交换到第一个元素
ElemType pivot=A[low);
//置当前表中的第一个元素为枢轴值
int i=low;
//使得表A[low..i]中的所有元素小于pivot,初始为空表
for(int j=low+1;j<=high;j++}
//从第二个元素开始寻找小于基准的元素
if (A[j]<pivot)
//找到后,交换到前面
swap(A[++i],A[j]);
swap (A[i],A[low]);
//将基准元素插入最终位置
return i;
//返回基准元素的位置
注意:本题代码中的比较方法和考点精析中的两种方法均不相同,请读者仔细模拟各种划分算法的执行步骤,做到真正掌握。对于下一题,读者可以尝试采用这三种比较方法来分别解决。

05.试编写一个算法,使之能够在数组L[1…n]中找出第k小的元素(即从小到大排序后处于第k个位置的元素)。

05.[解答]
显然,本题最直接的做法是用排序算法对数组先进行从小到大的排序,然后直接提取L(k)便得到了第k小元素,但其平均时间复杂度将达到O(nlog2n)以上。此外,还可采用小顶堆的方法,每次堆顶元素都是最小值元素,时间复杂度为O(n + k log2n)。下面介绍一个更精彩的算法,它基于快速排序的划分操作。
这个算法的主要思想如下:从数组L[1...n]中选择枢轴pivot(随机或直接取第一个)进行和快速排序一样的划分操作后,表L[1..n]被划分为L[1...m-1]和L[m+1_n),其中L(m)=pivot。
讨论m与k的大小关系:
1)当m=k时,显然pivot就是所要寻找的元素,直接返回pivot即可。
2)当m<k时,所要寻找的元素一定落在L[m+1....n)中,因此可对L[m+1...n]递归地查找第k-m小的元素。
3)当m>k时,所要寻找的元素一定落在L[1....m-1)中,因此可对L[1...m-1]递归地查找第k小的元素。
该算法的时间复杂度在平均情况下可以达到0(n),而所占空间的复杂度则取决于划分的方法。算法的实现如下:
int kth_elem(int a[],int low,int high,int k)(
int pivot=a[low];
int low_temp=low;
//由于下面会修改low与high,在递归时又要用到它们
int high_temp=high;
while(low<high){
while(low<high&&a[high]>=pivot)
--high;
a[low]=a[high];
while(low<high&óa[low]<=pivot)
++low;
a[high]=a[low];
}
a[low]=pivot;
//上面即为快速排序中的划分算法
//以下就是本算法思想中所述的内容
if (low==k)
//由于与k相同,直接返回pivot元素
return a[low];
else if(low>k)
//在前一部分表中递归寻找
return kth_elem(a,low_temp,low-1,k);
else
//在后一部分表中递归寻找
return kth_elem(a,low+1,high_temp,k);

06.荷兰国旗问题:设有一个仅由红、白、蓝三种颜色的条块组成的条块序列,请编写一个时间复杂度为O(n)的算法,使得这些条块按红,白、蓝的顺序排好,即排成荷兰国旗图案。

06.【解答]
算法思想:顺序扫描线性表,将红色条块交换到线性表的最前面,蓝色条块交换到线性表的最后面。为此,设立三个指针,其中,j为工作指针,表示当前扫描的元素,i以前的元素全部为红色,k以后的元素全部为蓝色。根据j所指示元素的颜色,决定将其交换到序列的前部或尾部。
初始时i=0,k=n-1,算法的实现如下:
typedef  enum{RED,WHITE, BLUE} color; //设置枚举数组
void Flag_Arrange(color a[],int n){
int i=0,j=0,k=n-1;
while (j<=k)
switch(a[j]){  //判断条块的颜色
case   RED: Swap(a[i],a[j]);i++;j++;break;
//红色,则和i交换
case WHITE: j++;break;
case   BLUE: Swap(a[j),a[k]);k--;
//蓝色,则和k交换
//这里没有j++语句以防止交换后a[j]仍为蓝色的情况
}
例如,将元素值正数、负数和零排序为前面都是负数,接着是0,最后是正数,也用同样的方法。思考:为什么case   RED时不用考虑交换后a[j]仍为红色,而case BLUE却需要考虑交换后 alj]仍为蓝色?

07.【2016统考真题】已知由n(n≥2)个正整数构成的集合A={ ak]0≤k<n},将其划分为两个不相交的子集A1和A2,元素个数分别是n1和n2,A1和h2中的元素之和分别为S1和S2。设计一个尽可能高效的划分算法,满足|n1-n2|最小且|S1-S2|最大。要求:
1)给出算法的基本设计思想。
2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
3)说明你所设计算法的平均时间复杂度和空间复杂度。

07.【解答]
1)算法的基本设计思想
由题意知,将最小的  Ln/2」个元素放在A1中,其余的元素放在A2中,分组结果即可满足题目要求。仿照快速排序的思想,基于枢轴将n个整数划分为两个子集。根据划分后枢轴所处的位置
i分别处理:
①若i=Ln/2」,则分组完成,算法结束。
② 若i<Ln2」,则枢轴及之前的所有元素均属于A1,继续对i之后的元素进行划分。
③ 若i>Ln/2」,则枢轴及之后的所有元素均属于A2,继续对i之前的元素进行划分。
基于该设计思想实现的算法,无须对全部元素进行全排序,其平均时间复杂度是0(n),空间复杂度是0(1)。
2)算法实现
int setPartition( int a[], int n){
int pivotkey, low=0,low0=0,high-n-1,high0=n-1,flag=1,k=n/2,i;
int s1=0,s2=0;
while(flag){
pivotkey=a[low];
//选择枢轴
while(low<high){
//基于枢轴对数据进行划分
while(low<high && a[high)>=pivotkey) --high;
if(low!=high) a[low]=a[high];
while(low<high && a[low]<-pivotkey) ++low;
if(low!=high) a[high]=a[low);
}
//end of while(low<high)
a[lowl=pivotkey;
if(low==k-1)
//若枢轴是第n/2小元素,划分成功
flag=0;
else(
//是否继续划分
if(low<k-1){
low0=++low;
high=high0;
}
else(
high0=--high;
low=low0;
}
}
}
for(i-0;i<k;i++) s1+=a[i);
for(i=k;i<n;i++) s2+=a[i];
return s2-sl;
}
3)本答案给出的算法平均时间复杂度是0(n),空间复杂度是0(1)。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值