【数据结构】排序之插入排序

排序目录

  • 1.前言
  • 2. 排序的概念及其运用
    • 2.1 排序的概念
    • 2.2 排序的运用
    • 2.3 常见的排序算法
  • 3. 插入排序
    • 3.1 基本思想
    • 3.2 直接插入排序
      • 3.2.1 直接插入排序实现
        • 3.2.1.1 分析
        • 3.2.1.2 代码实现
    • 3.3 希尔排序
      • 3.3.1 希尔排序实现
        • 3.3.1.1 分析
        • 3.3.1.2 代码实现
  • 4. 附代码
    • 4.1 sort.h
    • 4.2 sort.c
    • 4.3 test.c

1.前言

在生活中处处可见排序,当我们打开京东或者其它购物平台时,搜索物品,它会有一定的排序。
这次就来分享的博客与排序有关。
正文开始。

2. 排序的概念及其运用

2.1 排序的概念

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

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

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

2.2 排序的运用

举个例子:
在京东上平板的统合排名:
在这里插入图片描述
来看看高校的排名:
在这里插入图片描述
可能每个人搜出来的不一样。

2.3 常见的排序算法

在这里插入图片描述

3. 插入排序

3.1 基本思想

直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
就像是在玩扑克牌时候,对刚拿的牌来进行一个插入。
在这里插入图片描述

3.2 直接插入排序

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。

直接看动图:
在这里插入图片描述
直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定

3.2.1 直接插入排序实现

以升序为例子

3.2.1.1 分析

就是把没排好的数插入到已经排好的数中,实现有序。
实现排序,先实现单趟。
假设在一个已经排好序的区间[0,end],然后把end+1位置的值插入进去,那怎么插入呢?
从后往前,依次比较,如果比end+1大,那就往后挪,把位置空出来,再把值放进去。为了记录插入的数据,用一个临时变量tmp存储 end+1的值,避免被覆盖。

在这里插入图片描述
假设前面的已经[0,end],也就是3,4,9已经排好。这时要插入6,先记录一下tmp=6,然后依次往前比较,如果比tmp大,那就往后挪。
在这里插入图片描述
还有一种情况:
一直往走,往后挪数据,当end<0时结束。
在这里插入图片描述
所以这里循环的条件就是while (end >= 0)
如果tmp < a[end],就实现a[end + 1] = a[end],然后end1--
循环结束就有两种情况:一种是tmp>=end,tmp就得放在end后面。另一种是:在while条件结束,出现end<0。
在这里插入图片描述
单趟的已经实现,那怎么实现整体?
while循环外面再套一层循环。
第一个数据就是[0,0],再往下是[0,1],2位置的往前插入。那么它的结束位置就是n-1,不能是n,因为如果到n,那么tmp位置访问n+1,已经越界了。

3.2.1.2 代码实现
void InsertSort(int* a, int n)
{// [0, end] end+1for (int i = 0; i < n - 1; ++i){int end =i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];--end;}else{break;}}a[end + 1] = tmp;}
}

来看看结果:
在这里插入图片描述

3.3 希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。
在这里插入图片描述
希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排序的时间复杂度都不固定:
  4. 稳定性:不稳定

3.3.1 希尔排序实现

希尔排序分为两部分:第一部分是预排序,第二部分是直接插入排序。

3.3.1.1 分析

预排序的目的是让数组基本有序,这样直接插入就很快。
举个例子:gap=3
将间隔为3的分为一组,那么总的就分为了三组。红的一组,蓝的一组,绿的一组。
在这里插入图片描述
那么它的预排序怎么实现呢?
就是将分的这三组,分别进行插入排序。
首先将9当成已经排好的数据,那么下一个不是8,而是间隔为3的6,把6往前插入,然后继续找下一个就是4,继续往前面插入。
最后就是这样;
在这里插入图片描述
剩下的两组也是一样的,最终排出来就是
在这里插入图片描述
这里只是接近有序。

就是把上面的直接插入排序的tmp换成end + gap位置的就行。
在这里插入图片描述
假设先对红色这组经行排序,那就是:
在这里插入图片描述
注意循环的条件如果是i<n,那么就会访问越界,注意看图上,发现结束的位置就是n-gap。
如果排实现的两组,那么就直接再套一层,循环gap=3次就排完了。
在这里插入图片描述
这里套了三层排序,也只是预排序,j为0就是红色的一组,j为1就是蓝色那组,j为2就是绿色那一组。
在这里插入图片描述
优化一下,实现多组并排,之间是一组一组往后排,现在是直接在gap组之间来回跳,第一次排红色,第二次排蓝色,第三次排绿色。
少一层循环。
在这里插入图片描述
gap怎么选择:
在这里插入图片描述

《数据结构(C语言版)》— 严蔚敏
在这里插入图片描述

《数据结构-用面相对象方法与C++描述》— 殷人昆
在这里插入图片描述

在这里插入图片描述
所以这里实现希尔排序,就是将gap不断变小,
gap > 1时是预排序,目的让他接近有序,而gap == 1是直接插入排序,目的是让他有序。
那么这里gap怎么选择呢?
如果gap = gap / 2,这里跳跃就比较小,所以选择gap = gap / 3 ,但为了保证最后一次为1,这里就得加1,也就是gap = gap / 3 + 1。
在这里插入图片描述
来看看结果:
在这里插入图片描述

3.3.1.2 代码实现
void ShellSort(int* a, int n)
{int gap = n;// gap > 1时是预排序,目的让他接近有序// gap == 1是直接插入排序,目的是让他有序while (gap > 1){//gap = gap / 2;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;}}
}

4. 附代码

