【C语言】Shell排序(希尔排序)---详细讲解,由浅入深。

什么是希尔排序?

其实,希尔排序主要就是分组插入,即将一组数据分组然后使用插入排序来实现的。主要分为两步走:1.分组直接插入预排序     2.分组直接插入预排序结束后的整体直接插入排序

经过这两步之后,其数据也就排序好了。

Shell排序的效率其实很高的,尤其在对大量数据进行排序的时候就容易体现出来了。

分组直接插入预排序

 那么,我们首先该如何来先对一组数据进行分组呢?

假设,我们想将这组数据分成三组,设gap = 3

那么,接下来我们可以同时将间距gap的作为一组数据。其每组数据的起始位置分别是数组下标为0,1,2这三个。

 如下图:

 这样,我们就迈出了第一步了,将数据分好组了。

分别为:

              黑色:7     9       8      3

             

              红色:10     2      6

              蓝色:1       5      4

接下来就是插入预排序了。

这里,我们先以黑色的一组数据也就是7 9 8 3先来作为一组数组来对其进行直接插入排序

关于直接插入排序的博客请参考上一篇博客:C语言】直接插入排序_吃饭爱喝水的博客-CSDN博客

对黑色一组数据进行直接插入排序的核心代码:

(****end的最初位置在数组下标为6的位置上,并且 n-gap的值就是end能取到的的最大值)

        int gap;

		//a[0]~a[end]有序,把数组下标为end + 1的值插入数组后,数组仍保持有序。
		int end;
		int tmp = a[end + gap];

		while (end >= 0) {

			if (tmp < a[end]) {

				a[end + 1] = a[end];
				end -= gap;

			}
			else {
				break;
			}
		}

		a[end + gap] = tmp;

最终结果如下图:

剩下的两个红色一组和蓝色一组的数据的排序和黑色这一组类似,就不过多重复了。

三组数据分别插入排序整体代码:

void ShellSort(int* a, int n) {
	
	int gap = 3;

	//对分成的gap组数据的分别插入排序
	for (int i = 0; i < gap; i++) {
		
		//对其中一组数据的直接插入排序
		for (int j = 0; j < n - gap; j += gap) {//***当gap等于1时,就是一个直接插入排序
			// n-gap 就是end能取到的的最大值

			//a[0]~a[end]有序,把数组下标为end + gap的值插入数组后,数组仍保持有序。
			int end = j;
			int tmp = a[end + gap];

			while (end >= 0) {

				if (tmp < a[end]) {

					a[end + 1] = a[end];
					end -= gap;

				}
				else {
					break;
				}
			}

			a[end + gap] = tmp;
		}


	}

}

三组数据交替插入排序整体代码(与三组数据分别插入排序整体代码产生最终结果一致):

for (int j = 0; j < n - gap; j++){
			
      
			int end = j;
			int tmp = a[end + gap];

			while (end >= 0) {

				if (tmp < a[end]) {

					a[end + 1] = a[end];
					end -= gap;

				}
				else {
					break;
				}
			}

			a[end + gap] = tmp;
		}

三组数据都分别直接插入排序最终结果为:分组插入预排序的最终结果 

 3,2,1,7,10,4,9,6,5,8.

这就是分组插入预排序的最终结果。

分组直接插入预排序结束后的整体直接插入排序

排升序的时候,gap的值如果越大,那么其大的值会很后面去,小的值也同样会很的到前面来。但是这样的话,会导致预排序结果也就越混乱

gap的值越,预排序的结果混乱度也就越,越接近有序

并且当gap等于1时,也就是直接插入排序了。

因此,我们可以将其代码写为:

void ShellSort(int* a, int n) {
	
	int gap = n;

	//当 gap>1时,就是对数据进行了分组预排序
	//当gap ==1时,就是对分组预排序的结果进行直接插入排序了
	while (gap >= 1) {

		gap = (gap / 3) +  1;//除以三是为了使gap的值不至于太大从而导致预排序的结果比较混乱
		                    //并且最后加1是为了确保最后 gap == 1 ,从而进行最后的直接插入排序

		//对分成的gap组数据的分别插入排序
		for (int i = 0; i < gap; i++) {

			//对其中一组数据的直接插入排序
			for (int j = 0; j < n - gap; j += gap) {//***当gap等于1时,就是一个直接插入排序
				// n-gap 就是end能取到的的最大值

				//a[0]~a[end]有序,把数组下标为end + gap的值插入数组后,数组仍保持有序。
				int end = j;
				int tmp = a[end + gap];

				while (end >= 0) {

					if (tmp < a[end]) {

						a[end + 1] = a[end];
						end -= gap;

					}
					else {
						break;
					}
				}

				a[end + gap] = tmp;
			}


		}
	}

}

这里,我们就可以将上面的:

 3,2,1,7,10,4,9,6,5,8.

分组插入预排序的最终结果

其实,这个部分也就是gap>1时循环的最终结果。

接下来,当gap循环到成为1 时,就将这组数据进行直接插入排序,从而完成整个数据的排序。

到这,Shell排序就完成了。

简单小结:

其实,Shell排序就分为两步走。

首先,先对整个数剧分为gap组。分组完了后,再分别对每一组数据进行直接插入排序。完成后,数据整体上就转成为了一个相对有序的数据了。当然,还是有些个地方不是有序的。

然后,这时,再对其进行最后一次,也就是gap == 1时,进行直接插入排序。从而使整个数据转成为有序了。到这时,Shell排序也就完成了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值