【手撕排序1】希尔排序(直接插入排序)

🍃 如果觉得本系列文章内容还不错,欢迎订阅🚩

🎊个人主页:小编的个人主页

🎀 🎉欢迎大家点赞👍收藏⭐文章

✌️ 🤞 🤟 🤘 🤙 👈 👉 👆 🖕 👇 ☝️ 👍

目录

🐼前言

🌟在上一节我们实现了链式二叉树,如果感兴趣的小伙伴,可以阅读我的上一篇文章:> 二叉树,从这节开始小编带大家实现常见的排序算法

🐼排序

❄️在我们日常生活中,能见到各式各样的排序,比如成绩的排名,网页热度的排名,院校的排名等等,排序与我们的生活是密不可分的。

🔍 排序的概念:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作,旨在将一组无序的记录按照某一定义的标准(如数字大小、字母顺序等)调整为有序的序列。

🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟

我们来看看常见的排序算法有哪些?

本节内容我们先来介绍插入排序

🐼直接插入排序

✨直接插入排序是⼀种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到⼀个已经排好序的有序序列中,直到所有的记录插入完为止,得到⼀个新的有序序列。在 ***日常生活中,我们打扑克调整牌的顺序就用到了插入排序的思想。***

插入排序动态图:

📋直接插入排序的算法是:用i 遍历整个数组,end 为当前元素下标,用tmp 保存与end位置相隔为1 位置的值,如果当前位置的值(arr[end] )值(tmp )大,那么就让end位置的值(arr[end])覆盖下一个位置(arr[end+1] ),再让end走到前一个位置(end--),再拿当前值arr[end]和tmp比较,直到当前值arr[end]小于tmp或end<0,此时,让保存的小值tmp覆盖当前值的end的下一个位置arr[end+1]。

🐟该算法思想是,抽取当前元素,与保存位置的元素的元素比较,如果,当前元素小于要保存位置的元素,表明小值在前,大值在后,跳出循环;反之,每比较一次,就让大的元素向后走一步,tmp再与前一个位置的元素比较,将当前元素插入到比他大的元素之前。***如果能进入循环,说明tmp的值一定比end位置的值小,tmp找到不比end位置值小的地方,插入。***将大的元素往后方,小的元素tmp往前放。摸一张牌,找到比他大的牌前面,比他小的牌后面。
直接插入排序代码:

cpp 复制代码
//直接插入排序
void InsertSort(int* arr, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = arr[end + 1];//保存end的一个元素的值
		while (end>=0)
		{
			if (arr[end] > tmp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}

假设直接插入排序数组是:int arr[] = { 4,6,7,2};

🍂画图剖析:

测试数组为:int arr[] = { 4,6,7,2,1,8,9,5,3 ,0 };

🍀测试结果:

直接插入排序特性分析:
数组越有序,排序性能越优。
如果数组为降序,要进行多次比对插入,时间复杂度会很高
在交换过程中,不改变数据的原有相对顺序,是一种较为稳定的算法。
时间复杂度为O(N^2),空间复杂度O(1)

🐼希尔排序

🔍在直接插入排序中,如果遇到降序的数组(大的数据在前面,小的数据在后面)会很棘手。而希尔排序就可以优化直接插入排序此类问题。

📋希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定⼀个整数(通常是gap=n/3+1),把待排序文件所有记录分成各组,所有的距离相等的记录分在同⼀组内并对每⼀组内的记录进行排序,然后gap=gap/3+1得到下⼀个整数,再将数组分成各组,进行插入排序。这样,不断地将数组拆分成多个直接插入排序,经过多次预排序,保证了大的数据在后面,小的数据在前面。这样,再进行直接插入排序,效率就会很高了。
总结:当gap>1时,进行预排序,保证了(大的数据在后面,小的数据在前面)当gap==1时,就是直接插入排序

🍂希尔排序算法图:

希尔排序代码:

cpp 复制代码
//希尔排序
void ShellSort(int* arr, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;//4 2 1
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = arr[end + gap];//保存end+gap处的值
			while (end >= 0)
			{
				if (arr[end] > tmp)
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else {
					break;
				}
			}
			arr[end + gap] = tmp;
		}
	}
}

🌻代码解析

🍂画图剖析:

📋 假设有10组数据,gap的取值为4,2,1 。也就是说,进行三轮插入排序,第一轮和第二轮都进行预排序,每一轮都比前一轮更有序 ,当gap==1时,进行直接插入排序。在我们刚刚写的直接插入排序算法中,end每次的变化都是跳一步,而这里,由于我们以gap分组,end的变化都是以end±gap为单位跳跃的,但思想都是一样的,就是让tmp找到不比end位置值小的地方,插入,需要注意,由于gap的变化,end最多走到n-gap步,不然,再定位tmp,会造成数组越界。

🍐在遍历的过程中,尽管end是跳跃变化的,但我们循环定位的元素是从左向右依次遍历的,在减少了时间复杂度的同时,也进行了分组插入的效果。

👀为什么gap = gap/3+1?

如果gap过大,如gap=8,划分的组变少了,但是每组要比较的元素次数变多了。

而gap过小,虽然要比较的元素次数变少了,但是划分的组变多了,while循环变多了。

🍀测试结果:

希尔排序的性能分析:

🌽 希尔排序是直接插入排序的一种改进版本。但由于gap的未知,所以时间复杂度不好计算,希尔排序的时间复杂度受待排序数组的初始状态影响较大。在最坏情况下,其时间复杂度为O(n²),但在大多数情况下,其时间复杂度要优于直接插入排序。希尔排序的时间复杂度范围为O(nlogn)~O(n²)。通常情况下是O(N^1.3); ,空间复杂度为O(1).)

在排序过程中,可能会造成数据的原有相对顺序的改变,是一种非稳定的算法。**

🐼文末

感谢你看到这里,如果觉得本篇文章对你有帮助,点个赞👍 吧,你的点赞就是我更新的最大动力 ⛅️🌈 ☀️

相关推荐
The_Ticker13 分钟前
CFD平台如何接入实时行情源
java·大数据·数据库·人工智能·算法·区块链·软件工程
Lenyiin1 小时前
02.06、回文链表
数据结构·leetcode·链表
爪哇学长1 小时前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
爱摸鱼的孔乙己1 小时前
【数据结构】链表(leetcode)
c语言·数据结构·c++·链表·csdn
Dola_Pan1 小时前
C语言:数组转换指针的时机
c语言·开发语言·算法
繁依Fanyi1 小时前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
烦躁的大鼻嘎1 小时前
模拟算法实例讲解:从理论到实践的编程之旅
数据结构·c++·算法·leetcode
C++忠实粉丝2 小时前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
用户37791362947552 小时前
【循环神经网络】只会Python,也能让AI写出周杰伦风格的歌词
人工智能·算法
福大大架构师每日一题2 小时前
文心一言 VS 讯飞星火 VS chatgpt (396)-- 算法导论25.2 1题
算法·文心一言