4.1 sort.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
#include<time.h>void PrintArray(int* a, int n);
void InsertSort(int* a, int n);
void ShellSort(int* a, int n);

4.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");
}// 时间复杂度:O(N^2) 逆序
// 最好的情况:O(N)  顺序有序
void InsertSort(int* a, int n)
{// [0, end] end+1for (int i = 0; i < n - 1; ++i){int end =i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];--end;}else{break;}}a[end + 1] = tmp;}
}// 平均O(N^1.3)
void ShellSort(int* a, int n)
{int gap = n;// gap > 1时是预排序,目的让他接近有序// gap == 1是直接插入排序,目的是让他有序while (gap > 1){//gap = gap / 2;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;}}/*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;}}*/
}

4.3 test.c

#include"Sort.h"void TestInsertSort()
{int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };InsertSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestShellSort()
{int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };ShellSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}
int main()
{TestInsertSort();	TestShellSort();return 0;
}

在之后的博客中会继续分享与排序有关的内容,请多多关注。
有问题请指出,大家一起进步!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS4.0系列——04、@Styles、@Extend、@Extend事件以及多态样式stateStyles

Styles、Extend、Extend事件以及多态样式stateStyles Styles 通用样式 类似于css中的class 语法一&#xff1a;内部样式 放在struct内 Styles commonStyle(){.backgroundColor(Color.Pink).padding(20px)}语法二&#xff1a;外部样式 Styles function commonStyle() {.backg…

几代WiFi有什么差异,它们有什么区别

最典型的差异指标&#xff1a;单流传输速率 第一代 基于的标准&#xff1a; 802.11 使用频率&#xff1a;2.4GHz 单流最大传输速率&#xff1a;2Mbit/s 第二代 基于的标准&#xff1a; 802.11b 使用频率&#xff1a;2.4GHz 单流最大传输速率&#xff1a;11Mbit/s 第三代 …

什么牌子冻干猫粮性价比高?性价比高的五款冻干猫粮牌子推荐

很多养猫的小伙伴们都磨刀霍霍准备给猫咪屯些猫冻干吧&#xff0c;特别是家里有挑食猫咪的家庭。有养猫的铲屎官们应该都知道&#xff0c;猫咪是对蛋白质的需求量很高&#xff0c;而且对植物蛋白的吸收效率比较低&#xff0c;所以蛋白质最好都是来自动物的优质蛋白。猫咪挑食就…

jmeter函数助手-常用汇总

一.函数助手介绍 1.介绍及作用 介绍&#xff1a; jmeter自带的一个特性&#xff0c;可以通过指定的函数规则创建后进行调用该函数&#xff0c;在后续接口请求参数中进行调用 作用 &#xff08;1&#xff09;做参数化。 2.如何使用 jmeter工具栏-->工具-->函数助手…

华为---USG6000V防火墙web基本配置示例

目录 1. 实验要求 2. 配置思路 3. 网络拓扑图 4. USG6000V防火墙端口和各终端相关配置 5. 在USG6000V防火墙web管理界面创建区域和添加相应端口 6. 给USG6000V防火墙端口配置IP地址 7. 配置通行策略 8. 测试验证 8.1 逐个删除策略&#xff0c;再看各区域终端通信情况 …

为何软件开发时需要性能测试工具

在当今数字化时代&#xff0c;软件已经成为我们生活和业务的核心。随着用户对高性能、高响应性和卓越用户体验的期望不断增长&#xff0c;软件开发过程中的性能测试变得至关重要。性能测试工具在确保软件正常运行、高效响应以及适应负载压力方面发挥着关键作用。本文将介绍为什…

台式电源质量如何检测?纳米软件为您科普

一、外观检测 观察台式机电脑电源外观是否有损伤、烧焦&#xff0c;电源线是否有破损、短线的情况。观察电源的电压、电流、功率等参数&#xff0c;是否符合台式机电脑。 二、直观检测 开通电源&#xff0c;如果所有指示灯不亮&#xff0c;风扇没有声音&#xff0c;电源损坏的可…

react学习第一天

脚手架的创建 1.创建环境变量 npm init -y 2.创建node-modules npm add -D create-react-app 3.创建脚手架 npx create-react-app react-demo1 报错一号 报错原因&#xff1a;node版本太低 解决&#xff1a;升级版本 nvm install 14.0.0 nvm use 14.0.0 报错二号 报错原因&…

搭建Vue前端项目的流程

一、搭建Vue项目流程 1、安装nodejs 测试安装是否成功 $ npm -v 6.14.16 $ node -v v12.22.122、全局安装npm install -g vue/cli&#xff0c;后续会使用到vue命令 $ vue --version vue/cli 5.0.83、使用vue create demo_project_fe命令创建项目&#xff0c;使用箭头键来选择…

lv13 内核模板编译方法 7

1 内核模块基础代码解析 Linux内核的插件机制——内核模块 类似于浏览器、eclipse这些软件的插件开发&#xff0c;Linux提供了一种可以向正在运行的内核中插入新的代码段、在代码段不需要继续运行时也可以从内核中移除的机制&#xff0c;这个可以被插入、移除的代码段被称为内…

Android画布Canvas drawPath绘制跟随手指移动的圆,Kotlin

Android画布Canvas drawPath绘制跟随手指移动的圆&#xff0c;Kotlin import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path import android.os.Bundle import android.…

深入Mybatis数据源

数据源是持久层框架中最核心的组件之一&#xff0c;在实际工作中比较常见的数据源有 C3P0、Apache Common DBCP、Proxool 等。作为一款成熟的持久化框架&#xff0c;MyBatis 不仅自己提供了一套数据源实现&#xff0c;而且还能够方便地集成第三方数据源。 javax.sql.DataSourc…