希尔排序原理

目录:

一、希尔排序与插入排序

        1)希尔排序的概念

        2)插入排序实现   

二、希尔排序实现


一、希尔排序与插入排序

        1)希尔排序的概念

        希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。

        希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。


        2)插入排序实现   

        既然希尔排序是插入排序的优化,那么我们有必要先了解一下插入排序的过程,基本操作是将需要进行排序的元素插入到已排序区当中,这样每次插入都会使已排序区长度加一。

        直观的看,插入排序的操作就和我们在打扑克牌时一样,我们默认将小的或者大的往一边插进去,插入排序也是如此。

         1、我们从第二个元素开始插入排序,因为这样左边只有一个数,必然有序,我们把左边的称为已排序区,右边的称为待排序区。

        2、将待排序区的第一个元素向已排序区插入,将其与已排序区元素从后向前比较,将其插入到合适位置,已排序区元素个数+1。

        3、然后待排序区重复2的步骤向已排序区从后往前比较,找到合适位置插入。

        4、 继续将待排序区元素插入到已排序区,当待排序区元素为0时,这组数据就已经排序完成。

        我们明白了插入排序的过程,接下来就是实现插入排序了,我们先来分析,插入排序中第一个元素(0位置处)本来就是有序的,所以我们直接从第二个元素开始操作(1位置处)。

        1、定义待排序区的首元素下标为end,用tmp记录下end下标的元素,将tmp与已排序区元素进行比较,发现小于5,则将待排序区的元素插入到首元素位置。

        2、已排序区数组元素加一,待排序区首元素变为3,end也变为3的下标,tmp记录此元素的值,将tmp与已排序区元素进行比较,首先与5比较,小于5。

         3、再跟1比较发现大于1,那么这个值就插入在1和5之间,已排序元素加一,待排序数组元素减一。

        4、一直刷新end与tmp值,与已排序区进行从右往左的比较,比较到合适的位置才进行插入,而不是每次比较都插入元素。

        时间复杂度最坏情况下为 O(N^2),此时待排序列为逆序或者说接近逆序最好情况下为 O(N)此时待排序列为升序,或者说接近升序。平均为O(N^2)

        空间复杂度:没有额外使用空间,所以空间复杂度为 O(1)

        代码实现:

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

