选择排序:
简单选择排序是指,对一个序列A中元素A[1]~A[n],令i从1到n枚举,进行n趟操作,每趟从待排序部分[i,n]中选择最小的元素,令其与待排序部分的第一个元素A[i]进行交换,这样元素A[i]就会与当前有序区间[1,i-1]形成新的有序区间[1,i]。于是在n趟操作后,所有元素就会是有序的。
该算法总共需要进行n趟操作,每趟操作选出待排序部分[i,n]中最小的元素,令其与A[i]交换。因此总时间复杂度为O(n^2),实现代码如下:
void selectSort(){
for(int i=1;i<=n;i++){ //进行n趟排序
int k=i;
for(int j=i;j<=n;j++){ //选出[i,n]中最小的元素,下标为K
if(A[j]<A[k])
k=j;
}
int temp=A[i];
A[i]=A[k];
A[k]=temp;
}
}
插入排序:
直接插入排序是指,对序列A的n个元素A[i]~A[n],令i从2到n枚举,进行n-1趟操作。假设某一趟时,序列A的前i-1个元素A[1]-A[n]已经有序,而范围[i,n]还未有序,那么该趟从范围[1,i-1]中寻找某个位置j,使得将A[i]插入位置j后(此时A[j]-A[i-1]会后移一位),范围[1,i]有序。
void insertSort(){
for(int i=2;i<=n;i++){ //进行n-1趟排序
int temp=A[i],j=i; //temp临时存放A[i],j从i开始往前枚举
while(j>1&&temp<A[j-1]){ //只要temp小于前一个元素A[j-1]
A[j]=A[j-1]; //把A[j-1]后移至A[j]
j--;
}
A[j]=temp; //插入位置为j
}
}
归并排序
归并排序是一种基于归并思想的排序方法,下面主要介绍2-路归并。2-路归并排序的原理是,将序列两两分组,将序列归并为[n/2]个组,组内单独排序;然后将这些组再两两归并,生成[n/4]个组,组内再单独排序;以此类推,直到只剩下一个组为止。归并排序的时间复杂度为O(nlogn)。
1.递归实现代码
const int maxn=1010;
//将数组A的[L1,R1]与[L2,R2]区间合并为有序区间(此处L2即为R1+1)
void merge(int A[],int L1,int R1,int L2,int R2){
int i=L1,j=L2; //i指向A[L1],j指向A[L2]
int temp[maxn],index=0; //temp临时存放合并后的数组,index为其下标
while(i<=R1&&j<=R2){
if(A[i]<=A[j]) //如果A[i]<=A[j]
temp[index++]=A[i++]; //将A[i]加入序列temp
else //如果A[i]>A[j]
temp[index++]=A[j++]; //将A[j]加入序列temp
}
while(i<=R1)
temp[index++]=A[i++]; //将[L1,R1]的剩余元素加入序列temp
while(j<=R2)
temp[index++]=A[j++]; //将[L2,R2]的剩余元素加入序列temp
for(i=0;i<index;i++)
A[L1+i]=temp[i]; //将合并后的序列赋值返回数组A
}
void mergeSort(int A[],int left,int right){
if(left<right){ //只要left小于right
int mid=(left+right)/2; //取[left,right]的中点
mergeSort(A,left,mid); //递归,将左子区间[left,mid]归并排序
mergeSort(A,mid+1,right); //递归,将右子区间[mid+1,right]归并排序
merge(A,left,mid,mid+1,right); //将左子区间和右子区间合并
}
}
2.非递归实现
const int maxn=1010;
//将数组A的[L1,R1]与[L2,R2]区间合并为有序区间(此处L2即为R1+1)
void merge(int A[],int L1,int R1,int L2,int R2){
int i=L1,j=L2; //i指向A[L1],j指向A[L2]
int temp[maxn],index=0; //temp临时存放合并后的数组,index为其下标
while(i<=R1&&j<=R2){
if(A[i]<=A[j]) //如果A[i]<=A[j]
temp[index++]=A[i++]; //将A[i]加入序列temp
else //如果A[i]>A[j]
temp[index++]=A[j++]; //将A[j]加入序列temp
}
while(i<=R1)
temp[index++]=A[i++]; //将[L1,R1]的剩余元素加入序列temp
while(j<=R2)
temp[index++]=A[j++]; //将[L2,R2]的剩余元素加入序列temp
for(i=0;i<index;i++)
A[L1+i]=temp[i]; //将合并后的序列赋值返回数组A
}
void mergeSort(int A[]){
//setp为组内元素个数,setp/2为左子区间元素个数,注意等号可以不取
for(int step=2;step/2<=n;step*=2){
//每setp个元素一组,组内前setp/2和后setp/2个元素进行合并
for(int i=1;i<=n;i+=step){ //对每一组
int mid=i+step/2-1; //左子区间元素个数为setp/2
if(mid+1<=n){ //右子区间存在元素则合并
//左子区间为[i,mid],右子区间为[mid+1,min(i+setp-1,n)]
merge(A,i,mid,mid+1,min(i+step-1,n));
}
}
}
}
快速排序(c++中sort()函数)
快速排序是排序算法中平均时间复杂度为O(nlogn)的一种算法,其实现需要先解决寻找主元的问题。(即使得A[1]的左侧所有元素都不超过A[1],右侧所有元素都大于A[1])。此问题可通过two pointers思想解决:
- 先将A[1]存至某个临时变量temp,并令两个下标left,right分别指向序列首尾(如令left=1,right=n)。
- 只要right指向的元素A[right]大于temp,就将right不断左移,当某个时候A[left]<=temp时,将元素A[right]挪到left指向的元素A[left]处。
- 只要left指向的元素A[left]不超过temp,就将left不断左移,当某个时候A[left]>temp时,将元素A[left]挪到right指向的元素A[right]处。
- 重复2,3步骤,直到left与right相遇,把temp放到相遇的地方。
代码实现如下:
int Partition(int A[],int left,int right){
int temp=A[left]; //将A[left]存放至临时变量temp
while(left<right){ //只要left与right不相遇
while(left<right&&A[right]>temp) //反复左移right
right--;
A[left]=A[right]; //将A[right]挪到A[left]
while(left<right&&A[right]<=temp) //反复右移left
left++;
A[right]=A[left]; //将A[left]挪到A[right]
}
A[left]=temp; //把temp放到left与right相遇的地方
return left; //返回相遇的下标
}
在主元基础上利用递归法对左右区间不断进行找主元的递归,最终完成排序。
快速排序代码实现:
//快速排序,left与right初值为序列首尾下标
void quickSort(int A[],int left,int right){
if(left<right){ //当前区间的长度超过1
//将[left,right]按A[left]一分为二
int pos=Partition(A,left,right);
quickSort(A,left,pos-1); //对左子区间递归进行快速排序
quickSort(A,pos+1,right); //对右子区间递归进行快速排序
}
}