元旦特辑:Note5---插入排序

news2025/1/19 11:29:29


目录

前言🪩

1. 排序的概念+运用🟣

1.1 排序的概念🟪

1.2 排序的运用💜

2. 直接插入排序🟢

2.1 基本思想🟩

2.2 思路分析💚

2.3 代码实现✅

2.3.1 sort.h

2.3.2 sort.c

2.3.3 test.c

2.4 特性总结❇️

3. 希尔排序🟠

3.1 基本思想🟧

3.2 思路分析🔶

3.3 预排序实现🧡

3.3.1 sort.h

3.3.2 sort.c

3.3.3 优化

3.3.4 test.c

3.4 完整实现✴️

3.4.1 gap到底设置成多少?

3.4.2 sort.c

3.4.3 test.c

3.5 特性总结🚼

4. 性能对比--test.c🆚

后语🪅


前言🪩

Hey young fella!Welcome to my channel !我们前几篇博客学完了二叉树的一些基本内容,那么今天我们要学什么?没错👍从上次后语中,知道了我们今天要学习的是排序。本篇文章里面主要介绍插入排序的方法哦!话不多说,开始吧!


1. 排序的概念+运用🟣

1.1 排序的概念🟪

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小递增或递减的排列起来的操作。

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[n]=r[m],且r[n]在r[m]之前,而在排序后的序列中,r[n]仍在r[m]之前,则称这种排序算法是稳定的否则称为不稳定的

内部排序:数据元素全部放在内存中的排序。

外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

下图是我们要学习的常见排序算法:


1.2 排序的运用💜

例如:网购的商品排序呈现、成绩排名、大学综合实力排名......


2. 直接插入排序🟢

2.1 基本思想🟩

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

类似于斗地主中,拿到牌之后给自己的牌插入排序


2.2 思路分析💚


看完可视化的思路之后,相信很多小伙伴已经有了大致的思路。下面,我们来整理一下思路👇

目标:先实现升序
思路:先单趟再多趟,先局部再整体

我们默认一开始前面某一部分是有序的(实际上,我们是不知道哪到哪是有序的),然后再将无序的数据开始插入进去

插入的时候会遇到以下2种情况:
情况1:该值比前面所有的数都大,那么直接接着之前的数据往后排就行
情况2:   该值比前面任意一个数小(这个数据不是前面有序的数据中最大的),前面比该数大的往后挪,腾出一个位置给该数插入

问题:

1. 有序的部分怎么知道?

我们假设0-end是有序的
一开始排序的时候可能只有第一个有序,那就是0-0

注意⚠️

2. 我们还需要tmp来存储end的下一个位置的数据----为什么?

情况1:  直接在end++的位置插入数据就行

情况2:  有一部分数据需要往后移动,end++会被覆盖掉,不保存的话,就不知道原来end++的数据是多少了

3. 循环结束的条件是什么?


2.3 代码实现✅

2.3.1 sort.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

//打印
void PrintArray(int* a, int n);
//直接插入排序
void InsertSort(int* a, int n);

2.3.2 sort.c

#include"sort.h"
//打印
void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
		printf("%d ", a[i]);
	printf("\n");
}

//[0,end]  end+1
//直接插入排序
void InsertSort(int* a, int n)
{
	//i<n-1是为了防止end+1造成数组越界
	for (int i = 0; i < n - 1; i++)
	{
		int end=i;
		int tmp = a[end + 1];
		//单趟排序
		while (end >= 0)//最差end走到-1,插入到最前面
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
				break;
		}
		a[end + 1] = tmp;
	}
}

2.3.3 test.c


2.4 特性总结❇️

直接插入排序的特性总结:

1. 元素集合越接近有序,直接插入排序算法的时间效率越高

2. 时间复杂度:O(N^2)---最坏情况:逆序

3. 空间复杂度:O(1),它是一种稳定的排序算法

4. 稳定性:稳定

3. 希尔排序🟠

3.1 基本思想🟧

选定一个整数---gap(我们目前还不确定gap是多少),把待排序文件中所有数据分成n/gap个组,所有距离为gap的数据分在同一组内,并对每一组内的数据进行直接插入排序---进行预排序,使其接近有序

然后再次确定gap的值,重复上述分组和排序的工作。当最后gap=1时,(相当于直接插入排序)所有数据在统一组内排好序。


3.2 思路分析🔶


目标:实现升序