void InsertSort(int *a, int len)//插入排序
{
	int i = 0;
	for(i = 1 ; i < len ; i++)//从下标为1的位置进行插入排序
	{
		int end = i;//用end记录待排序区的首元素下标
		int tmp = a[end];//用tmp记录待排序区首元素的值
		while(end > 0)//保证不越界tmp就一直往前进行比较,找到合适的位置break
		{
			if(a[end - 1] > tmp)
			{
				a[end] = a[end - 1];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end] = tmp;//最后在将tmp值放在end的下标下
	}
}

void Print(int *a, int len)//打印数组元素
{
	int i = 0;
	for(i = 0 ; i < len ; i++)
	{
		printf("%3d",a[i]);
	}
	printf("\n");
	return;
}

void Test()//测试
{
	int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
	int len = sizeof(a) / sizeof(int);
	InsertSort(a, len);
	Print(a, len);
	return;
}

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

运行结果:


二、希尔排序实现

         希尔排序法又称为缩小增量法。希尔排序法的基本思想是:首先选定一个整数,把待排序文件中所有记录分成gap个组(增量),所有距离为gap的数据记录在同一组内,并对每一组内的记录进行排序。然后,再取gap/2个组(缩小增量),重复上述分组和排序的工作。当gap == 1时所有记录在统一组内排好序

        注希尔排序缩小增量在数学上是个难题,大家经常用的就是gap/2。

         我们有这样一个数组:a[] = {6, 1, 5, 2, 4, 8, 3, 7, 9}。我们对这个数组进行排序,首先假设设置gap的值为3,那么这组数就会分为三组:

        接下来控制这三组,每组分别进行插入排序,结果为:

        那么gap为3时的所有组已经排完了,接下来就该缩小增量了,gap /= 2,gap == 1:

        知晓了希尔排序是如何进行数据管理的,下面来看看具体的操作是如何完成的:

        1、首先, 我们需要对gap进行控制,在gap>0范围内,每次分组后的所有组排完序之后都要除以二,可以用while循环来控制gap的大小:

void ShellSort(int *a, int n)
{
	assert(a);
	int gap = n;
	int i = 0;
	while(gap > 1)
	{
		if(gap > 1)
		gap /= 2;
        //..分完组后的预排序	    	
    }
}

        2、我们已经将缩小增量设置好了,接下来只需要把每次分完组都进行排序,也就是预排序。如何进行预排序呢?既然希尔排序是插入排序的优化,我们不妨以插排的思路对希尔预排序进行调整。

        用for循环对所有数据进行预排序,值得注意的是这里不会像插排那样循环到n,我们只需要限制在n - gap 的范围就行了,例如上图:

        这个数组从3往后就不需要排了,因为在每一组的排序中最后一个值都是被拍过序的,没必要再次进行一次排序,总共为n个数据,那么就是只需要n - gap - 1个数据进行排序。则:

void ShellSort(int *a, int n)
{
	assert(a);
	int gap = n;
	int i = 0;
	while(gap > 1)
	{
		if(gap > 1)
		gap /= 2;
        for(i = 0 ; i < n - gap ; i++)//控制n - gap数据进行预排序
        {
            //具体排序过程...
        }
    }
}

         3、其实预排序的实现和直接插入排序的过程几乎是完全相似,前面也说了当希尔排序的缩小增量为1时,和插入排序没区别,也就是说,插入排序每次都对相邻的数据处理,而希尔排序是将分好的组看成新的数组,例如上面数据的6, 2, 3为一组,我们可以看成其他的数据不存在,只有这一组存在,那么对于这一组而言,希尔排序就是插入排序,将上图的三组都排完序,这一趟预排序就算完成了。

        与插入排序相同,定义一个end记录当前元素下标,定义一个tmp记录a[end + gap]处的值,为什么不是a[end]处的值?可别忘了第一个值是默认有序的,所以要从第二个值向前比较,当end对应的值要大于tmp那么就将end处的值赋给下一个位置,也就是end+gap处,当不满足end处的值大于end+gap时,代表前面已经没有比自己大的值了,直接break,最后在循环结束的时候记得将a[end + gap]之前被覆盖的地方重新赋值:

void ShellSort(int *a, int n)
{
	assert(a);
	int gap = n;
	int i = 0;
	while(gap > 1)
	{
		if(gap > 1)
		gap /= 2;
		for( i = 0; i < n - gap; i++)//对n组数据进行n - gap次预排序
		{
			int end = i;
			int tmp = a[gap + end];
			while(end >= 0)//当end >= 0时候对每组进行预排序
			{
				if(tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
	return;
}

        这样希尔排序就完成了,其实在希尔排序的过程中,或许你还有疑问,为什么for循环这里是连续的?不是进行分组了吗?其实你仔细想想, 我们还是以上面gap==3为例,首先是第一个数据,就是对第一组的首个数据进行排序,当到了第二个数据的时候,就是对第二组首个数据进行排序,但是因为有gap的控制,这两组数据其实是互不影响的,所以连续的遍历数据进行预排序也是没有问题的。

        总结希尔排序的特性:

        1、希尔排序是对直接插入排序的优化。 

        2、当gap > 1时,都是预排序,目的是让数组更接近有序,当gap==1时,将前面预排序的结果进行直接插入排序而完成排序。

        时间复杂度O(NlogN)(近似),因为增量问题并不能准确得出时间复杂度。

        空间复杂度:没有开额外的空间,所以空间复杂度为O(1)

以下是希尔排序的完整代码:

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

void ShellSort(int *a, int n)
{
	assert(a);
	int gap = n;
	int i = 0;
	while(gap > 1)
	{
		if(gap > 1)
		gap /= 2;
		for( i = 0; i < n - gap; i++)//对n组数据进行n - gap次预排序
		{
			int end = i;
			int tmp = a[gap + end];
			while(end >= 0)//当end >= 0时候对每组进行预排序
			{
				if(tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
	return;
}

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

int main()
{
	int a[] = {5,6,1,2,7,4,8,3,9};
	int len = sizeof(a) / sizeof(int);
	ShellSort(a, len);
	Print(a, len);
	return 0;
}


如果这篇文章对你有帮助的话,还望各位佬能多多三连~~[doge][玫瑰] 

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

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

相关文章

ChineseChess.2023.11.09.01

中国象棋残局模拟器ChineseChess.2023.11.09.01

【PC】特殊空投-2023年11月

亲爱的玩家朋友们&#xff0c;大家好&#xff01; 寒冷的11月&#xff0c;特殊空投活动来袭。从连续签到活动到神秘市场相关活动&#xff0c;我们已经为大家准备好了一切&#xff0c;只为给大家在这寒冷的11月带来一份暖意。还有大量的黑货票劵等着大家&#xff0c;请一定要领取…

C语言-调试文件

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> //读256 a 256 fseek 改文件&#xff0c;用ocd&#xff0c;先搞b5v0 int main(int argc, char **argv) {if (argc ! 2) return -1;char file_buf[256];FILE* file1 fopen(argv[1], …

前后端交互常见的几种数据传输格式 form表单+get请求 form表单+post请求 json键值对格式

目录 1. get请求 query string 2.form表单get请求 3..form表单post请求 4..json格式 5.总结 1. get请求 query string 前端通过get请求携带 query string&#xff08;键值对&#xff09; ,后端通过req.getParameter(key)方法获取数据。如果key不存在&#xff0c;获取到的就…

pyspark将数据多次插入表的时候报错

代码 报错信息 py4j.protocol.Py4JJavaError: An error occurred while calling o129.sql. : org.apache.spark.sql.catalyst.parser.ParseException: mismatched input INSERT expecting <EOF>(line 12, pos 0) 原因 插入语句结束后没有加&#xff1b;结尾 把两个&am…

ROS学习笔记(6):ros_control

1.ros_control简介 ros_control - ROS Wiki ros_control是为ROS提供的机器人控制包&#xff0c;包含一系列控制器接口、传动装置接口、控制器工具箱等,有效帮助机器人应用功能包快速落地&#xff0c;提高开发效率。 2.ros_control框架 ros_control总体框架&#xff1a; 针对…

python连接mysql进行查询

pymysql连接工具类 import pymysql 数据库连接工具类 class MySQLConnection:def __init__(self, host, port, user, password, database):self.host hostself.port portself.user userself.password passwordself.database databaseself.conn Noneself.cursor None# …

广域网加速的作用:企业为什么需要广域网加速?

由于局域网与广域网之间巨大的带宽鸿沟&#xff0c;通过增加带宽来满足膨胀的流量需求是不切实际的。 并且广域网带宽成本较高&#xff0c;增加广域网带宽对任何企业都意味着巨大的成本负担。这些使得控制 管理广域网带宽使用成为必需。 企业为什么要加速广域网? 对重要的企…

微信小程序案例3-2 计算器

文章目录 一、运行效果二、知识储备&#xff08;一&#xff09;data-*自定义属性&#xff08;二&#xff09;模块 三、实现步骤&#xff08;一&#xff09;准备工作&#xff08;二&#xff09;实现页面结构&#xff08;三&#xff09;实现页面样式&#xff08;四&#xff09;实…

Debian12换镜像源

0 背景 用docker运行了一个node容器&#xff0c;发现连vim也没有&#xff0c;所以打算安一个vim 1 查看操作系统 find / -name *release* #查看release信息2 更换镜像源 2.1 从网上找个国内镜像源 确定好操作系统版本后&#xff0c;从网上搜一下对应的数据源。这里提供一个…

Ubuntu20.0工作区(workspace)介绍,切换工作区方式和快捷键

Ubuntu20.0工作区&#xff08;workspace&#xff09;介绍&#xff0c;切换工作区方式和快捷键 先修改一下ubuntu截屏的快捷键查看工作区新建工作区工作区切换 先修改一下ubuntu截屏的快捷键 修改为 查看工作区 按下Super键&#xff08;即Windows键&#xff09;&#xff0c;可…

动态壁纸软件Live Wallpaper HD mac中文版功能特色

Live Wallpaper HD mac提供了一系列美丽的主题场景&#xff0c;将为您的桌面增添活力。从城市景观、日落到遥远的星系&#xff0c;每个屏幕都有特别的触感&#xff0c;可以定制您的天气小部件和时钟样式&#xff0c;并使用您喜爱的图片创建您自己的个性化壁纸。 Living Wallpap…

安装dubbo-admin报错node版本和test错误

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;dubbo-admin安装 &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0…

5G毫米波通信中的关键技术

随着5G技术的快速发展&#xff0c;毫米波通信作为其中的一项重要技术&#xff0c;在高速数据传输、低延迟通信和大规模连接等方面具有显著的优势。本文将探讨5G毫米波通信中的关键技术&#xff0c;包括毫米波频段的选择、信号处理技术和MIMO技术等。 一、毫米波频段的选择 毫米…

Selenium alert 弹窗处理!

页面弹窗有 3 种类型&#xff1a; alert&#xff08;警告信息&#xff09;confirm&#xff08;确认信息&#xff09;prompt&#xff08;提示输入&#xff09; 对于页面出现的 alert 弹窗&#xff0c;Selenium 提供如下方法&#xff1a; 序号方法/属性描述1accept()接受2dismis…

查分小程序,教学大作用

数字化时代&#xff0c;技术已经深深的改变了我们的生活和工作方式。当然&#xff0c;教育领域也不例外。如果你是一位老师&#xff0c;你可能会想知道如何利用这些技术工具来提高学生的学习体验和成绩。今天&#xff0c;我们就来聊聊如何用各种代码、Excel等工具&#xff0c;打…

systemd-journald日志管理服务详解

journald参考文档&#xff1a; journald.confhttps://www.freedesktop.org/software/systemd/man/latest/journald.conf.html Amazonlinux2023系统默认不再安装rsyslog&#xff0c;因此在amazonlinux2中的诸多日志文件例如/var/log/message默认不可用。 AWS官方文档建议使用sys…

在Kotlin中设置User-Agent以模拟搜索引擎爬虫

前言 随着双十一电商活动的临近&#xff0c;电商平台成为了狂欢的中心。对于商家和消费者来说&#xff0c;了解市场趋势和竞争对手的信息至关重要。在这个数字时代&#xff0c;爬虫技术成为了获取电商数据的有力工具之一。本文将以亚马逊为例&#xff0c;介绍如何使用Kotlin编…

Git的安装和常用命令Git与SVN的区别Gitee远程仓库团队开发代码共享演示

目录 一、Git入门 1.1 Git简介 1.2 Git与SVN的区别 1.2.1 详解 1.2.2 图解 1.3 Git相较于SVN的优势与劣势 1.3.1 Git的优势与劣势 1.3.2 SVN的优势与劣势 1.4 Git的工作流程 1.4.1 图解 1.4.2 详解 二、Git的安装以及常用命令 2.1 Git官网链接 2.2 安装步骤 2.…

圣杯布局/双飞翼布局/flex/grid等,实现CSS三栏自适应布局的几种方法

简介 三栏布局是网页设计中常用的布局&#xff0c;即网页中的内容被分为三块&#xff1a;左侧/中间/右侧。其中两侧部分宽度固定&#xff0c;中间部分宽度自适应的根据容器&#xff08;浏览器&#xff09;宽度撑满剩余空间。而三栏布局也有很多变形&#xff0c;比如两栏或者N栏…