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

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

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

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

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

目录

🐼前言

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

🐼排序

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

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

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

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

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

🐼直接插入排序

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

插入排序动态图:

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

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

🐼文末

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

相关推荐
大鱼>5 小时前
AI+货物追踪:全链路货物可视化追踪系统
人工智能·深度学习·算法·机器学习
大江东去浪淘尽千古风流人物5 小时前
【SLAM】slam高动态位姿估计全链路拆解
算法·面试·职场和发展·视觉里程计·slam·vio·大疆
想你依然心痛5 小时前
量子计算对嵌入式密码学的挑战与后量子算法准备
算法·密码学·量子计算
Jerry9 小时前
LeetCode 28. 找出字符串中第一个匹配项的下标
算法
Jerry11 小时前
LeetCode 459. 重复的子字符串
算法
海石13 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石13 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry15 小时前
LeetCode 151. 反转字符串中的单词
算法
gugucoding16 小时前
31. 【C语言】堆栈与队列的实现
c语言·开发语言·数据结构·链表
ChaoZiLL17 小时前
我的数据结构3——链表(link list)
数据结构·链表