希尔是一个人的名字,因为他在论文中首次提到了这样的排序方式,这是希尔排序的来源
希尔排序,比插入排序有更加的优化,
有很多的文章中说希尔排序中也用到了插入排序,这样的说法只是为了方便理解,其实是不正确的
希尔排序中并没有用到插入排序
对吧,那么我们希尔排序:
希尔排序的思想:对于一个数组,如何来提高排序的效率,排序过程过程有两个步骤:1,比较大小 2.交换位置
只要减少比较的次数和数组之间交换的次数,那么就可以实现效率的提高
比较次数最多的 是任意的两个数字之间都进行比较,比如 a,b,c 这三个数据进行排序,最多的次数比较如下:[a,b],[b,c],[a,c] 那么次数为 排列组合 3!/2!=3次 按照这样的比较方法:
数组中包含4个元素 4!/2!=12
数组中包含5个元素5!/2!=60
随着元素的增加,那么比较的次数是非常的大的
上节说的是插入排序 O(n^2) (算法结构1.插入排序_datouniao1的博客-CSDN博客) 相对于上面的任意元素比较 减少了不少比较次数
那么希尔排序,如何在减少比较的次数,希尔排序中引入了步长的概念 ,比如:
[a,b,c,d,e,f,g,h,i,j,k.....]
在一开始的时候a 不和b 比较了,a 直接和g 比较,比如说a 是一个很大的数字和b 比较了大于b那么我和b进行交换位置,发现还大于c 那么又要和c交换位置,发现还大于d......一点点的往后挪动,这个过程自然增加了比较的次数和交换 次数
那么a和g比较,b 就和 h比较,.....从a 到g 这个中间的长度,称为步长,步长选择多少比较合适
合适的步长能够减少交换和比较的次数,
首位利用程序写出希尔排序算法的人,她采用的步长是 gap=length/2,后来很多人就感觉这个比较合适
那么我在此处也用c语言采用 gap=length/2作为步长,在之后每次步长 变化gap=gap/2
我们来看:
int xepx2(int arrs [],int size){
int gap=size/2;
int count=0;
int compare=0;
do {
for(int k=0;k<=size;k++){
for (int j = k + gap; j <size; j += gap) {
compare++;
if(arrs[k] > arrs[j]){
count++;
int temp = arrs[k];
arrs[k] = arrs[j];
arrs[j] = temp;
}
}
}
gap=gap/2;
}while(gap>0);
printf("一共转移了多少次元素%d,比较了%d \n",count,compare);
}
int xepx3(int arrs [],int size){
int gap=size/2;
int count=0;
int compare=0;
do {
for(int k=0;k<size/2;k++){
for (int j = k; j+gap<size; j += gap) {
compare++;
if(arrs[j] > arrs[j+gap]){
count++;
int temp = arrs[j];
arrs[j] = arrs[j+gap];
arrs[j+gap] = temp;
}
}
}
gap=gap/2;
}while(gap>0);
printf("一共转移了多少次元素%d,比较了%d \n",count,compare);
}
int main(){
int arrs [] ={13,19,20,2,1,6,5,9,12,7,8,3,10,11,11};
printf("原始数据:");
for(int i=0;i<sizeof(arrs)/4;i++){
printf("%d,",arrs[i]);
}
printf("\n");
xepx2(arrs,sizeof(arrs)/4);
printf("希尔之后:");
for(int i=0;i<sizeof(arrs)/4;i++){
printf("%d,",arrs[i]);
}
}
希尔排序的结果如下:
原始数据:13,19,20,2,1,6,5,9,12,7,8,3,10,11,11,
一共转移了多少次元素26次,比较了144次
希尔之后:1,2,3,5,6,7,8,9,10,11,11,12,13,19,20,
在这个地方我看看希尔排序和插入排序都是的区别在于希尔排序在第二个for循环里 j 每次变化的是gap步长的长度,并且在最外层增加了一个步长的循环
为了展示出希尔排序比插入排序更加的优越,我们来看一下插入排序的数据,相同数组:
int shellsort(int arrs [],int size){
int compare=0;
int count=0;
for(int i=0;i<size;i++){
for(int j=0;j<size;j++){
compare++;
if(arrs[i] < arrs[j]){
count++;
int temp = arrs[i];
arrs[i] = arrs[j];
arrs[j] = temp;
}
}
}
printf("一共转移了多少次元素%d,比较了%d \n",count,compare);
}
int main(){
int arrs [] ={13,19,20,2,1,6,5,9,12,7,8,3,10,11,11};
int length=sizeof(arrs)/sizeof(int);
printf("排序之前:");
for(int i=0;i<length;i++){
printf("%d,",arrs[i]);
}
printf("\n");
shellsort(arrs,length);
printf("排序之后:");
for(int i=0;i<length;i++){
printf("%d,",arrs[i]);
}
printf("\n");
return 0;
}
插入排序结果:
排序之前:13,19,20,2,1,6,5,9,12,7,8,3,10,11,11,
一共转移了多少次元素55,比较了225
排序之后:1,2,3,5,6,7,8,9,10,11,11,12,13,19,20,
从比较的次数和交换的次数来说,希尔排序整整比插入排序少了一半
上面我们对数组进行排序,步长采用的是length/2 并且依次的减半
其实我们还可以不采用这样的步长,对希尔排序进行优化,如何优化,通过采用合适的步长来进行优化,我们可以采用步长依次为{5,3,2,1}
为什么用{5,3,2,1},通过这样的步长设置,可以挪动最小的,比较最少
优化之后的希尔排序:
int xepx(int arrs [],int size){
int gaps[]={5,3,2,1};
int count=0;
int compare=0;
for(int j=0;j<4;j++){
int gap=gaps[j];
for(int i=0;i<gap;i++){
int l=i;
do{
compare++;
if(arrs[l]>arrs[l+gap]){
count++;
int temp=arrs[l];
arrs[l]=arrs[l+gap];
arrs[l+gap]=temp;
}
l=l+gap;
}while(l+gap<size);
}
}
printf("一共转移了多少次元素%d,比较了%d \n",count,compare);
}
int main(){
int arrs [] ={13,19,20,2,1,6,5,9,12,7,8,3,10,11,11};
printf("原始数据:");
for(int i=0;i<sizeof(arrs)/4;i++){
printf("%d,",arrs[i]);
}
printf("\n");
xepx(arrs,sizeof(arrs)/4);
printf("希尔之后:");
for(int i=0;i<sizeof(arrs)/4;i++){
printf("%d,",arrs[i]);
}
}
原始数据:13,19,20,2,1,6,5,9,12,7,8,3,10,11,11,
一共转移了多少次元素24,比较了49
希尔之后:1,2,3,5,6,8,7,9,10,11,11,12,13,19,20,
我们可以看到的是优化之后的希尔排序仅仅交换了24次,比较了49 次,完成了整个排序,这个比gap=length/2 转移26 次比较144次可以说优化了很多
我们来看一下这个排序的古城:
1.对步长进行遍历
2.根据步长找到找到一组数,对这一组数进行冒泡排序(下一节内容)
冒泡排序:它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这里的冒泡排序中相邻的元素 每次是增加1 ,在这个优化的算法中相邻的元素看作是 每次增加步长的元素
优化之后的算法可以看作是希尔算法和冒泡排序的优化,所以在有的文章中认为希尔排序是按照步长分割之后的数组,然后运用插入排序进行排序是没有真正的理解希尔排序,希尔并没有说获取到的新数组里面插入排序,也可以利用冒泡排序对新的数组进行排序
上面是对希尔排序方法的介绍,我们也可以将希尔排序算法看作是步长排序算法
希望对你有所帮助