思路:先实现单趟排序,然后实现多趟排序

一、预排序:gap>1   接近有序

1. 先确定gap + 分组

2. 实现单趟(一组)排序

3.实现多趟排序 

二、直接插入排序:gap==1   有序

4. 所有数据进行直接插入排序

问题:

可能是后面会遇到的问题,可以和代码结合着看

1. 依据n和gap分组的时候,剩余的数据个数达不到分组的标准,出现数组越界怎么办?


3.3 预排序实现🧡

3.3.1 sort.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

//打印
void PrintArray(int* a, int n);
//希尔排序
void ShellSort(int* a, int n);

3.3.2 sort.c

#include"sort.h"
//打印
void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
		printf("%d ", a[i]);
	printf("\n");
}

//希尔排序---一组一组排序
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)//问题1
		{
			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;
		}
	}
}

3.3.3 优化

//希尔排序---多组并排,反复横跳
void ShellSort(int* a, int n)
{
	//单趟排序
	int gap = 3;
	for (int i = 0; i < n - gap; i ++)//问题1
	{
		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;
	}
}

3.3.4 test.c

#include"sort.h"
//希尔排序
void testShellSort()
{
	int a[] = { 3,2,6,8,9,7,5,10,1,4 };
	int n = sizeof(a) / sizeof(int);
	ShellSort(a, n);
	PrintArray(a, n);
}
int main()
{
	//testInsertSort();
    testShellSort();
	return 0;
}


3.4 完整实现✴️

3.4.1 gap到底设置成多少?

预排序:
1. gap过大,分组少,排序速度快,效果不好,比较不接近有序

2. gap过小,分组多,排序速度慢,效果好,比较接近有序

问题来了:gap设置为多少呢?可不能是定值,数据个数不同gap定义是有差异的

注意⚠️

gap定义的前提:我们需要保证多组预排序之后,gap==1,从而进行直接插入排序

其实,到现在并没有确切的说法说gap怎么定义是最优的,所以这里也只是一个参考:

有人觉得gap/2(可以保证最后一次的gap==1),但是还是排序太多次了,所以就提出了gap/3,但是gap/3不能保证最后一次是1(进行直接插入排序)--->gap/3+1就可以保证啦

3.4.2 sort.c

//希尔排序---多组并排,反复横跳
void ShellSort(int* a, int n)
{
	int gap = n;
	//预排序
	while (gap > 1)
	{
		//单趟排序
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)//问题1
		{
			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;
		}
	}
	//直接插入排序
	InsertSort(a, n);
}

3.4.3 test.c


3.5 特性总结🚼

​​​​​​​ 1. 希尔排序是 对直接插入排序的优化

2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定,导致希尔排序的时间复杂度不是很好算,需要借助数学模型,感兴趣的小伙伴可以自行搜索🔍

这里,就简单说一下平均时间复杂度:O(N^1.25)-O(1.6*N^1.25)

为了方便,我们直接粗略一点:O(N^1.3)​​​​​​​

4. 稳定性:不稳定

4. 性能对比--test.c🆚

// 测试排序的性能对比
// 测试排序的性能对比
void TestOP()
{
	srand(time(0));
	const int N = 100000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	int* a6 = (int*)malloc(sizeof(int) * N);
    int* a7 = (int*)malloc(sizeof(int) * N);
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand()+i;
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
        a7[i] = a1[i];
    }
    int begin1 = clock();
    InsertSort(a1, N);
    int end1 = clock();
    int begin2 = clock();
    ShellSort(a2, N);
    int end2 = clock();
    int begin3 = clock();
    //SelectSort(a3, N);
    int end3 = clock();
    int begin4 = clock();
    //HeapSort(a4, N);
    int end4 = clock();
    int begin5 = clock();
    //QuickSort(a5, 0, N - 1);
    int end5 = clock();
    int begin6 = clock();
    //MergeSort(a6, N);
    int end6 = clock();
    int begin7 = clock();
    //BubbleSort(a7, N);
    int end7 = clock();
    printf("InsertSort:%d\n", end1 - begin1);
    printf("ShellSort:%d\n", end2 - begin2);
    printf("SelectSort:%d\n", end3 - begin3);
    printf("HeapSort:%d\n", end4 - begin4);
    printf("QuickSort1:%d\n", end5 - begin5);
    printf("MergeSort:%d\n", end6 - begin6);
    printf("BulbbleSort:%d\n", end7 - begin7);
    free(a1);
    free(a2);
    free(a3);
    free(a4);
    free(a5);
    free(a6);
    free(a7);
}
int main()
{
	testInsertSort();
    testShellSort();
    TestOP();
	return 0;
}


