放在同一组中进行直接插入排序为止。
在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按增量作为距离排过序,使文件较接近于有序状态,所以新的一趟排序过程也较快。由于Shell排序算法是按增量分组进行的排序,所以Shell排序算法是一种不稳定的排序算法。
关于插入排序的另一篇文章:
/******************************************************************
希尔排序是D.L.Shell于1959年提出来的一种排序算法,在这之前排序算法的时间复杂度基本都是O(n2)的,
希尔排序算法是突破这个时间复杂度的第一批算法之一。插入排序的效率在某些时候是很高的,比如,
记录本身就是基本有序的,只需要少量的插入操作,就可以完成整个记录集的排序工作,此时直接插入很高效。
还有就是记录数比较少时,直接插入的优势也比较明显。可问题在于,两个条件本身就过于苛刻,现实中记录少或者基本有序都属于特殊情况。
不过别急,有条件当然是好,条件不存在,我们创造条件,也是可以去做的。于是科学家希尔研究出了一种排序,对直接插入排序改进后可以增加效率的方法。
如何让待排序的记录个数较少呢?很容易想到的就是将原本有大量记录数的记录进行分组。
分割成若干个子序列,此时每个子序列待排序的记录个数就比较少了。然后在这些子序列内分别进行直接插入排序,
当整个序列都基本有序时,注意只是基本有序时,再对全体记录进行一次直接插入排序。
算法描述
1)选定步长gap1(小于数据长度),按步长gap1将数据分成gap1组(按列分的)
2)所有距离为gap1倍数的数据放在同一组中
3)在各组中进行直接插入排序
4)重新取步长gap2(<gap1)重复上述的分组和排序,直到所取步长gapn=1,即所有数据
放在同一组中进行直接插入排序为止。
********************************************************************/
#include <cmath>
#include<cstdlib>
#include<time.h>
#include<cstdio>
#include<iostream>
#include <unistd.h> // Sleep函数
using namespace std;
//产生随机数组
void Random(int a[],int n)
{
int i=0;
srand( (unsigned)time( NULL ) );
while(i<n)
{
a[i++]=rand()/9999999;
}
}
//print the array
void print(int a[], int len)
{
int i;
for (i = 0; i < len; i++)
cout<<a[i]<<" ";
cout<<endl;
}
void ShellSort_v1(int a[],int n)
{
int i,j;
int increment=n;
int temp;
do
{
increment=increment/3+1; /* 增量序列 */
for(i=increment; i<n; i++)
{
if (a[i]<a[i-increment]) /* 需将a[i]插入有序增量子表 */
{
temp=a[i]; /* 暂存在temp */
for(j=i-increment; j>=0 && temp<a[j]; j-=increment) //注意是j>=0 如果没有=则第一个数据不对
a[j+increment]=a[j]; /* 记录后移,查找插入位置 */
a[j+increment]=temp; /* 插入 */
}
}
}
while(increment>1);
}
/**************************************************************************
//和插入排序相比较
void InsertSort(int a[], int n)
{
int i, j, temp;
for (i = 1; i < n; i++)
{
temp = a[i];//这个是未排序数据的第一个,要把它插入到合适的位置
for (j = i-1; j >= 0 && temp < a[j]; j--)//a[j]是排序区的最后一个数据
a[j+1] = a[j];//a[j]是排序区的最后一个数据
//第一次循环式,a[j+1] 即a[i]的位置因为后移而被占据,但是他有备份
if(j!=(i-1)) 第i个数字比前面的都大,不需要重新插入
{
a[j+1]=temp;
}
当跳出循环时,说明temp >= a[j],循环体未执行,上一个循环的执行
结果是a[j+2] = a[j+1],a[j+1]位置已经空出,且a[j]<=temp<a[j+1]
为保持有序,a[j+1] = temp
}
}
**************************************************************************/
/*和插入排序相比,只是步长不是1而是gap*/
void ShellSort_v2(int *data, size_t size)
{
int temp,j=0;
for (int gap = (size>>1); gap > 0; gap>>=1)
/*最后一个循环的执行,gap==1*/
for (int i = gap; i < size; ++i)
{
temp = data[i]; // 参考插入排序 temp保存的是无序区的第一个数据
//初始化
/* 0=<j<=i-gap 表示有序区
j = i -gap j随着上层循环i的增加而增加*/
for( j = i -gap; j >= 0 && data[j] > temp; j -=gap)//从有序区里边找到找到待插入位置
//data[j] 是排序区的最后一个数据
{
data[j+gap] = data[j];//数据依次后移,找到待插入位置
}
if(j!=i -gap) //第i个数字比前面的都大,不需要重新插入,没有下边v3的效率不高
//应该是先判断,再移动
{
data[j+gap] = temp;
}
}
}
void ShellSort_v3(int *data, size_t size)
{
int temp,j=0;
for (int gap = (size>>1); gap > 0; gap>>=1)
/*最后一个循环的执行,gap==1*/
for (int i = gap; i < size; ++i)
{
if(data[i]<data[i-gap]) //先判断是否需要插入,然后再寻找插入位置
{
temp = data[i]; // 参考插入排序 temp保存的是无序区的第一个数据
/* 0=<j<=i-gap 表示有序区
j = i -gap j随着上层循环i的增加而增加*/
for( j = i -gap; j >= 0 && data[j] > temp; j -=gap)//从有序区里边找到找到待插入位置
//data[j] 是排序区的最后一个数据
{
data[j+gap] = data[j];//数据依次后移,找到待插入位置
}
data[j+gap] = temp;
}
}
}
void test1()
{
int a[30] = {0};
Random(a,30);
cout<<"-----------berore -----sort-------ShellSort_v1-------------"<<endl<<endl;
print(a,30);
//shellsort(a,30);
ShellSort_v1(a,30);
cout<<endl<<"------------after-------sort---------ShellSort_v1--------"<<endl;
print(a,30);
}
void test2()
{
int a[30] = {0};
Random(a,30);
cout<<"-----------berore -----sort------ShellSort_v2--------------"<<endl<<endl;
print(a,30);
//shellsort(a,30);
ShellSort_v2(a,30);
cout<<endl<<"------------after-------sort---ShellSort_v2--------------"<<endl;
print(a,30);
}
void test3()
{
int a[30] = {0};
Random(a,30);
cout<<"-----------berore -----sort------ShellSort_v3--------------"<<endl<<endl;
print(a,30);
//shellsort(a,30);
ShellSort_v2(a,30);
cout<<endl<<"------------after-------sort---ShellSort_v3--------------"<<endl;
print(a,30);
}
int main()
{
cout<<endl<<"=====================================test1============================"<<endl<<endl;
test1();
sleep(1); //防止两次测试数据相同,时间种子的问题
cout<<endl<<"=====================================test2============================"<<endl<<endl;
test2();
sleep(1); //防止两次测试数据相同,时间种子的问题
cout<<endl<<"=====================================test3============================"<<endl<<endl;
test3();
}
/*************************************************
=====================================test1============================
-----------berore -----sort-------ShellSort_v1-------------
206 157 192 37 29 167 44 200 107 49 140 21 167 1 94 70 195 51
179 105 81 39 10 114 9 203 11 95 180 93
------------after-------sort---------ShellSort_v1--------
1 9 10 11 21 29 37 39 44 49 51 70 81 93 94 95 105 107 114 140
157 167 167 179 180 192 195 200 203 206
=====================================test2============================
-----------berore -----sort------ShellSort_v2--------------
176 32 151 106 123 2 123 50 175 68 144 58 147 14 95 171 22 3
115 35 100 107 57 199 169 96 62 35 97 60
------------after-------sort---ShellSort_v2--------------
2 3 14 22 32 35 35 50 57 58 60 62 68 95 96 97 100 106 107 115
123 123 144 147 151 169 171 175 176 199
=====================================test3============================
-----------berore -----sort------ShellSort_v3--------------
38 13 108 175 109 49 93 113 25 86 146 200 18 25 200 162 60 167
49 177 116 65 208 171 110 90 213 183 116 19
------------after-------sort---ShellSort_v3--------------
13 18 19 25 25 38 49 49 60 65 86 90 93 108 109 110 113 116 116
146 162 167 171 175 177 183 200 200 208 213
Process returned 0 (0x0) execution time : 2.006 s
Press ENTER to continue.
***************************************************/