二路归并排序
算法原理
假设该数组为q,左边界为 l,右边界为 r,并设置临时数组tmp。
采用分治思想:
第一步:确定分界点: mid = l + r >>1。
第二步:递归划分左右两段。
第三步:归并(合二为一):将左右两段在 tmp 中排好序,并将tmp中排好序的数据拷回 q 的相应位置。
代码实现
#include<iostream>
using namespace std;
const int N = 100010;
int q[N], tmp[N];
void merge_sort(int q[], int l, int r){
if(l >= r) return;
int mid = l+r>>1;
merge_sort(q, l, mid);
merge_sort(q, mid+1, r);
int i = l, j = mid+1, k = 0;
while(i <= mid && j <= r)
if(q[i] < q[j]) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
while(i <= mid) tmp[k++] = q[i++];
while(j <= r) tmp[k++] = q[j++];
for(i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];
}
int main(){
int n;
scanf("%d", &n);
for(int i=0; i<n; i++) scanf("%d", &q[i]);
merge_sort(q, 0, n-1);
for(int i=0; i<n; i++) printf("%d ", q[i]);
return 0;
}
性能分析
- 空间效率:使用临时数组tmp,共n个单元,所以空间复杂度为O(n)O(n)O(n)
- 时间效率:每趟归并的时间复杂度为O(n)O(n)O(n),共需要进行⌈log2n⌉\lceil\log_2n\rceil⌈log2n⌉趟归并,所以时间复杂度为O(nlog2n)O(n\log_2n)O(nlog2n)
- 稳定性:稳定
基数排序
算法原理
- 基于关键字各位大小进行排序。
- 最高位优先法(MSD),按关键字位权重递减依次逐层划分成若干个更小的子序列,最后将所有子序列依次连接成一个有序序列。
- 最低位优先法(LSD),按关键字权重递增依次进行排序,最后形成一个有序序列
- 排序过程(以最低位优先法为例)
- 假设长度为nnn的线性表中每个结点aja_jaj的关键字为一个ddd元组(kjd−1,kjd−2,…,kj1,kj0k_j^{d-1},k_j^{d-2},…,k^1_j,k^0_jkjd−1,kjd−2,…,kj1,kj0),满足0<=kji<=r−1(0<=j<n,0<=i<=d−1)0<=k_j^i<=r-1(0<=j<n,0<=i<=d-1)0<=kji<=r−1(0<=j<n,0<=i<=d−1),其中kjd−1k_j^{d-1}kjd−1为最主位关键字,kj0k_j^0kj0为最次位关键字。
- 使用rrr个队列Q0,Q1,…,Qr−1Q_0,Q_1,…,Q_{r-1}Q0,Q1,…,Qr−1
- 对i=0,1,…,d−1i=0,1,…,d-1i=0,1,…,d−1,依次做一次“分配”和“收集”。
- 分配:开始时,把各个队列设为空队列,然后依次考察每个结点aja_jaj,若aja_jaj关键字kji=kk^i_j=kkji=k,则把aja_jaj放进QkQ_kQk队列中
- 收集:将各个队列中的结点依次首尾相接,得到新的结点序列
代码实现
void RadixSort(int A[], int n){
int temp[10][n]; // 10个辅助队列
int len[10]; // 记录每个队列中的数据个数
int max=A[1]; // 记录最大值
for(int i=2; i<=n; i++)
if(max < A[i])
max = A[i];
int s=max, count=0; // count记录最大值的位数
while(s){
s = s/10;
count++;
}
for(int j=1, x=1; j<=count; j++, x*=10){
for(int i=0; i<10; i++) len[i]=0; // 初始化
// 分配
for(int k=1; k<=n; k++){
int elem = (A[k]/x)%10; // 取相应位置的数
temp[elem][len[elem]++] = A[k];
}
// 收集
int cnt=1;
for(int y=0; y<10; y++)
for(int z=0; z<len[y]; z++)
A[cnt++] = temp[y][z];
printf("第%d趟:", j);
for(int k=1; k<=n; k++) printf("%d ", A[k]);
printf("\n");
}
}
举例
性能分析
- 空间效率:需要辅助存储空间为r个队列,所以时间复杂度为O(r)O(r)O(r)
- 时间效率:需要进行d趟排序(分配和收集),一趟分配需要O(n)O(n)O(n),一趟收集需要O(r)O(r)O(r),所以其时间复杂度为O(d(n+r))O(d(n+r))O(d(n+r)),且与序列的初始状态无关
- 稳定性:稳定