【数据结构初阶】C语言从0到1实现希尔排序

 🌟hello,各位读者大大们你们好呀🌟

🍭🍭系列专栏:【数据结构初阶】

✒️✒️本篇内容:深入剖析希尔排序

🚢🚢作者简介:计算机海洋的新进船长一枚,请多多指教( •̀֊•́ ) ̖́-

📡📡同期数据结构文章:【数据结构初阶】C语言从0到1带你了解直接插入排序

前言

需要直接插入排序的基础,才能更好的理解文章,详情可见上栏同期数据结构文章

希尔排序法又称缩小增量法,希尔排序法的基本思想是:

先选定一个整数,把待排序文件中所有数分成 gap 个组,所有距离为 gap 的数分在同一组内,并对每一组内的数先进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

1.先进行预排序,将一组数分为 gap 组,对间隔为 gap 的数进行直接插入排序

void ShellSort(int* a, int n)
{
    int gap;
	int end;
	int tmp = a[end + gap];
	while (end >= 0)
	{
		if (a[end] > tmp)
		{
			a[end + gap] = a[end];
			end -= gap;
		}
		else
		{
			break;
		}
	}

	a[end + gap] = tmp;
}

至此,我们已经完成了一次插入排序,也就是可以将红色组的9、5变得有序。接下来就是加入循环,完成多次插入排序,使间隔为 gap 的数组成的数组有序,以下图红色组为例,就是将 9,5,8,5 变得有序


 2.加入循环,完成多次插入排序,使间隔为 gap 的数组成的数组有序

  • 增加一个for循环,对红色组进行排序
  • n为数组长度,i < n - gap 避免越界(若 i<n,end可以指到数组尾部,tmp将出界)
		int gap = 3;
        for (int i = 0; i < n - gap; i += gap)
		{
			// [0,end] 插入 end+gap [0, end+gap]有序  -- 间隔为gap的数据
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}

至此,我们第一组(红色组)就排完了,数组将会变成 5,1,2,5,7,4,8,6,3,9


3.再加一组循环,循环 gap 次,使 gap 组的数都变得有序(以下数组为例,就是让红、蓝、绿组都变得有序)

  • 创建一个for循环,对不同颜色组排序
  • 在这里,j = 0,排红色组;j = 1,排蓝色组;j = 2,排绿色组

void ShellSort(int* a, int n)
{
	int gap = 3;
	for (int j = 0; j < gap; ++j)
	{
		for (int i = j; i < n - gap; i += gap)
		{
			// [0,end] 插入 end+gap [0, end+gap]有序  -- 间隔为gap的数据
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}	


4.算法优化,三层循环变两层循环(时间复杂度相同,所以写上面的这种也行)

  • 只需要将第二层循环 i += gap 变为  ++i 即可,可以理解红组,然后红组9,5拍完,蓝组1,7排,蓝组排完绿组2,4排,以此类推。
  • 不同于上面先给红组排完,再给蓝组排,蓝组排完,再给绿组排

void ShellSort(int* a, int n)
{
	int gap = 3;
	for (int i = j; i < n - gap; ++i)
	{
		// [0,end] 插入 end+gap [0, end+gap]有序  -- 间隔为gap的数据
		int end = i;
		int tmp = a[end + gap];
		while (end >= 0)
		{
			if (a[end] > tmp)
			{
				a[end + gap] = a[end];
				end -= gap;
			}
			else
			{
				break;
			}
		}
		a[end + gap] = tmp;
	}
}	

至此,我们已经完成了预排序, 完成了数组接近有序的目标,大大降低了对每个数直接排序的时间复杂度


5.对每个数进行直接插入排序,我们可以通过控制 gap 的值,来区分预排序和直接插入排序

  • 当 gap == 1 时,这个算法就是直接插入排序
  • 我们可以设 gap = n,再通过循环,不断缩小gap的值,不断预排序,使其最后变为直接插入排序
  • 通过大量实验我们可以得出,gap / 2,或者 gap / 3+1 时,预排序的效果最好
  • gap / 3+1 是为了保证除到最后的数=1,如3/3+1=1,4/3+1=2,2/3+1=1
	// gap > 1  预排序
	// gap == 1 直接插入排序

// O(N^1.3)
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		//gap = gap / 2;
		gap = gap / 3 + 1;    //+1是为了保证除到最后的数=1

		// [0,end] 插入 end+gap [0, end+gap]有序  -- 间隔为gap的数据
		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}


6.我们将上述代码补充完整,进行一次实验

sort.h

#include <stdio.h>
#include <stdlib.h>

void ShellSort(int* a, int n);

 sort.c

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

	// gap > 1  预排序
	// gap == 1 直接插入排序

// O(N^1.3)
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		//gap = gap / 2;
		gap = gap / 3 + 1;    //+1是为了保证除到最后的数=1

		// [0,end] 插入 end+gap [0, end+gap]有序  -- 间隔为gap的数据
		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

test.c

void TestShellSort()
{
	//int a[] = { 9,8,7,6,5,4,3,2,1,0 };
	int a[] = { 34, 56, 25, 65, 86, 99, 72, 66 };
	ShellSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}


int main()
{
    TestShellSort();
	return 0;
}

结果如下  


希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定,我们可以使用Knuth的取值或者大概平均值O(n^1.3),对比下略差于O(n*logn)的算法
  4. 稳定性:不稳定

《数据结构-用面相对象方法与C++描述》--- 殷人昆


🌹🌹希尔排序排序大概就讲到这里啦,博主后续会继续更新更多数据结构的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪 

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值