归并排序
题目
利用归并排序法将包含n个整数的数列S按升序排序。另外,请报告merge中总共执行了多少次比较运算。
思路
归并排序:
- 以整个数组为对象执行mergeSort
- mergeSort如下所示:
1、将给定的包含n个元素的局部数组“分割”成两个局部数组,每个数组包含n/2个元素。
2、对两个局部数组分别执行mergeSort排序。
3、通过merge将两个已排序完毕的局部数组“整合”成一个数组。
当局部数组只剩一个元素时,mergeSort不作任何处理直接结束。如果不是,则计算局部数组的中央位置mid,将left到mid(不包含mid)视作后半部分,再分别套用mergeSort。
一般来说,n个数据大致会分为Log2(n)层。每层执行merge的总复杂度为O(n)。因此整体复杂度为O(nlogn)。
代码
/*归并*/
void merge(int A[],int n,int left,int mid,int right){
int n1=mid-left;
int n2=right-mid;
for(int i=0;i<n1;i++) L[i]=A[left+i];
for(int i=0;i<n2;i++) R[i]=A[right+i];
L[n1]=R[n2]=SENTINEL;//末尾标记
int i=0,j=0;
for(int k=left;k<right;k++){
cnt++;
if(L[i]<=R[i]){
A[k]=L[i++];
}else{
A[k]=R[j++];
}
}
}
/*归并排序*/
void mergeSort(int A[],int n,int left,int right){
if(left+1<right){
int mid=(right+left)/2;
mergeSort(A,n,left,mid);
mergeSort(A,n,mid,right);
merge(A,n,left,mid,right);
}
}
分割排序
题目
输入有n个元素的数列A,得出分割后的数列,把用作分割基准的元素用“[ ]”标出。
思路
数组A的分割对象范围为p到r,设置分割的基准是A[r]为x。然后移动A中的元素,将小于等于x的元素移到p到i的范围,大于x的元素移到i+1到j的范围内(不包含j)。i的初始化为p-1,j初始化为p。
A[j]大于x时不必移动元素,直接让j向前移动一个位置,将A[j]归入“大于x的组”。
如果A[j]小于等于x,则先让i向前移动一个位置,然后交换A[j]进而A[i]。这样一来就进入了“小于等于x的组”。而随着j向前移动一个位置,原本位于A[i]的元素又会回到“大于x的组”中。
代码
#include <stdio.h>
#define MAX 100000
int A[MAX],n;
int partition(int p,int r){
int x,i,j,t;
x=A[r];
i=p-1;
j=p;
for(j=p;j<r;j++){
if(A[j]<=x){//如果A[j]小于x,则将i向前一位,A[j] A[i]交换。
i++;
t=A[i];A[i]=A[j];A[j]=t;
}
}
t=A[i+1];A[i+1]=A[r];A[r]=t;//最后将分割基准值交换即可
return i+1;
}
int main(){
int i,q;
scanf("%d",&n);
for(i=0;i<n;i++) scanf("%d",&A[i]);
q=partition(0,n-1);
for(i=0;i<n;i++){
if(i) printf(" ");
if(i==q) printf("[");
printf("%d",A[i]);
if(i==q) printf("]");
}
printf("\n");
return 0;
}
快速排序
题目
给n张卡片排序。每张卡片由1个花色(S、H、C、D)和1个数字组成。请用快速排序,将这些卡片按升序排列。
限制:输入中花色和数字完全相同的卡片不超过2张。
思路
快速排序:
- 以整个数组为对象执行quickSort。
- quickSort流程如下:
1、通过分割将对象局部数组分割为前后两个局部数组。
2、对前半部分的局部数组执行quickSort。
3、对后半部分的局部数组执行quickSort。
快速排序与归并排序一样基于分支法,但其执行partition进行分割时就已经在原数组中完成了排序,因此不需要归并排序中那种手动的合并处理。
代码
#include <stdio.h>
#define MAX 100000
#define SENTINEL 200000000
struct Card{
char suit;
int value;
};
struct Card L[MAX/2+2],R[MAX/2+2];
/*归并排序*/
void merge(struct Card A[],int n,int left,int mid,int right){
int i=0,j=0,k;
int n1=mid-left;
int n2=right-mid;
for(i=0;i<n1;i++) L[i]=A[left+i];
for(i=0;i<n2;i++) R[i]=A[mid+1];
R[n2]=L[n1]=SENTINEL;
for(k=left;k<right;k++){
if(L[i].value<=R[j].value){
A[k]=L[i]++;
}else{
A[k]=R[j++];
}
}
}
void mergeSort(struct Card A[],int n,int left,int right){
if(left<=right){
int mid=(left+right)/2;
mergeSort(A,n,left,mid);
mergeSort(A,n,mid,right);
merge(A,n,left,mid,right);
}
}
/*快速排序*/
int partition(struct Card A[],int n,int p,int r){
int i,j;
struct Card t,x;
x=A[r];
i=p-1;
j=p;
for(j=p;j<r;j++){
if(A[j].value<=x.value){
i++;
t=A[i];A[i]=A[j];A[j]=t;
}
}
t=A[i+1];A[t+1]=A[r];A[r]=t;
return i+1;
}
void quickSort(struct Card A[],int n,int p,int r){
int q;
if(p<r){
q=partition(A,n,p,r);
quickSort(A,n,p,q-1);
quickSort(A,n,q+1,r);
}
}
int main(){
int n,i,v;
struct Card A[MAX],B[MAX];
char S[10];
int stable=1;
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%s %d",S[i],&v);
A[i].suit=B[i].suit=S[i];
A[i].value=B[i].value=v;
}
mergeSort(A,n,0,n);
quickSort(B,n,0,n-1);
for(i=0;i<n;i++){
//比较归并排序与快速排序的结果
if(A[i].suit != B[i].suit) stable=0;
}
if(stable==1) printf("Stable\n");
else printf("Not Stable\n");
for(i=0;i<n;i++){
printf("%c %d\n",B[i].suit,B[i].value);
}
return 0;
}
计数排序
又叫桶排序或者箱排序。
题目
输入数列A,通过计数排序算法将A按升序排列输出。
思路
对输入数组A的各元素Aj进行排序时,先将小于等于Aj的元素数记录在计数数组C中,然后根据C中的数值计算Aj在输出数组B中的位置。
计数数组C[x]的值表示数组A中有多少个小于等于x的元素。
代码
#include <stdio.h>
#include <stdlib.h>
#define MAX 2000001
#define VMAX 10000
int main(){
unsigned short *A,*B;
int C[VMAX+1];
int n,i,j;
scanf("%d",&n);
A=malloc(sizeof(short) * n+1);
B=malloc(sizeof(short) * n+1);
for(i=0;i<=VMAX;i++) C[i]=0;
for(i=0;i<n;i++){
scanf("%hu",&A[i+1]);
C[A[i+1]]++;
}
for(i=1;i<=VMAX;i++) C[i]=C[i]+C[i-1];
for(j=1;j<=n;j++){
B[C[A[j]]]=A[j];
C[A[j]]--;
}
for(i=1;i<=n;i++){
if(i>1) printf(" ");
printf("%d",B[i]);
}
printf("\n");
return 0;
}
利用标准库排序
Sort函数
STL为用户提供了给元素排序的函数sort。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
int n;
vector<int> v;
cin>>n;
for(int i=0;i<n;i++){
int x;
cin>>x;
v.push_back(x);
}
sort(v.begin(),v.end());
for(int i=0;i<v.size();i++){
cout<<v[i]<<" ";
}
cout<<endl;
return 0;
}