按数的大小排
-
内定义数据类型
题目描述:
对输入的n个数进行排序并输出。
输入
输入的第一行包括一个整数n(1<=n<=100)。
接下来的一行包括n个整数。输出
可能有多组测试数据,对于每组数据,将排序后的n个整数输出,每个数后面都有一个空格。
每组测试数据的结果占一行。样例输入
4
1 4 3 2样例输出
1 2 3 4
解法思路
代码
#include <cstdio>
#include <iostream>
#include <algorithm> //使用sort函数时需要引入
using namespace std;
int arr[100];
int main(){
int n;
while(scanf("%d",&n)!=EOF){
//输入序列的元素个数
//遍历输入
for(int i=0;i<n;++i){
scanf("%d",&arr[i]);
}
//排序
sort(arr,arr+n); //数组的名字也代表它起始元素的地址; sort()有三个参数(first,last,comp)
//这里comp省略,默认升序
//遍历输出
for(int i=0;i<n;++i){
printf("%d ",arr[i]); //输出中间有空格:"%d "
}
printf("\n");
}
return 0;
}
-
自定义数据类型(成绩排序)
题目描述:
用一维数组存储学号和成绩,然后,按成绩排序输出。
输入
输入第一行包括一个整数N(1<=N<=100),代表学生的个数。
接下来的N行每行包括两个整数p和q,分别代表每个学生的学号和成绩。输出
按照学生的成绩从小到大进行排序,并将排序后的学生信息打印出来。
如果学生的成绩相同,则按照学号的大小进行从小到大排序。样例输入
3
1 90
2 87
3 92样例输出
2 87
1 90
3 92解法思路
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
struct student{
int number;
int score;
};
student arr[100];
bool compare(student x,student y){
//返回ture的话x在y的前面
if(x.score==y.score){
return x.number<y.number; //同分时:按number升序排
}else{
return x.score<y.score; //不同分时:按score升序排
}
}
//默认的升序compare实现:
//bool compare(int a,int b){
// return a>b; //按默认的升序排序
//}
int main(){
int n;
scanf("%d",&n); //输入元素个数
//遍历输入一维数组
for(int i=0;i<n;++i){
scanf("%d%d",&arr[i].number,&arr[i].score);
}
sort(arr,arr+n,compare); //这里不能默认比较函数,需要按题自定义compare函数
//遍历输出
for(int i=0;i<n;++i){
printf("%d %d\n",arr[i].number,arr[i].score);
}
return 0;
}
还可以使用重载操作符实现:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
//在结构体当中写明,每个student要怎比
struct student{
int number;
int score;
//使用重载操作符的方式
bool operator< (student stu) const {
//“operator<” 相当于函数名,传入一个student类型的参数stu,并将函数设为常函数
if(score==stu.score){
//重载函数里的逻辑和之前的一样
return number<stu.number;
}else{
return score<stu.score;
}
}
};
student arr[100];
int main(){
int n;
scanf("%d",&n); //输入元素个数
//遍历输入一维数组
for(int i=0;i<n;++i){
scanf("%d%d",&arr[i].number,&arr[i].score);
}
sort(arr,arr+n); //在结构体里将“<”重新定义了,所以默认的<就是题目中的比较方法
//遍历输出
for(int i=0;i<n;++i){
printf("%d %d\n",arr[i].number,arr[i].score);
}
return 0;
}
不是按数的大小排
-
整数奇偶排序
题目描述:
输入10个整数,彼此以空格分隔。重新排序以后输出(也按空格分隔),要求: 1.先输出其中的奇数,并按从大到小排列; 2.然后输出其中的偶数,并按从小到大排列。
输入
任意排序的10个整数(0~100),彼此以空格分隔。
输出
可能有多组测试数据,对于每组数据,按照要求排序后输出,由空格分隔。
1. 测试数据可能有很多组,请使用while(cin>>a[0]>>a[1]>>…>>a[9])类似的做法来实现;
2. 输入数据随机,有可能相等。样例输入
4 7 3 13 11 12 0 47 34 98
样例输出
47 13 11 7 3 0 4 12 34 98
解法思路
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int arr[10];
bool compare(int x,int y){
if(x%2==1 && y%2==1){
//x,y都是奇数时,按降序排
return x>y;
}else if(x%2==0 && y%2==0){
return x<y; //x,y都是偶数时,按升序排
}else if(x%2==1 && y%2==0){
return true; //x为奇数,y为偶数时,奇数x在前(需要返回ture) 还可以写成return x%2>y%2;
}else{
return false; //x为偶数,y为奇数时,奇数y在前(需要返回false) 还可以写成return x%2>y%2;
}
}
int main(){
while(scanf("%d",&arr[0])!=EOF){
//已经告诉了元素的个数时:先只输入1个
for(int i=1;i<10;++i){
//之后的9个元素,按循环输入
scanf("%d",&arr[i]);
}
sort(arr,arr+10,compare);
for(int i=0;i<10;++i){
printf("%d ",arr[i]);
}
}
return 0;
}
利用排序算法的特性解题
-
线性排序
题目描述:
给你n个整数,请按从大到小的顺序输出其中前m大的数。
输入
每组测试数据有两行,第一行有两个数n,m (0<n,m<1000000) ,第二行包含n个各不相同,且都处于区间[-500000,500000]的整数(加了限制)。
输出
对每组测试数据按从大到小的顺序输出前m大的数。
样例输入
5 3
3 -35 92 213 -644样例输出
213 92 3
解法思路
已知基于比较的排序算法的时间复杂度的下限为O(nlogn)
那么如何控制排序算法在O(n)内完成排序?
如何不通过比较大小的方法将排序突破O(n):必须加一个限制——给定范围
如:5 3 2 4 3 3 2 7 7 1(所有元素都在0~9)→这样可以保证辅助数组的元素个数
⭐构造一个辅助数组:记录每个记录在序列中的出现次数
之后再线性地扫描一遍辅助数组,即可得到排序代码
#include <cstdio>
#include <iostream>
#include <cstring> //使用了memset
#include <algorithm>
using namespace std;
const int MAXN=1e6 +10;
const int RANGE=5e5;
int arr[MAXN]; //构造存放原序列的数组
int number[MAXN]; //根据每个元素取值范围,构造辅助数组
int main(){
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
//清空辅助数组,不然上次排序的数据残余会对下次的辅助数组统计有影响
memset(number,0,sizeof(number)); //memset(首个元素地址,全都置为0,数组大小)
for(int i=0;i<n;++i){
scanf("%d",&arr[i]);
number[arr[i]+RANGE]++; //每输入一个,辅助数组中对应的加1
//且由于输入的arr[i]的范围是(-5e5,5e5),但辅助数组构造的范围(0,1e6)
//所以将输进来的arr[i]+5e5(整体向右平移5e5)
}
//遍历辅助数组
int index=0;
for(int i=0;i<MAXN;++i){
while(number[i]--){
//当number[i]中的数字不为0时,
arr[index++] = i-RANGE; //将排好的序列输出回原来的数组
}
}
//序列是升序排的,所以从后向前输出m个
for(int i=n-1;i>=n-m;--i){
if(i==n-m){
printf("%d\n",arr[i]); //最后一个输出后加换行
}else{
printf("%d ",arr[i]);
}
}
}
return 0;
}
-
求逆序数对
题目描述:
输入
输出
4
4 2 8 0 3
10 0 1 2 3 4 5 6 7 8 9
6 -42 23 6 28 -100 65537
5 0 0 0 0 0样例输入
4
4 2 8 0 3
10 0 1 2 3 4 5 6 7 8 9
6 -42 23 6 28 -100 65537
5 0 0 0 0 0样例输出
Scenario #1:
3
Scenario #2:
0
Scenario #3:
5
Scenario #4:
0解法思路
原序列:2 8 0 4 3
逆序对:(8,0)(8,4)(8,3)(4,3)
逆序数:4
使用归并排序O(nlogn)处理逆序数问题很高效
如何统计逆序数:
在combine函数中,右边序列中的元素在原序列中一定是在左边序列的后边
当将 工作指针 j 指向的元素放到temp中时,此时在左序列工作指针 i 右边的所有数一定比工作指针 j 指向的元素大,这些元素就与工作指针 j 指向的元素构成逆序对
故在combine函数中添加:
⭐number += middle-i+1; 就统计出了逆序对数代码
//归并排序算法
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int arr[100];
int temp[100];
void Combine(int left,int middle,int right){
//归并函数
int i = left;
int j = middle +1;
int k = left;
while(i<=middle && j<=right){
//i,j分别是左右两个的工作指针
if(arr[i] <= arr[j]){
//将两者小的放到temp辅助数组中
temp[k++] = arr[i++];
}else{
temp[k++] = arr[j++];
}
}
while(i<=middle){
//将两个序列中剩下的全部输入temp
temp[k++] = arr[i++];
}
while(j<=right){
temp[k++] = arr[j++];
}
for(k=left;k<=right;++k){
//将排好的序列复制到原来的数组arr中
arr[k]=temp[k];
}
return;
}
void MergeSort(int left,int right){
//left,right待排序区间
if(left<right){
//只要left<right就递归归并
//int middle = (left+right)/2;
int middle = left + (right-left)/2; //上面的写法如果left+right可能会大于int的上界,从而溢出
MergeSort(left,middle);
MergeSort(middle+1,right);
Combine(left,middle,right);
}
return;
}
int main(){
int n;
scanf("%d",&n); //输入n
for(int i=0;i<n;++i){
//输入所有待排元素
scanf("%d",&arr[i]);
}
MergeSort(0,n-1); //调用归并函数
for(int i=0;i<n;++i){
//将排好的序列全部输出
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
//本题代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN=1010;
int arr[MAXN];
int temp[MAXN];
int number; //统计逆序数
void Combine(int left,int middle,int right){
//归并函数
int i = left;
int j = middle +1;
int k = left;
while(i<=middle && j<=right){
//i,j分别是左右两个的工作指针
if(arr[i] <= arr[j]){
//将两者小的放到temp辅助数组中
temp[k++] = arr[i++];
}else{
number += middle-i+1; //⭐
temp[k++] = arr[j++];
}
}
while(i<=middle){
//将两个序列中剩下的全部输入temp
temp[k++] = arr[i++];
}
while(j<=right){
temp[k++] = arr[j++];
}
for(k=left;k<=right;++k){
//将排好的序列复制到原来的数组arr中
arr[k]=temp[k];
}
return;
}
void MergeSort(int left,int right){
//left,right待排序区间
if(left<right){
//只要left<right就递归归并
//int middle = (left+right)/2;
int middle = left + (right-left)/2; //上面的写法如果left+right可能会大于int的上界,从而溢出
MergeSort(left,middle);
MergeSort(middle+1,right);
Combine(left,middle,right);
}
return;
}
int main(){
int casenum;
scanf("%d",&casenum);
for(int current=1;current<=casenum;++current){
//本题要求输出对应的哪一组,下面需要用到每一个current
int n;
scanf("%d",&n);
for(int i=0;i<n;++i){
//输入所有待排元素
scanf("%d",&arr[i]);
}
number=0;
MergeSort(0,n-1); //调用归并函数
printf("Scenario #%d:\n",current);
printf("%d\n\n",number);
}
return 0;
}
-
第K大的数
题目描述:
输入
输出
样例输入
样例输出
解法思路
利用快排
- 当进行一次划分后,可以确定其中一个元素(枢纽) 的最终位置,并可以得到他的下标,如果此时要求第k大的数,刚好left=k-1,则这个枢纽就是要找的数
- 但是并不是每次都能刚好找到,但是划分好的数列,枢纽左边的数一定比枢纽小,右边的一定比枢纽大
- 所以我们要找的第k大的数,当k-1<left时,第k大一定不可能出现在left右边,就去left 的左边去找,直到找到刚好left=k-1。
代码
//快速排序
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int arr[100];
int Partition(int left,int right){
//一趟划分
int random=left+rand()%(right-left); //找一个随机值作为枢纽
swap(arr[left],arr[random]); //将枢纽放到第一个元素,此时left为枢纽
while(left<right){
//一直交换直到left=right
while(left<right&&arr[left]<=arr[right]){
//此时left指向枢纽,当枢纽比right的数小时
--right; //让right左移
}
swap(arr[left],arr[right]); //直到找到一个right的值小于left(枢纽)的值,则交换
while(left<right&&arr[left]<=arr[right]){
//交换后,此时right指向枢纽,当枢纽比left的数大时
left++; //让left右移
}
swap(arr[left],arr[right]); //直到找到一个left的值大于right(枢纽)的值,则交换
}
return left; //当left=right时停止划分,返回枢纽的下标
}
void Quicksort(int left,int right){
if(left<right){
int position=Partition(left,right); //一次划分
Quicksort(left,position-1); //对划分好的左右,分别快速排序
Quicksort(position+1,right);
}
}
int main(){
int n;
scanf("%d",&n); //两天就把%记成&了?
for(int i=0;i<n;++i){
scanf("%d",&arr[i]);
}
Quicksort(0,n-1);
for(int i=0;i<n;++i){
printf("%d",arr[i]);
}
return 0;
}
求第k大的数(时间复杂度为O(n),快排)
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int arr[100];
int Partition(int left,int right){
int random=left+rand()%(right-left);
swap(arr[left],arr[random]);
while(left<right){
//一直交换直到left=right
while(left<right&&arr[left]>=arr[right]){
//更改递增递减,改此处的<=,>=即可
--right;
}
swap(arr[left],arr[right]);
while(left<right&&arr[left]>=arr[right]){
left++;
}
swap(arr[left],arr[right]);
}
return left;
}
int Quicksort(int left,int right,int k){
if(left<right){
int position=Partition(left,right); //position为划分返回的枢纽下标
if(position==k-1){
//⭐返回的下标刚好等于所求的k-1
return arr[position]; //则直接返回枢纽值
}else if(position<k-1){
//当position<k-1时,第k大只能出现在枢纽的右边
Quicksort(position+1,right,k);
}else {
//当position>k-1时,第k大只能出现在枢纽的左边
Quicksort(left,position-1,k);
}
}
}
int main(){
int n;
scanf("%d",&n); //两天就把%记成&了?
for(int i=0;i<n;++i){
scanf("%d",&arr[i]);
}
int k; //输入k
scanf("%d",&k);
printf("%d",Quicksort(0,n-1,k));
// for(int i=0;i<n;++i){
// printf("%d",arr[i]);
// }
return 0;
}
时间复杂度较大的方法
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int arr[100];
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%d",&arr[i]);
}
int k;
scanf("%d",&k);
sort(arr,arr+n);
printf("%d",arr[k-1]);
return 0;
}