我们发现,希尔排序排序还是很厉害👍的

后语🪅

今天,我们学习了插入排序的实现和性能对比以及他们的特性总结。希望小伙伴们自己也可以练习一下,加强记忆和理解。

下一篇博客,我们将一起学习选择排序的相关知识点!请大家多多期待🙏


本次的分享到这里就结束了!!!

PS:小江目前只是个新手小白。欢迎大家在评论区讨论哦!有问题也可以讨论的!期待大家的互动!!!

公主/王子殿下,请给我点赞👍+收藏⭐️+关注➕(这对我真的很重要!!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux网络编程学习心得.4

1.epoll工作模式 水平触发 LT 边沿触发 ET 因为设置为水平触发,只要缓存区有数据epoll_wait就会被触发,epoll_wait是一个系统调用,尽量少调用 所以尽量使用边沿触发,边沿出触发数据来一次只触发一次,这个时候要求一次性将数据读完,所以while循环读,读到最后read默认带阻塞…

设计模式——行为型模式

模板方法模式 行为型模式用于描述程序在运行时复杂的流程控制&#xff0c;即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务&#xff0c;它涉及算法与对象间职责的分配。 行为型模式分为类行为模式和对象行为模式&#xff0c;前者采用继承机制来在类间…

专访大华智慧运营:利用物联网技术,加快融合智能

近日&#xff0c;指令集智能科技到访浙江大华智慧物联运营服务有限公司&#xff0c;对产品研发部总经理孙旭先生进行了深度采访。孙旭先生在采访中表示“现在行业对于运营服务的诉求越来越高&#xff0c;而针对物理空间的运营&#xff0c;最典型的特征就是要靠物联网产品支撑……

嵌入式科普(8)ESP-IDF newlib相关介绍和对比分析

一、目的/概述 二、资料来源 三、ESP-IDF简介 3.1 ESP-IDF FreeRTOS 3.2 ESP-IDF heap_caps 3.3 ESP-IDF newlib 四、对比 嵌入式科普(8)ESP-IDF newlib相关介绍和对比分析 一、目的/概述 1、在我的嵌入式科普(6)你听说过FreeRTOS heap6吗&#xff1f;…

命令模式-举例

开关和电灯之间并不存在直接耦合关系&#xff0c;在命令模式中&#xff0c;发送者与接收者之间引入了新的命令对象&#xff0c;将发送者的请求封装在命令对象中&#xff0c;再通过命令对象来调用接收者的方法。 命令模式的主要缺点如下&#xff1a; 使用命令模式可能会导致某…

Halcon纹理分析texture_laws/trans_from_rgb

Halcon纹理分析 文章目录 Halcon纹理分析1. 纹理滤波器2. 织物折痕检测 纹理是图像表面的一种灰度变化。有的纹理很规则&#xff0c;会以局部小区域为单元重复出现&#xff0c;而有的纹理则呈现出随机性。对于规则的纹理&#xff0c;可以很容易地从中分辨出重复的区域&#xff…

【BERT】深入理解BERT模型1——模型整体架构介绍

前言 BERT出自论文&#xff1a;《BERT&#xff1a;Pre-training of Deep Bidirectional Transformers for Language Understanding》 2019年 近年来&#xff0c;在自然语言处理领域&#xff0c;BERT模型受到了极为广泛的关注&#xff0c;很多模型中都用到了BERT-base或者是BE…

一个静态网站可以增加什么第三方功能/服务

一个静态网站&#xff0c;无后台功能&#xff0c;怎么增加一些实用功能呢&#xff1f;我们来看看一些免费的第三方服务。 静态页面寄存 Gitee pages/Github pages 都可以&#xff0c;绑定一个域名&#xff0c;版本一提交&#xff0c;直接发布有效果。 评论 每个 URL 页面都…

从零开始学Python系列课程第17课:容器型数据类型之列表(上)

前言 列表算是 Python 中比较常用的一种容器型数据类型&#xff0c;那么什么是列表&#xff0c;列表有什么样的作用致使它在 Python 中这么受欢迎呢&#xff1f;这便是接下来我们要一起讨论的问题。 在不久之前我们讲过变量&#xff0c;我们将数据使用变量保存&#xff0c;但是…

【动态规划精选题目】3、简单多状态模型

此动态规划系列主要讲解大约10个系列【后续持续更新】 本篇讲解简单多状态模型中的9道经典题&#xff0c;会在讲解题目同时给出AC代码 目录 1、按摩师 2、力扣198:打家劫舍1 3、打家劫舍II 4、删除并获得点数 5、 粉刷房子 6、力扣309:买卖股票的最佳时机含冷冻期 7、 买…

MySQL入门教程-触发器

9.触发器 什么是触发器 触发器(trigger)&#xff1a;监视某种情况&#xff0c;并进行某种操作&#xff0c;它的执行并不是程序调用&#xff0c;也不是手工启动&#xff0c;而是由事件来触发&#xff0c;例如&#xff1a;对一张表进行操作&#xff08;插入&#xff0c;更新&…

Winform RDLC报表(数据库连接、报表函数使用、动态表头)

文章目录 NuGet安装库数据库连接报表设计报表引用添加报表 数据集设计方法一手动添加方法二——连接数据库添加 关联报表与数据集表格数据与数据集数据设计表格格式、字体设计报表数据字段绑定 Winform 使用报表控件数据库填充数据集从数据库获取与数据源相同字段的数据 动态表…

城市生态数据大屏,PSD设计稿

现分享生态系统可视化大数据大屏的 Photoshop 源文件&#xff0c;下载即用&#xff01;以下为截图示意。 若需 更多行业 相关的大屏&#xff0c;请移步小7的另一篇文章&#xff1a;200套精选数据可视化大屏&#xff0c;大屏PSD设计&#xff08;各行业大屏UI&#xff09;https:…

Pandas教程(一)—— 数据结构

前言 Pandas是贯穿数据分析的主要工具之一&#xff0c;它经常和其他数值计算工具一起使用&#xff08;例如&#xff1a;Numpy、SciPy和matplotlib&#xff09;。尽管pandas采用了很多NumPy的代码风格&#xff0c;但二者最大的区别是&#xff1a;pandas主要用于处理表格型或异质…

机器学习系列11:减少过拟合——L1、L2正则化

如果我们注意到模型在训练集上的表现明显优于模型在测试集上的表现&#xff0c;那么这就是模型过拟合了&#xff0c;也称为 high variance。 产生的过拟合的原因是对于给定的训练集数据来说&#xff0c;模型太复杂了。有几种可以减少过拟合的方法&#xff1a; 收集更多的训练数…

洛谷 P1086:花生采摘 ← 结构体

【题目来源】https://www.luogu.com.cn/problem/P1086https://www.acwing.com/problem/content/description/420/【题目描述】 在告示牌背后&#xff0c;路边真的有一块花生田&#xff0c;花生植株整齐地排列成矩形网格&#xff08;如图 1&#xff09;。 有经验的多多一眼就能看…

Net6 Core webApi发布到IIS

Net6 Core Api发布到IIS不同于webapi&#xff0c;依赖框架不同&#xff0c;配置也移至项目内Program.cs 一、发布到指定文件夹和IIS&#xff0c;不过注意IIS应用程序池选择的是 “无托管代码“ 在IIS管理器中点击浏览&#xff0c;访问接口路径报500.19&#xff0c;原因是所依赖…

Prometheus告警处理

Alertmanager介绍 Prometheus 包含一个报警模块&#xff0c;就是 AlertManager&#xff0c;Alertmanager 主要用于接收 Prometheus 发送的告警信息&#xff0c;它支持丰富的告警通知渠道&#xff0c;而且很容易做到告警信息进行去重、降噪、分组等&#xff0c;是一款前卫的告警…

链表精选题集

目录 1 链表翻转 题目链接&#xff1a; 解题&#xff1a; 试错版&#xff1a; 2 找中间节点 题目链接: 题解&#xff1a; 3 找倒数第k个节点 题目链接&#xff1a; 题解&#xff1a; 4 将两个升序链表合并为一个升序链表 题目链接&#xff1a; 题解&#xff1a; …

[电磁学]猴博士不挂科

1 利用表格求场强 2 利用叠加求场强 3 利用积分求场强 电场立库仑力 球的面积公式是4πr&#xff0c;其中r为球的半径。 球的体积公式是(4/3)πr&#xff0c;其中r为球的半径。 带电物体有体积: