【数据结构】第十六弹---C语言实现希尔排序
✨个人主页: 熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】
目录
1、希尔排序( 缩小增量排序 )
1.1、预排序实现
1.2、希尔排序代码实现
1.3、代码测试
1.4、时空复杂度分析
1.5、性能比较
总结
上一弹我们学习了直接插入排序,通过时空复杂度分析,时间复杂度为O(N^2),一般情况效率较低,有没有对直接插入排序进行优化的排序呢???没错,我们这一弹讲解的排序就是对直接插入排序的优化的排序!!!
1、希尔排序( 缩小增量排序 )
希尔排序是一种基于插入排序的算法,通过引入增量的概念来改进插入排序的性能
希尔排序法又称缩小增量法。希尔排序法的基本思想是:将原始列表分成多个子列表,先对每个子列表进行插入排序,然后逐渐减少子列表的数量,使整个列表趋向于部分有序,最后当整个列表作为一个子列表进行插入排序时,由于已经部分有序,所以排序效率高。这个过程中,每次排序的子列表是通过选择不同的“增量”来确定的。
动图如下:
实现思路
- 预排序
- 直接插入排序
1.1、预排序实现
预排序:
根据当前增量,数组被分为若干子序列,这些子序列的元素在原数组中间隔着固定的增量。对每个子序列应用插入排序。
假设当前增量为5:
首先,增量为5,我们将数组元素分为增量(5)个子序列,每个子序列由原数组中相隔增量位置上的元素组成。所以我们有如下子序列:
子序列1: 9,4
子序列2: 1,8
子序列3: 2,6子序列4: 5,3
子序列5: 7,5
然后对每个子序列进行独立的插入排序:
子序列1排序后:4,9
子序列2排序后:1,8
子序列3排序后:2,6子序列2排序后:3,5
子序列3排序后:5,7
一趟排序之后的数组:
4 1 2 3 5 9 8 6 5 7
完成了一轮希尔排序,此时整个数组并不完全有序,但是已经比原始的数组更接近有序了。然后减小增量,通常是将原来的增量除以2(或者除以3+1),现在选择下一个增量为 2,按照此排序规则继续预排序即可,直到增量为1时,则为直接插入排序,此时则排序完成。
一个子序列排序实现:
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;
与直接插入代码不同的是,这里对end所加减的均为gap;
单次插入完成后,我们来控制单个子序列的整个过程,每实现一次排序,下一次插入的数据为end+gap。
单趟排序实现:
int gap;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;
}
这里for循环的条件为 i <n-gap
防止数组越界.
完成单个子序列的排序后,我们再对整个子序列排序:
int gap;
for (int j = 0; j < gap; j++)
{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;}
}
外层循环(for (int j = 0; j < gap; j++))
意在对每个以gap为间隔的分组进行遍历。
优化:
这串代码三层循环的逻辑是按照每一组排序完成后再进行下一组排序的,事实上我们可以不需要最外层的循环。
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;
}
这里我们将原先代码中的i += gap
修改为i++
,意味着这次不是按照一组一组进行了,是一次排序完每个组的第二个元素,再进行下一个元素的排序。
1.2、希尔排序代码实现
我们先对预排序的增量进行分析:
gap越大,大的值更快调到后面,小的值更快调到前面,越不接近有序。
gap越小,大的值更慢调到后面,小的值更慢调到前面,越接近有序。
当gap为1,就是直接插入排序。
所以在实现希尔排序时,给gap固定值是行不通的。
因此,gap的值是应该随着n来变化的,实现多次预排。为了满足gap最终为1,博主推荐的方式是先将gap赋值成n,然后在排序的时候将gap赋值成gap/3+1(或者gap/2)。
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;//博主写的是/3+1也可以是gap/2for (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最终都会除以到值为1。
在这里:
gap>1时是预排序,目的让其接近有序。
gap=1时是直接插入排序,目的让其有序。
在gap=1时,已经十分接近有序了。
这里gap预排序次数还是有点多,因此我们可以再次进行修改,让gap每次除以3,为了使gap最后能回到1,我们进行加一处理。
注意:
1. 此处都是每隔gap进行插入。
2. gap不是一定为gap/3 + 1,也可以是gap /2 ,原因是当gap等于1的时候就是直接插入排序,进行一次排序即可变成有序,所以只要最后的gap为1都是可以的。
1.3、代码测试
测试代码:
//测试希尔排序
int main()
{int a[] = { 9,8,7,6,5,4,3,2,1,0 };//给一组数据int sz = sizeof(a) / sizeof(a[0]);//计算数组元素个数printf("排序前:\n");ArrayPrint(a, sz);ShellSort(a, sz);printf("排序后:\n");ArrayPrint(a, sz);return 0;
}
测试结果:
1.4、时空复杂度分析
希尔排序的时间复杂度并不固定,它依赖于所选择的间隔序列(增量序列)。直到今天,已经有多种不同的间隔序列被提出来,每种都有自己的性能特点。
《数据结构(C语言版)》--- 严蔚敏
《数据结构-用面相对象方法与C++描述》--- 殷人昆
时间复杂度:
因为咋们的gap是按照Knuth提出的方式取值的,而且Knuth进行了大量的试验统计,我们暂时就按照:O(N^1.25) 到 O(1.6* N^1.25) 来算。
空间复杂度:
插入排序的空间复杂度为O(1),因为它是一个原地排序算法,不需要额外的存储空间来排序。
1.5、性能比较
我们在前面一弹提到了clock()函数可以获取程序启动到函数调用时之间的CPU时钟周期数,我们在这里通过具体的排序算法来进行比较性能。
注意:clock()函数的头文件是#include<time.h>,时间的单位为毫秒。
性能比较的思想是通过比较两个函数所运行的时间大小。通过clock计算排序前的程序运行的时间,再计算排序结束程序运行的时间,时间的差值则为排序运行的时间。
尽量使用release模式进行测试,因为release效率更高。
测试代码:
void TestOP()
{srand(time(0));//随机数种子const int N = 100000;int* a1 = (int*)malloc(sizeof(int) * N);//动态开辟N个元素int* a2 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; ++i){a1[i] = rand() + i;//随机数只有3万,为了更加随机再加上ia2[i] = a1[i];}//clock计算程序运行到此时的时间 毫秒int begin1 = clock();//排序前程序运行时间InsertSort(a1, N);int end1 = clock();//排序后程序运行时间int begin2 = clock();ShellSort(a2, N);int end2 = clock();printf("InsertSort:%d\n", end1 - begin1);//程勋运行时间的差值即排序运行的时间printf("ShellSort:%d\n", end2 - begin2);free(a1);//释放空间free(a2);
}
当N为10万时,release版本测试出来的结果:
当N为100万时,release版本测试出来的结果:
明显能够看到希尔排序的效率比直接插入排序的效率高很多,当N为10万的时候,希尔排序是直接插入排序的18倍,当N为10万的时候,希尔排序是直接插入排序的20倍。
希尔排序的特性总结:
时间复杂度:O(N²)
空间复杂度:O(1)
稳定性:不稳定
复杂性:简单
总结
本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!
相关文章:

【数据结构】第十六弹---C语言实现希尔排序
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1、希尔排序( 缩小增量排序 ) 1.1、预排序实现 1.2、希尔排序代码实现 1.3、代码测试 1.4、时空复杂度分析 1.5、性能比较 总结 上一弹我们…...

用Python向Word文档添加页眉和页脚
用Python向Word文档添加页眉和页脚 添加页眉和页脚效果代码 添加页眉和页脚 在本文中,我们将用python向文档中添加页眉和页脚。 效果 添加前的文档: 添加页眉和页脚后: 代码 from docx import Documentdef add_header_footer(doc_path…...

REST风格
黑马程序员Spring Boot2 文章目录 1、REST简介1.1 优点1.2 REST风格简介1.3 注意事项 2、RESTful入门案例 1、REST简介 1.1 优点 隐藏资源的访问行为,无法通过地址的值对资源适合中操作书写简化 1.2 REST风格简介 按照RST风格访问资源时使用行为动作区分对资源进…...
Mongodb连接测试程序【Java版】
先导入Maven依赖 <dependency><groupId>org.mongodb</groupId><artifactId>mongodb-driver-sync</artifactId><version>4.9.0</version> </dependency>import com.mongodb.MongoClientSettings; import com.mongodb.MongoCred…...

SM3国密算法:优秀的密码散列函数
随着信息技术的飞速发展,信息安全已成为全球关注的焦点。密码学作为保障信息安全的核心技术,其重要性不言而喻。中国在密码学领域也取得了显著的成就,其中SM3国密算法就是中国自主设计并推广使用的密码学标准之一。 一、SM3算法概述 SM3算法…...

【安卓】在安卓中使用HTTP协议的最佳实践
人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…...

Spring Boot集成antlr实现词法和语法分析
1.什么是antlr? Antlr4 是一款强大的语法生成器工具,可用于读取、处理、执行和翻译结构化的文本或二进制文件。基本上是当前 Java 语言中使用最为广泛的语法生成器工具。Twitter搜索使用ANTLR进行语法分析,每天处理超过20亿次查询࿱…...

多线程中run()和start()的区别
我们知道,在多线程中 Thread thread new Thread(runnable); thread.start();以及 thread.run();都可以执行runnable中run方法下的代码,但是二者又有所不同 下面给出一段代码用以体现二者的区别: 以下代码中,通过thread.start()启…...
Nginx基础理论
Nginx最为最受欢迎的反向代理和负载均衡服务器,被广泛的应用于互联网项目中。这不仅仅是因为Nginx本身比较轻量,更多的是得益于Nginx的高性能特性,以及支持插件化开发,为此,很多开发者或者公司基于Nginx开发出了众多的…...

【QT5】<应用> 小游戏:贪吃蛇
文章目录 一、项目要求 二、需求分析 三、实现效果 四、代码 一、项目要求 【1】主要实现:游戏界面存在一条蛇🐍,使用键盘wsad或者↑↓←→键盘可以控制蛇的行走方向。同时界面中会随机出现食物,蛇可以吃食物,然后…...

【Webpack】使用 Webpack 构建 Vue3+TS 项目
构建项目目录 tsc --init npm init -yshim.d.ts 文件是一个类型声明文件,用于告诉 TypeScript 编译器如何处理 Vue 的单文件组件(SFC)和其他自定义模块。为 Vue 的单文件组件和其他非 TypeScript 模块提供类型信息,以便在 TypeScr…...

数据防泄漏的六个步骤|数据防泄漏软件有哪些
在当前复杂多变的网络安全环境下,数据防泄漏软件成为了企业信息安全架构中不可或缺的一环。下面以安企神软件为例,告诉你怎么防止数据泄露,以及好用的防泄露软件。 1. 安企神软件 安企神软件是当前市场上备受推崇的企业级数据防泄漏解决方案…...

SpringCloud 网关Gateway配置并使用
目录 1 什么是网关? 2 Gateway的使用 2.1 在其pom文件中引入依赖 2.2 然后gateway配置文件中配置信息 2.3 启动网关微服务 3 网关处理流程 4 前端-网关-微服务-微服务间实现信息共享传递 1 什么是网关? 网关:就是网络的关口ÿ…...

MySQl基础----Linux下搭建mysql软件及登录和基本使用(附实操图超简单一看就会)
绪论 涓滴之水可磨损大石,不是由于他力量强大,而是由于昼夜不舍地滴坠。 只有勤奋不懈地努力,才能够获得那些技巧。 ——贝多芬。新开MySQL篇章,本章非常基础包括如何在Linux上搭建(当然上面的SQL语句你在其他能执行…...
PostgreSQL17优化器改进(4)允许UNION(没有ALL)使用MergeAppend
PostgreSQL17优化器改进(4)允许UNION(没有ALL)使用MergeAppend UNION存在的问题 到PostgreSQL16.3版本为止,UNION执行计划通常不是最优的,优化器有两种处理方法: 优化器只考虑使用Append节点并通过使用Hash Aggregate,Append -…...

SSM 基于大数据技术的创业推荐系统-计算机毕业设计源码02979
摘 要 科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代。在现实运用中,应用软件的工作…...

基于WPF技术的换热站智能监控系统03--实现左侧加载动画
1、左侧布局规划 左侧分5行,每行的高度通过height属性来指定,1.2*表示占1.2倍的宽度 2、创建用户控件 在WPF中想要进行个性化处理,主要可以通过三个方面来实现:控件模板(控件模板、数据模板、数据容器模板)…...

4D毫米波雷达技术及发展
文章目录 前言一、4D毫米波雷达是什么?二、毫米波雷达是什么?毫米波雷达的基本原理多普勒效应 前言 现阶段自动驾驶技术中,主要用到的传感器有摄像头、激光雷达和毫米波雷达。 摄像头的光谱从可见光到红外光谱,是最接近人眼的传感…...
请解释Java Web应用的开发流程,包括前后端分离和交互方式。请解释Java中的锁分离技术,并讨论其在提高并发性能方面的作用。
请解释Java Web应用的开发流程,包括前后端分离和交互方式。 Java Web应用的开发流程是一个涵盖多个阶段的过程,这些阶段从需求分析开始,经过设计、编码、测试,最终到部署和维护。在这个过程中,前后端分离成为现代Web应…...
selenium使用已经打开的浏览器
Selenium 本身不支持直接连接到一个已经打开的浏览器页面。Selenium 启动的浏览器实例是一个全新的会话,它与手动打开的浏览器页面是分开的。但是,有一些变通的方法可以实现类似的效果。 一种方法是通过附加代理连接到已经打开的浏览器。下面是如何实现…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...

springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...