目录
一.排列方法:
说到排序,c++首选sort(),其平均复杂度为O(nlogn),但它不是很稳定的排序方法,而且许多题并不能运用sort()来解决。除了sort()以外,我们常用的还有快速排列和归并排列。
二.快速排列:
1.实现:
例如:有一个序列长度为8的序列a[7]={4,8,5,7,1,3,6,2}。
排列步骤:
1.先处理:选中一个元素放到x这个位置,将比a[x]小的元素全部放到左边,不比a[x]小的元素全部放在右侧。
2.分解问题:
3.问题合并:
当子问题无法分解,也就是只剩一个点的时候,处理结束。因为每个子问题有序,所以原问题也必定有序。
说明:若选中基准数字4,将序列分成两段,在实现过程中先指定一个指针p=1,在寻找比4小的数,如果找到,则指针右移动,且把数字放到当前位置。完成后,交换指针与l的位置即可。
2.代码呈现
一般我们会将快速排列写成函数形式
void qspx(int x,int y){
if(x>y) return;
int l=a[x],r=x;
for(int i=x;i<=y;i++){
if(a[i]>l) swap(a[++r],a[i]);
}
swap(a[x],a[r]);
qspx(x,r-1);
qspx(r+1,y);
}
例题:
给定一个数组,统计前k大的数并且把这k个数从大到小输出。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+3;
int a[N];//根据题目给定的范围来定
void qspx(int x,int y){//快速排序函数
if(x>y) return;
int l=a[x],r=x;
for(int i=x;i<=y;i++){
if(a[i]>l) swap(a[++r],a[i]);
}
swap(a[x],a[r]);
qspx(x,r-1);
qspx(r+1,y);
}
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
qspx(1,n);//从1到n进行排列
int k;cin>>k;
for(int i=1;i<=k;i++){
cout<<a[i]<<endl;
}
return 0;
}
三.归并排列:
归并排序是建立在归并操作上的一种有效,稳定的排列算法,该算法是采用的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
例如:有一个序列长度为8的序列a[7]={4,8,5,7,1,3,6,2}
将序列平均分成两段。int mid=(l+r)/2
假如我们用归并排列来做一下上面那道题。
代码:
#include<bits/stdc++.h>
using namesapce std;
const int N=1e5+5;
int a[N],b[N];
void mergesort(int l,int r){//归并排列函数
if(l>=r) return;
int mid=(l+r)/2;
mergesort(l,mid);
mergesort(mid+1,r);
//递归到子问题得出答案进行合并两个子问题
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r){
if(a[i]>a[j]) b[k++]=a[i++];
else b[k++]=a[j++];
}
while(i<=mid) b[k++]=a[i++];//子问题1没有处理完的直接放到序列后面
while(j<=r) b[k++]=a[j++];//子问题1没有处理完的直接放到序列后面
for(int i=l;i<=r;i++) a[i]=b[i]; //更新原序列
}
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
mergesort(1,n);
int k;cin>>k;
for(int i=1;i<=k;i++) cout<<a[i]<<endl;
return 0;
}
归并排列的优点是比快速排列更稳定,且比暴力枚举时间复杂度更低,所以我们大多数时候都会用归并排列来做。