[C/C++]数据结构 希尔排序

🥦前言:

        希尔排序也称 “缩小增量排序”,它也是一种插入类排序的方法,在学习希尔排序之前我们首先了解一下直接插入排序.

一: 🚩直接插入排序

1.1 🌟排序思路

        直接插入排序的基本原理是将一条记录插入到已排好的有序表中,从而得到一个新的、记录数量增1的有序表,其思路就和我们摸扑克牌一样,每摸到一张牌按照大小把他插入到对应位置,这样等摸完全部的牌时,我们手里的牌就是有序的

动态图解:

        首先我们把数组的第一个元素看作有序的,将第二个元素插入到与第一个元素对应的位置,再将数组的第三个元素插入到相对于数组前两个元素对应的位置,直到最后一个元素插入到对应位置 

代码演示: (排升序)

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
        //首先保存一下待插入的元素
		int tmp = a[end + 1];

        //比较的最坏情况时end=0
		while (end >= 0)
		{

            //若待排序元素比a[end]小,则让a[end]向后移动腾位置
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
                //由于本来数组就为有序数组,当不满足上述条件时,说明找到了待插入的位置
				break;
			}
		}
        //插入数据
		a[end + 1] = tmp;
	}
}

1.2 💬直接插入排序特点

  • 直接插入排序的时间复杂度为O(N^2),若待排序表为有序的则时间复杂度为O(N)
  • 待排序表越接近有序则时间复杂度越小
  • 空间复杂度为O(1),是一个稳定的排序算法
  • 与同为时间复杂度为O(N^2)的冒泡排序相比,若待排序数组为有序的,虽然冒泡排序不需要挪动数据,但是其时间复杂度依旧为O(N^2),所以直接插入排序在特定情况下还是优于冒泡排序的

二: 🚩希尔排序

2.1 🌟排序思路

  1. 预排序:先将数组分组,将每个组排序,目的是让整个数组相对有序
  2. 插入排序:待数组相对有序后,进行直接插入排序,这样效率比较高

图解:

假设待排序数组为[9 8 7 6 5 4 3 2 1]

1.我们将他们每隔三个坐标分为一组,假设gap=3

2.对三个数组分别进行直接插入排序,使数组整体相对有序

3.对数组整体进行插入排序


对于代码的理解我们一步一步来

首先我们先理解对红色的一组进行预排序

//希尔排序
void ShellSort(int* a, int n)
{
	int gap = 3;
 
	//要注意i的范围要控制到n-gap以内,因为当end大于等于n-gap时,tmp会越界             
	for (int i = 0; i < n - gap; i += 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;
	}
}

要对红蓝绿三组都进行排序的话,直接在外层套个循环即可,我们会发现定义gap为几最后数组就会被分割为几组

//希尔排序
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)
		{
			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;
		}
	}
}

上述代码是排完一组在排一组,其实也可以直接一次性把三组数据全部排好

//希尔排序
void ShellSort(int* a, int n)
{
	int gap = 3;
	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;
	}
}

到了这里数组就预排好了,但是我们得思考一个问题:

        由于我们排序的数组的长度都是不固定的,那直接把gap定死就是不合适的,gap越大,较大的数就能更快的排到后面,gap越小预排出来的数组就越接近有序,当gap=1时就为直接插入排序,所以gap应该随n的大小而变化,并且gap最后应该为1.

        对于希尔增量到底怎么取也没有一个最优的值刚开始有人认为gap=gap/2,但是后来有人认为这样处理预排出来数组还是不够有序,后来又提出了gap=gap/3+1 ,  (+1)是为了确保gap最终能变为1

希尔排序完整代码:

void ShellSort(int* a, int n)
{
    //首先将gap定为n
	int gap = n;

	/*while (gap > 1)
	{
		gap = gap / 3 + 1;

		for (int j = 0; j < gap; j++)
		{
			for (int i = j; i < n - gap; i += gap)
			{
				int end = i;
				int tmp = a[end + gap];
				while (end >= 0)
				{
					if (tmp < a[end])
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
					{
						break;
					}
				}
				a[end + gap] = tmp;
			}
		}

	}*/

	while (gap > 1)
	{
		gap = gap / 3 + 1;

		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

2.2 💬希尔排序特点

  • 希尔排序其实是对直接插入排序的优化,其效率更高
  • 当gap>1时,为预排序目的是让数组相对有序,当gap=1时,为直接插入排序,目的是让数组有序
  • 希尔排序的时间复杂度不容易计算,但是有专业人士算出希尔排序的时间按复杂度大概为O(N^1.3)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/270549.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

EDSR训练及测试教程

EDSR训练及测试教程 超分重建经典算法EDSR开源代码使用教程。 论文名称:Enhanced Deep Residual Networks for Single Image Super-Resolution,CVPR2017。 训练自己的数据集 由于EDSR开源代码只针对DIV2K数据集,在数据集加载时很多代码已经固定,因此在这里使用固定的文…

Android Studio 如何实现软件英文变中文教程

目录 前言 一、确认版本号 二、下载汉化包 三、汉化包安装 四、如何实现中英文切换 五、更多资源 前言 Android Studio是一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;用于开发Android应用程序。默认情况下&#xff0c;Android Studio的界面和…

STM32实战之深入理解I²C通信协议

目录 IC的物理层 IC的协议层 IC特点 IC 总线时序图 软件模拟IC时序分享 例程简介 例程分享 STM32的IC外设 IIC&#xff08;Inter-Integrated Circuit&#xff09;&#xff0c;也称为IC或TWI&#xff08;Two-Wire Interface&#xff09;&#xff0c;是一种广泛使用的串行…

数据仓库【1】:简介

数据仓库【1】&#xff1a;简介 1、诞生背景1.1、数据仓库诞生原因1.2、历史数据积存1.3、企业数据分析需要 2、基本概述2.1、数据仓库&#xff08;Data Warehouse&#xff0c;DW&#xff09;2.2、数据仓库特点2.3、数据仓库 VS 数据库 3、技术实现3.1、数据仓库建设方案3.2、传…

『CVE』简析CVE-2023-48795:SSH协议前缀截断攻击(Terrapin攻击)

文章目录 OpenSSH 9.6更新公告Terrapin攻击 (CVE-2023-48795)基本信息利用手段利用路径利用条件利用原理及示意图危害Terrapin-Scanner 基于Terrapin的潜在风险&#xff1a;CVE-2023-46445 & 46446参考完 OpenSSH 9.6更新公告 *ssh(1), sshd(8): implement protocol extens…

第十六节TypeScript 类

1、简介 TypeScript是面向对象的JavaScript。 类描述了所创建的对象共同的属性与方法。 2、类的定义 class class_name { // 类作用域 } 定义类的关键字是class&#xff0c;后面紧跟类名&#xff0c;类可以包含以下几个模块&#xff1a; 字段 – 字段是类里面声明的变量。字…

java练习之abstract (抽象) final(最终) static(静态) 练习

1&#xff1a;分析总结&#xff1a;写出private、abstract、static、final之间能否联动使用&#xff0c;并写出分析原因 private static final 之间可以任意结合 abstract 不可以与private static final 结合使用 2&#xff1a;关于三个修饰符描述不正确的是(AD) A. static …

实习知识整理8:如何实现将商品加入购物车

情景分析&#xff1a;当我们进入商品详情页面时&#xff0c;一般会有两个按钮&#xff0c;一个是加入购物车&#xff0c;另一个是直接购买的按钮&#xff0c;我们先来看看加入购物车是如何实现的 1. 数据库表分析 需要3个表&#xff1a;商品表item、用户表user、购物车表cart 需…

基于JAVA的医院门诊预约挂号系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 功能性需求2.1.1 数据中心模块2.1.2 科室医生档案模块2.1.3 预约挂号模块2.1.4 医院时政模块 2.2 可行性分析2.2.1 可靠性2.2.2 易用性2.2.3 维护性 三、数据库设计3.1 用户表3.2 科室档案表3.3 医生档案表3.4 医生放号…

Autosar CAN开发05(从实际应用认识CAN波特率)

建议同时阅读本专栏的&#xff1a; Autosar CAN开发03&#xff08;从实际应用认识CAN总线的物理层&#xff09; Autosar CAN开发04&#xff08;从实际应用认识CAN报文&#xff09; Autosar CAN开发05&#xff08;从实际应用认识CAN波特率&#xff09; 前言 当知道了CAN的物…

STM32MP157D-DK1开发板Qt镜像构建

上篇介绍了STM32MP57-DK1开发板官方系统的烧录。那个系统包含Linux系统的基础功能&#xff0c;如果要进行Qt开发&#xff0c;还需要重新构建带有Qt功能的镜像 本篇就来介绍如何构建带有Qt功能的系统镜像&#xff0c;并在开发板中烧录构建的镜像。 1 Distribution包的构建 ST…

优化模型:MATLAB整数规划

一、整数规划介绍 1.1 整数规划的定义 若规划模型的所有决策变量只能取整数时&#xff0c;称为整数规划。若在线性规划模型中&#xff0c;变量限制为整数&#xff0c;则称为整数线性规划。 1.2 整数规划的分类 整数规划模型大致可分为两类&#xff1a; &#xff08;1&…

HAL库的常用库函数(根据学习而更新)

目录 一、常用的GPIO相关HAL库函数 1、GPIO的初始化 2、配置GPIO引脚输出电平 3、切换指定引脚的电平&#xff0c;电平的翻转 4、读取指定GPIO引脚的电平 5、结构体 GPIO_InitTypeDef &#xff08;引脚&#xff09;定义&#xff1a; 6、高低电平的表示 7、延时函数&…

Java架构师系统架构需求分析实战

目录 1 导语2 需求分析实战3 核心方法论-架构立方体4 功能性模型-模块定义5 功能性模型-模块关系图6 功能性模型-模块细化 想学习架构师构建流程请跳转&#xff1a;Java架构师系统架构设计 1 导语 架构设计的实战和思维方法的讨论&#xff0c;主要聚焦于需求分析的重要性和方…

buuctf-Misc 题目解答分解97-99

97.[BSidesSF2019]zippy 下载完就是一个流量包 追踪tcp nc -l -p 4445 > flag.zip unzip -P supercomplexpassword flag.zip Archive: flag.zip 压缩包密码 supercomplexpassword 保存为 flag.zip 解压得到flag 98.[GUET-CTF2019]虚假的压缩包 先从虚假的压缩包入手 &am…

逆向P1P2总结

字节八位 word 16位 deword 32 位 00 00 00 e8 是存储32位信息的起点 不是程序运行的起点 为什么电脑有32位与64位之分 寻址宽度 以字节为单位 0xfffffff 1 就是最大容量 转为十进制为 4294967296 / 1024 &#xff08;k&#xff09;/1024 &#xff08;kb&#xff09;/ 1…

软件测试面试八股文——基础篇

5&#xff09;错误推测法&#xff1a;是基于经验和直觉推测程序中所有可能存在的各种错误&#xff0c;从而有针对性的设计测试用例的方法 6&#xff09;正交实验法 7&#xff09;判定表法 8&#xff09;测试大纲法 3、提交缺陷的八大要素 1&#xff09;缺陷编号&#xff1a…

数据通信网络基础华为ICT网络赛道

目录 前言&#xff1a; 1.网络与通信 2.网络类型与网络拓扑 3.网络工程与网络工程师 前言&#xff1a; 数据通信网络基础是通信领域的基本概念&#xff0c;涉及数据传输、路由交换、网络安全等方面的知识。华为ICT网络赛道则是华为公司提出的一种技术路径&#xff0c;旨在通…

合并的单元格如何填充连续的序号

希望你以后碰到合并的单元格&#xff0c;不在一个个输入序号&#xff0c;用以下操作帮你输入连续的序号。 一、操作过程如下 1.有一个基准的单元格在同一列&#xff0c;而且这个基准单元格必须得是序号为1的单元格的上面的一个单元格&#xff0c;这样的话后面才能自动递增&am…

Cesium.js三维地图的实现(依托天地图CDN文件)

零、技术选型&#xff1a; Vue2、VueCli5、天地图、Cesium.js 一、通过天地图官网案例实现 需要引入天地图官方提供的CDN链接访问Cesium.js相关文件 相关文件&#xff1a; https://api.tianditu.gov.cn/cdn/demo/sanwei/static/cesium/Cesium.js https://api.tianditu.gov.cn/…