专题二 简单排序算法
阅读代码,回答下列问题:
#include <iostream>
using namespace std;
const int M = 100;
const int N = 10;
int T[N+1];//桶排序专用数组(元素值为0-N的整数)
void PrintArray(const int A[], int low, int high); //输出数组A中区域[low,high]的元素
void BubbleSort (int A[], int n); //用冒泡排序法对数组A进行排序(非递减序列)
void SelectionSort(int A[], int n); //用选择排序法对数组A进行排序(非递减序列)
void InsertSort(int A[], int n); //用插入排序法对数组A进行排序(非递减序列)
void BucketSort(int A[], int n);//用桶排序法对数组A进行排序(非递减序列)
int main()
{
int A[M] = {2,8,2,1,2,6,5,6};
int B[M] = {2,8,2,1,2,6,5,6};
int C[M] = {2,8,2,1,2,6,5,6};
int D[M] = {2,8,2,1,2,6,5,6};
int n = 8;
BubbleSort(A, n);//冒泡排序算法
PrintArray(A, 0, n-1);
SelectionSort(B, n);//选择排序算法
PrintArray(B, 0, n-1);
InsertSort(C, n);//插入排序算法
PrintArray(C, 0, n-1);
BucketSort(D, n);//桶排序算法
PrintArray(D, 0, n-1);
return 0;
}
void PrintArray(const int A[], int low, int high) //输出数组A中区域[low,high]的元素
{
while (low <= high)
cout << A[low++] << " ";
cout << endl;
}
算法1:用冒泡排序法对数组A进行排序(非递减序列)
void BubbleSort(int A[], int n)
{
int temp;
for (int i=n-1; i>0; i--)
{
for (int j=0; j<i; j++) //通过交换相邻位置元素,进行冒泡操作
{
if (A[j] > A[j+1])
{
temp = A[j];
A[j] = A[j+1];
A[j+1] = temp;
}
}
}
}
问题1:写出每轮冒泡后的数组序列。
问题2:算法1是每次对A[0..i]部分进行冒泡排序,并将最大值存储到最右侧A[i]中,能否转换一下思路:每次将最小值冒泡到最左侧?试写出修改后的代码。
问题3:通过问题1,我们注意到在第3轮的时候就已经排好序了,第4-7轮没有做任何交换操作,实际是没有必要执行的。能否对算法1进行改进,一旦排序完成则结束循环,无需再进行下一轮冒泡?试写出改进后的代码。
答案:
问题1:第1轮:2 2 1 2 6 5 6 8; 第2轮:2 1 2 2 5 6 6 8; 第3轮:1 2 2 2 5 6 6 8;
第4轮:1 2 2 2 5 6 6 8; 第5轮:1 2 2 2 5 6 6 8; 第6轮:1 2 2 2 5 6 6 8;
第7轮:1 2 2 2 5 6 6 8;
问题2:代码如下:
void BubbleSort_0(int A[], int n)//冒泡排序算法0:逆序扫描,将最小值放到下方
{
int temp;
for (int i=1; i<n; i++)
{
for (int j=n-1; j>=i; j--)
{
if (A[j] < A[j-1])
{
temp = A[j]; A[j] = A[j-1]; A[j-1] = temp;
}
}
}
}
问题3:一种思路:设置交换操作标志。
void BubbleSort_1(int A[], int n)//冒泡排序改进算法1:设置交换操作标志
{
bool swapFlag = 1; //交换标志用来判断内层循环中是否有交换操作
int temp;
for (int i=n-1; swapFlag && i>0; i--)
{
swapFlag = 0; //先假设未做交换操作
for (int j=0; j<i; j++)
{
if (A[j] > A[j+1])
{
temp = A[j]; A[j] = A[j+1]; A[j+1] = temp;
swapFlag = 1;//设置交互操作标志
}
}
}
}
另一种思路:将待排序列的上界缩小到最后一次发生交换的位置处。
void BubbleSort_2(int A[], int n) //修改待排序数组的上界为最后一次发生交换操作的位置
{
int swapPos;//记录最后一次发生交换操作的位置
int temp;
for (int i=n-1; i>0; i=swapPos)//待排序部分数组的上边界,即lib[0..i]为待排序部分
{
swapPos = 0; //先假设最后一次发生交换操作的位置为0
for (int j=0; j<i; j++)
{
if (A[j] > A[j+1])
{
temp = A[j]; A[j] = A[j+1]; A[j+1] = temp;
swapPos = j;
}
}
}
}
算法2:用选择排序法对数组A进行排序(非递减序列)
void SelectionSort(int A[], int n)
{
int p;
int temp;
for (int i=0; i<n-1; i++)
{
p = i;
for (int j=i+1; j<n; j++)
{
if (A[j] < A[p])
p = j;
}
if (p != i)
{
//语句块1
}
}
}
问题4:写出每轮选择后的数组序列。
问题5:将语句块1填写完整(注意是语句块,可能不止一条语句)。
答案:
问题4:第1轮:1 8 2 2 2 6 5 6; 第2轮:1 2 8 2 2 6 5 6; 第3轮:1 2 2 8 2 6 5 6;
第4轮:1 2 2 2 8 6 5 6; 第5轮:1 2 2 2 5 6 8 6; 第6轮:1 2 2 2 5 6 8 6;
第7轮:1 2 2 2 5 6 6 8;
问题5:{
temp = A[p];
A[p] = A[i];
A[i] = temp;
}
算法3:用插入排序法对数组A进行排序(非递减序列)
void InsertSort(int A[], int n)
{
int p;
int temp;
for (int i=1; i<n; i++)
{
temp = A[i];
for (p=i-1; p>=0; p--) //语句1
{
if (A[p] > temp)
A[p+1] = A[p];
else
break;
}
A[p+1] = temp;
}
}
问题6:写出每轮插入后的数组序列。
问题7:算法3中语句1及其所在循环体的作用是逆序扫描A[0..i],把比temp大的元素右移1位,直到A[p] <= temp为止。注意到A[0..i-1]部分是有序排列的,能否设计更高效的算法实现插入排序?
答案:
问题6:第1轮:2 8 2 1 2 6 5 6; 第2轮:2 2 8 1 2 6 5 6; 第3轮:1 2 2 8 2 6 5 6;
第4轮:1 2 2 2 8 6 5 6; 第5轮:1 2 2 2 6 8 5 6; 第6轮:1 2 2 2 5 6 8 6;
第7轮:1 2 2 2 5 6 6 8;
问题7:有2种改进思路:
void InsertSort_1(int A[], int n)//插入排序改进算法1:二分插入
{
int low, high, mid;
int temp; //用来存储待插入元素的临时变量
for (int i=1; i<n; i++) //A[0..i-1]为已排序部分,将A[i]插入到A[0..i-1]中
{
temp = A[i];
low = 0;
high = i-1;
while(low <= high) //二分查找插入位置
{
mid = (low + high)/2;
if (A[mid] > temp)
high = mid - 1;
else
low = mid + 1;
}
for (int j=i; j>low; j--)//移动元素以腾出插入位置
A[j] = A[j-1];
A[low] = temp;
}
}
void InsertSort_2(int A[], int n)//插入排序改进算法2:希尔排序
{
int p; //用来存储插入位置的临时变量
int temp; //用来交换数组元素值的临时变量
for (int gap=n/2; gap>0; gap/=2)//元素跳跃的步长不断减少,步长为1时即为直接插入排序
{
for(int i=gap; i<n; i++)//将A[i]插入到A[0..i-1]中
{
temp = A[i];
for (p=i; p>=gap; p-=gap)//向后跳跃移动元素以腾出插入位置,跳跃距离为gap
{
if (A[p-gap] > temp)
A[p] = A[p-gap];
else
break;
}
A[p] = temp;
}
}
}
算法4:用桶排序法对数组A进行排序(非递减序列)
void BucketSort(int A[], int n)
{
for (int i=0; i<n; i++)
{
T[A[i]]++;
}
for (int i=0,t=0; i<=N; i++)//语句1
{
for (int j=T[i]; j>0; j--)
A[t++] = i;
}
}
问题8:语句1中有一个全局常量N,请问其含义是什么?
问题9:桶排序能否对元素值大小范围为[-100,100]的整数数组排序?如果元素值是负数呢?
答案:
问题8:N代表元素值的最大值,桶排序只能对元素值大小为[0,N]内的整型数组排序。
问题9:将桶的大小做些修改,就能存储负整数,对范围为[-100,100]的整数数组,可设置桶的大小N=200,将数组元素值加上100后再装入桶中,取出元素时再减去100即可。如果元素值是小数,可先将其转化为整数,再装入桶中。
课后练习:
练习1:02_奇数单增序列
描述:给定一个长度为N(不大于500)的正整数序列,请将其中的所有奇数取出,并按升序输出。
输入
共2行:第1行为 N;第2行为 N 个正整数,其间用空格间隔。
输出
增序输出的奇数序列,数据之间以逗号间隔。数据保证至少有一个奇数。
样例输入
10
1 3 2 6 5 4 9 8 7 10
样例输出
1,3,5,7,9
练习2:08_病人排队
描述:病人登记看病,编写一个程序,将登记的病人按照以下原则排出看病的先后顺序:
1. 老年人(年龄 >= 60岁)比非老年人优先看病。
2. 老年人按年龄从大到小的顺序看病,年龄相同的按登记的先后顺序排序。
3. 非老年人按登记的先后顺序看病。
输入
第1行,输入一个小于100的正整数,表示病人的个数;后面按照病人登记的先后顺序,每行输入一个病人的信息,包括:一个长度小于10的字符串表示病人的ID(每个病人的ID各不相同且只含数字和字母),一个整数表示病人的年龄,中间用单个空格隔开。
输出
按排好的看病顺序输出病人的ID,每行一个。
样例输入
5
021075 40
004003 15
010158 67
021033 75
102012 30
样例输出
021033
010158
021075
004003
102012
练习3:06_整数奇偶排序
描述:给定10个整数的序列,要求对其重新排序。排序要求:
1.奇数在前,偶数在后;
2.奇数按从大到小排序;
3.偶数按从小到大排序。
输入
输入一行,包含10个整数,彼此以一个空格分开,每个整数的范围是大于等于0,小于等于100。
输出
按照要求排序后输出一行,包含排序后的10个整数,数与数之间以一个空格分开。
样例输入
4 7 3 13 11 12 0 47 34 98
样例输出
47 13 11 7 3 0 4 12 34 98
练习4:09_明明的随机数
描述:明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。
输入
有2行,第1行为1个正整数,表示所生成的随机数的个数:N;
第2行有N个用空格隔开的正整数,为所产生的随机数。
输出
也是2行,第1行为1个正整数M,表示不相同的随机数的个数。第2行为M个用空格隔开的正整数,为从小到大排好序的不相同的随机数。
样例输入
10
20 40 32 67 40 20 89 300 400 15
样例输出
8
15 20 32 40 67 89 300 400
练习5:09_直方图
描述:给定一个非负整数数组,统计里面每一个数的出现次数。我们只统计到数组里最大的数。假设 Fmax (Fmax < 10000)是数组里最大的数,那么我们只统计 {0,1,2.....Fmax} 里每个数出现的次数。
输入
第一行n是数组的大小。1 <= n <= 10000。紧接着一行是数组的n个元素。
输出
按顺序输出每个数的出现次数,一行一个数。如果没有出现过,则输出0。
对于例子中的数组,最大的数是3,因此我们只统计{0,1,2,3}的出现频数。
样例输入
5
1 1 2 3 1
样例输出
0
3
1
1
练习6:29_统计字符数
描述:给定一个由a-z这26个字符组成的字符串,统计其中哪个字符出现的次数最多。
输入
输入包含一行,一个字符串,长度不超过1000。
输出
输出一行,包括出现次数最多的字符和该字符出现的次数,中间以一个空格分开。如果有多个字符出现的次数相同且最多,那么输出ascii码最小的那一个字符。
样例输入
abbccc
样例输出
c 3
练习7:06_校门外的树
描述:某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。
我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数轴上的每个整数点,即0,1,2,……,L,都种有一棵树。
由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。
输入
第一行有两个整数L(1 <= L <= 10000)和 M(1 <= M <= 100),L代表马路的长度,M代表区域的数目,L和M之间用一个空格隔开。
接下来的M行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点和终止点的坐标。
对于20%的数据,区域之间没有重合的部分;
对于其它的数据,区域之间有重合的情况。
输出
包括一行,这一行只包含一个整数,表示马路上剩余的树的数目。
样例输入
500 3
150 300
100 200
470 471
样例输出
298
练习8:07_合影效果
描述:小云和朋友们去爬香山,为美丽的景色所陶醉,想合影留念。如果他们站成一排,男生全部在左(从拍照者的角度),并按照从矮到高的顺序从左到右排,女生全部在右,并按照从高到矮的顺序从左到右排,请问他们合影的效果是什么样的(所有人的身高都不同)?
输入
第一行是人数n(2 <= n <= 40,且至少有1个男生和1个女生)。
后面紧跟n行,每行输入一个人的性别(男male或女female)和身高(浮点数,单位米),两个数据之间以空格分隔。
输出
n个浮点数,模拟站好队后,拍照者眼中从左到右每个人的身高。每个浮点数需保留到小数点后2位,相邻两个数之间用单个空格隔开。
样例输入
6
male 1.72
male 1.78
female 1.61
male 1.65
female 1.70
female 1.56
样例输出
1.65 1.72 1.78 1.70 1.61 1.56
练习9:07_有趣的跳跃
描述:一个长度为n(n>0)的序列中存在“有趣的跳跃”当前仅当相邻元素的差的绝对值经过排序后正好是从1到(n-1)。
例如,1 4 2 3存在“有趣的跳跃”,因为差的绝对值分别为3,2,1。当然,任何只包含单个元素的序列一定存在“有趣的跳跃”。
你需要写一个程序判定给定序列是否存在“有趣的跳跃”。
输入
一行,第一个数是n(0 < n < 3000),为序列长度,接下来有n个整数,依次为序列中各元素,各元素的绝对值均不超过1,000,000,000。
输出
一行,若该序列存在“有趣的跳跃”,输出"Jolly",否则输出"Not jolly"。
样例输入
4 1 4 2 3
样例输出
Jolly
数组专题讲义之简单排序算法
最新推荐文章于 2022-05-30 11:25:12 发布