希尔排序是第一批冲破二次时间屏障的第一批算法,其使用比较相距一定间隔的元素来工作,各趟比较的距离随着算法的进行而减少,知道比较相邻距离的最后一趟排序为止。因此也叫做最小增量排序
希尔排序的最重要部分在于增量序列,即使用的距离序列 h 1 , h 2 , ⋯ , h n h_1,h_2,\cdots,h_n h1,h2,⋯,hn,其中只要 h 1 = 1 h_1=1 h1=1则希尔排序有效,但是增量序列的选择却是决定希尔排序时间复杂度的决定性因素。
希尔排序生效的还有一个重要前提是“ h k h_k hk排序性”不变,此时后一次排序不会打乱前一次的排序结果。
Shell序列指的是 h t = [ N / 2 ] h_t=[N/2] ht=[N/2]和 h k = [ h k + 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;
}
}
其最坏情形运行时间复杂度为 Θ ( N 2 ) \Theta(N^2) Θ(N2),只要令 N N N为2的幂,并且使得当 h = 2 h=2 h=2时的两条序列都为单调序列但不相交,例如
1,9,2,10,3,11,4,12,...
此时,除了 h = 1 h=1 h=1以外的增量序列均不会对其排序,浪费大量时间,具体证明这里不再赘述。
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 , . . . , 2 k − 1 1,3,7,...,2^k-1 1,3,7,...,2k−1的增量序列,其最坏复杂度为 O ( N 3 2 ) 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);
}
}
利用快速排序算法将读入的 N N N 个数从小到大排序后输出。
快速排序是信息学竞赛的必备算法之一。对于快速排序不是很了解的同学可以自行上网查询相关资料,掌握后独立完成。(C++ 选手请不要试图使用 STL
,虽然你可以使用 sort
一遍过,但是你并没有掌握快速排序算法的精髓。)
第 1 1 1 行为一个正整数 N N N,第 2 2 2 行包含 N N N 个空格隔开的正整数 a i a_i ai,为你需要进行排序的数,数据保证了 a i a_i ai 不超过 1 0 9 10^9 109。
将给定的 N N N 个数从小到大输出,数之间空格隔开,行末换行且无空格。
5
4 2 4 5 1
1 2 4 4 5
对于 20 % 20\% 20% 的数据,有 N ≤ 1 0 3 N\leq 10^3 N≤103;
对于 100 % 100\% 100% 的数据,有 N ≤ 1 0 5 N\leq 10^5 N≤105。
#include
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
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;
}