插入排序:由于插入排序所执行的交换操作涉及近邻元素,使得元素每次只能移动一位,所以插入排序的效率较低。比如当关键字最小的元素刚好在数组的尾端,就需要N步将该元素放到数组最前端。而当整个记录本身是基本有序是,只需插入排序的效率是非常高的。
改进方法:让每次交换操作不要涉及近邻元素,通过非相邻的元素进行交换来提高执行效率,这就是希尔排序对插入排序的扩展。希尔排序的算法思想:将文件重新排列,使文件具有这样的性质,每第h个元素(从任何地方开始)产生一个排好序的文件。这样的文件称为h-排序文件。即h-排序的文件是h个独立的已排好序的文件,相互交叉在一起。将无序数组分割为若干个子序列,子序列不是逐段分割的,而是相隔特定增量的子序列,对各个子序列进行插入排序;然后再选择更小的增量,再将数组分割为多个子序列进行排序;直到最后选择再来增量为1,使用直接插入排序,使最终数组成为有序。
增量的选择:在每趟排序过程都有一个增量,增量要满足关系:d[1]>d[2]>...>d[t] = 1;增量序列选取不同,时间复杂度也会有变化。增量的选择决定着算法的执行效率。
算法实现:首选增量为n/2,每次增量为原来的1/2,直到增量为1。代码:
外循环控制步长,内循环进行插入排序。首先选择步长
/*步长选取为n/2*/
void
shellsort(int v[],int n)
{
int gap, i, j, temp;
for(gap = n/2; gap > 0; gap /= 2)
for(i = gap; i < n; i++)
for(j = i - gap; j >= 0 && v[j] > v[j+gap]; j -= gap)
{
temp = v[j];
v[j] = v[j+gap];
v[j+gap] = temp;
}
}
/*======================================================================================*/
/*程序源代码:*/
void shell_sort(int v[], int len)
{
int j,i,key,m;
int gap=0;
if( len <= 0 || v == NULL )
return;
while( gap <= len )
{
gap = gap*3+1;
}
for(; gap > 0; gap /= 3)
{
cout << "gap = " << gap << endl;
for(j = gap; j < len; j++)
{
cout << "j = " << j << endl;
key = v[j];
for(i = j-gap; i >= 0; i -= gap)
{
cout << "Hello" << endl << endl;
cout << "i = " << i <<endl;
if(key > v[i])
break;
else
{
v[i+gap] = v[i];
v[i] = key;
}
for(m = 0; m < MAXNUM; m++)
{
cout << v[m] << '\t' ;
}
cout << endl;
cout << "World" << endl << endl;
}
}
for(m = 0; m < MAXNUM; m++)
{
cout << v[m] << '\t' ;
}
cout << endl;
}
}
/*============================================================================================*/
排序过程:
The input number is:
41 67 34 0 69 24 78 58 62 64
gap = 13
41 67 34 0 69 24 78 58 62 64
gap = 4
j = 4
Hello
i = 0
j = 5
Hello
i = 1
41 24 34 0 69 67 78 58 62 64
World
j = 6
Hello
i = 2
j = 7
Hello
i = 3
j = 8
Hello
i = 4
41 24 34 0 62 67 78 58 69 64
World
Hello
i = 0
j = 9
Hello
i = 5
41 24 34 0 62 64 78 58 69 67
World
Hello
i = 1
41 24 34 0 62 64 78 58 69 67
gap = 1
j = 1
Hello
i = 0
24 41 34 0 62 64 78 58 69 67
World
j = 2
Hello
i = 1
24 34 41 0 62 64 78 58 69 67
World
Hello
i = 0
j = 3
Hello
i = 2
24 34 0 41 62 64 78 58 69 67
World
Hello
i = 1
24 0 34 41 62 64 78 58 69 67
World
Hello
i = 0
0 24 34 41 62 64 78 58 69 67
World
j = 4
Hello
i = 3
j = 5
Hello
i = 4
j = 6
Hello
i = 5
j = 7
Hello
i = 6
0 24 34 41 62 64 58 78 69 67
World
Hello
i = 5
0 24 34 41 62 58 64 78 69 67
World
Hello
i = 4
0 24 34 41 58 62 64 78 69 67
World
Hello
i = 3
j = 8
Hello
i = 7
0 24 34 41 58 62 64 69 78 67
World
Hello
i = 6
j = 9
Hello
i = 8
0 24 34 41 58 62 64 69 67 78
World
Hello
i = 7
0 24 34 41 58 62 64 67 69 78
World
Hello
i = 6
0 24 34 41 58 62 64 67 69 78
0 24 34 41 58 62 64 67 69 78
The duration time is: 0.094000 seconds
/*==================================================================================================*/
/*将序列分为gap组,对每一组分别进行插入排序*/
程序源代码:
/*======================================================================================================*/
/*每次将文件分为gap组,对每一组分别进行直接插入排序*/
void shell_sort(int v[], int len)
{
int j,i,r,key,m;
int gap=0;
if( len <= 0 || v == NULL )
return;
while( gap <= len )
{
gap = gap*3+1;
}
for(; gap > 0; gap /= 3)
{
if(gap > len)
continue;
cout << "gap = " << gap << endl;
for(j = 0; j < gap; j++) //分为gap组,每组分别进行直接插入排序
{
cout << "Hello" << endl;
/*直接插入排序*/
for(i = j+gap; i < len; i += gap)
{
key = v[i];
r = i-gap;
for(; r >= j; r = r-gap)
{
if(key > v[r])
break;
else
{
cout << "r - gap = " << r - gap << endl;
v[r+gap] = v[r];//v[r] = v[r-gap];v[r-gap] = key;导致数组访问出错:出现访问v[-1],v[-3]等;
v[r] = key;
}
}
}
}
for(m = 0; m < MAXNUM; m++)
{
cout << v[m] << '\t' ;
}
cout << endl;
}
}
/*======================================================================================================*/
/*==================================================================================================*/
算法分析
平均时间复杂度:
步长选定,已知的最好步长序列由Marcin Ciura设计(1,4,10,23,57,132,301,701,1750,…) 这项研究也表明“比较在希尔排序中是最主要的操作,而不是交换。”用这样步长序列的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快,但 是在涉及大量数据时希尔排序还是比快速排序慢。
与增量序列的长度选择有关,某些序列可以为O(n^1.3);
空间复杂度:O(1)
稳定性:不稳定。由于记录是跳跃式的移动,希尔排序并不是一 种稳定的排序算法。