希尔排序
简介
希尔排序是第一批冲破二次时间屏障的第一批算法,其使用比较相距一定间隔的元素来工作,各趟比较的距离随着算法的进行而减少,知道比较相邻距离的最后一趟排序为止。因此也叫做最小增量排序
希尔排序的最重要部分在于增量序列,即使用的距离序列h1,h2,⋯ ,hnh_1,h_2,\cdots,h_nh1,h2,⋯,hn,其中只要h1=1h_1=1h1=1则希尔排序有效,但是增量序列的选择却是决定希尔排序时间复杂度的决定性因素。
希尔排序生效的还有一个重要前提是“hkh_khk排序性”不变,此时后一次排序不会打乱前一次的排序结果。
序列选择
1.Shell序列
Shell序列指的是ht=[N/2]h_t=[N/2]ht=[N/2]和hk=[hk+1/2]h_k=[h_{k+1}/2]hk=[hk+1/2]的序列,使用该代码的希尔排序代码如下:
void sort(long long num[],int N){
int i=N/2;
while(i>=1){
for(int j=0;j<i;j++){
for(int k=j;k<N;k+=i){
for(int m=k;m>j;m-=i){
if(num[m]<num[m-i]){
long long temp=num[m];
num[m]=num[m-i];
num[m-i]=temp;
}
else
break;
}
}
}
i/=2;
}
}
其最坏情形运行时间复杂度为Θ(N2)\Theta(N^2)Θ(N2),只要令NNN为2的幂,并且使得当h=2h=2h=2时的两条序列都为单调序列但不相交,例如
1,9,2,10,3,11,4,12,...
此时,除了h=1h=1h=1以外的增量序列均不会对其排序,浪费大量时间,具体证明这里不再赘述。
2.Hibbard序列
void sort(long long num[],int N){
int i=1;
while(i<N){
i=2*i;
}
i=i/2-1;
while(i>=1){
for(int j=0;j<i;j++){
for(int k=j;k<N;k+=i){
for(int m=k;m>j;m-=i){
if(num[m]<num[m-i]){
long long temp=num[m];
num[m]=num[m-i];
num[m-i]=temp;
}
else
break;
}
}
}
i=(i+1)/2-1;
}
}
Hibbard序列使用的是形如1,3,7,...,2k−11,3,7,...,2^k-11,3,7,...,2k−1的增量序列,其最坏复杂度为O(N32)O(N^{\frac{3}{2}})O(N23),证明不再赘述。
堆排序
堆排序是将优先队列用于排序的一种方法,简单来说,只要先对数组进行建堆操作建立最大堆,然后按顺序Delete Max即可。如果我们用一个新的数组去存取Delete得到的最大值,会导致其空间复杂度会达到O(N)O(N)O(N)。为了降低空间复杂度,我们会将堆的堆首元素与堆尾元素交换位置,然后使堆的大小减小一后调整堆为最大堆。
实现代码如下:
void percdown(long long a[],int i,int N){
while(i<N){
if(2*i+2<N){
if(a[2*i+2]>a[2*i+1])
if(a[2*i+2]>a[i]){
long long temp=a[i];
a[i]=a[2*i+2];
a[2*i+2]=temp;
i=2*i+2;
}
else
break;//break容易被忘记
else
if(a[2*i+1]>a[i]){
long long temp=a[i];
a[i]=a[2*i+1];
a[2*i+1]=temp;
i=2*i+1;
}
else
break;
}
else if(2*i+1<N){
if(a[2*i+1]>a[i]){
long long temp=a[i];
a[i]=a[2*i+1];
a[2*i+1]=temp;
i=2*i+1;
}
else
break;
}
else
break;
}
}
void sort(long long num[],int N){
for(int i=N-1;i>=0;i--){
percdown(num,i,N);
}
for(int i=0;i<N;i++){
long long temp=num[0];
num[0]=num[N-1-i];
num[N-1-i]=temp;
percdown(num,0,N-1-i);
}
}
P1177【模板】快速排序
题目描述
利用快速排序算法将读入的 NNN 个数从小到大排序后输出。
快速排序是信息学竞赛的必备算法之一。对于快速排序不是很了解的同学可以自行上网查询相关资料,掌握后独立完成。(C++ 选手请不要试图使用 STL
,虽然你可以使用 sort
一遍过,但是你并没有掌握快速排序算法的精髓。)
输入格式
第 111 行为一个正整数 NNN,第 222 行包含 NNN 个空格隔开的正整数 aia_iai,为你需要进行排序的数,数据保证了 aia_iai 不超过 10910^9109。
输出格式
将给定的 NNN 个数从小到大输出,数之间空格隔开,行末换行且无空格。
样例 #1
样例输入 #1
5
4 2 4 5 1
样例输出 #1
1 2 4 4 5
提示
对于 20%20\%20% 的数据,有 N≤103N\leq 10^3N≤103;
对于 100%100\%100% 的数据,有 N≤105N\leq 10^5N≤105。
AC代码如下
希尔排序
#include <stdio.h>
long long p[1000001];
void sort(long long num[],int N){
int i=N/2;
while(i>=1){
for(int j=0;j<i;j++){
for(int k=j;k<N;k+=i){
for(int m=k;m>j;m-=i){
if(num[m]<num[m-i]){
long long temp=num[m];
num[m]=num[m-i];
num[m-i]=temp;
}
else
break;
}
}
}
i/=2;
}
}
int main()
{
int n;
scanf("%d",&n);
for (int i = 0;i < n;i++)
scanf("%lld",&p[i]);
sort(p,n);
for (int i = 0;i < n;i++)
printf("%lld ",p[i]);
return 0;
}
堆排序
#include <stdio.h>
long long p[1000001];
void percdown(long long a[],int i,int N){
while(i<N){
if(2*i+2<N){
if(a[2*i+2]>a[2*i+1])
if(a[2*i+2]>a[i]){
long long temp=a[i];
a[i]=a[2*i+2];
a[2*i+2]=temp;
i=2*i+2;
}
else
break;
else
if(a[2*i+1]>a[i]){
long long temp=a[i];
a[i]=a[2*i+1];
a[2*i+1]=temp;
i=2*i+1;
}
else
break;
}
else if(2*i+1<N){
if(a[2*i+1]>a[i]){
long long temp=a[i];
a[i]=a[2*i+1];
a[2*i+1]=temp;
i=2*i+1;
}
else
break;
}
else
break;
}
}
void sort(long long num[],int N){
for(int i=N-1;i>=0;i--){
percdown(num,i,N);
}
for(int i=0;i<N;i++){
long long temp=num[0];
num[0]=num[N-1-i];
num[N-1-i]=temp;
percdown(num,0,N-1-i);
}
}
int main()
{
int n;
scanf("%d",&n);
for (int i = 0;i < n;i++)
scanf("%lld",&p[i]);
sort(p,n);
for (int i = 0;i < n;i++)
printf("%lld ",p[i]);
return 0;
}