数组专题讲义之简单排序算法

专题二 简单排序算法
阅读代码,回答下列问题:
#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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值