DS:八大排序之直接插入排序、希尔排序和选择排序

                                         创作不易,感谢三连支持!! 

一、排序的概念及运用

1.1 排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起               来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记                   录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列                   r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据                      的排序。

关于这些基础概念我会在后面慢慢介绍! 

1.2 排序的运用

我们在淘宝购买商品的时候,可以选择让商品根据销量、信用、价格、综合程度进行排序

 还有高校排名,以及考试的排名,都是通过排序来完成的!!

排序存在的意义:帮助我们筛选出最优的选择

1.3 常见的排序算法

二、直接插入排序

2.1 思路

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

 这就和我们小时候玩扑克牌摸牌整理的一样,一次与前面的排比较找到合适的位置插入!

2.2 直接插入排序的实现

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

           我们先按照上面的思路,先模拟摸一张牌的过程,假设目前手上的牌是2 4 9 然后摸到了1张3,我们设置最后一张牌9的下标位置为end(2),然后让新摸的牌为temp(a[3]),开始慢慢往前比较,发现较大的就交换位置。

    int end=2;int temp=a[3];while (end >= 0){if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置{        a[end + 1] = a[end];--end;}elsebreak;}a[end+1] = temp;//不写在循环里面,是避免end减成-1,此时说明新加入的牌是最小的,正好放在一开始的位置

 上述过程可以实现插入一张牌,那么整体的实现就在外面加个for循序即可!!

void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int temp = a[i+1];while (end >= 0){if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置a[end + 1] = a[end];elsebreak;--end;}a[end + 1] = temp;//不写在循环里面,是避免end减成-1,此时说明新加入的牌是最小的,正好放在一开始的位置}
}

但要注意的是:外面的for循环的判断条件,i < n - 1, 也就是说i最多走到n - 2的位置即倒数第二个元素,原因是:tmp是每次要插入的元素,而tmp = a[end +1]是end的下一个位置,如果让end到最后一个元素的位置即n-1处,那tmp = a[end+1]就会越界!所以i只能到倒数第二个元素的位置!

2.3 复杂度分析

时间复杂度:O(N^2)  ---> 单趟是O(N),最坏情况N个元素都要走一次单趟(基本上逆序)

空间复杂度:O(1)  ---> 额外使用空间的个数是常数个

当要排序的序列接近有序时性能最好O(N)(接近有序)

三、希尔排序

3.1 思路

         希尔排序其实是直接插入排序的一种变形,我们知道对于直接插入排序来说,最坏的情况就是逆序,此时的时间复杂度就是O(N^2),最好的情况是接近有序,此时时间复杂度为O(N),这个时候希尔有了一个想法:有没有一种方法可以让一组无序的数据经过处理后使他接近有序,然后再最后实现一次直接插入排序呢?

      最后希尔发明出来了希尔排序

3.2 希尔排序的实现

具体思路:

1、对无序的数组进行预排序,使其接近有序。

2、最后再来一次直接插入排序

 这里的预排指的是:间隔gap的元素为一组,总计gap组,我们先假设gap为3,然后我们画个图来理解一下:

 根据我们之前写的直接插入排序算法,我们可以先实现将红色的一组进行排序的算法

int gap = 3;
for (int i = 0; i < n - gap; i+=gap)
{int end = i;int temp = a[i + gap];while (end >= 0){if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置a[end + gap] = a[end];elsebreak;end -= gap;}a[end + gap] = temp;
}

 我们发现,如果我们一开始让i=1,就可以实现蓝色组的排序,让i=2的话,就可以实现绿色组的排序,所以为了让三组都完成排序,我们再外面再嵌套一层循环!

int gap = 3;
for (int j = 0; j < gap; j++)
{for (int i = j; i < n - gap; i += gap){int end = i;int temp = a[i + gap];while (end >= 0){if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置a[end + gap] = a[end];elsebreak;end -= gap;}a[end + gap] = temp;}
}

这样我们就实现了三组的预排序了!! 

但其实上面的代码还可以优化成两层循环!!

int gap = 3;for (int i = 0; i < n - gap; i ++){int end = i;int temp = a[i + gap];while (end >= 0){if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置a[end + gap] = a[end];elsebreak;end -= gap;}a[end + gap] = temp;}

刚刚那种写法是一组一组去完成预排,而现在这种写法是实现多组并排,效果是一样的!! 

这样的预排序有什么意义呢?

1、 gap越大,大的数可以更快到后面,小的数可以更快到前面,但是越不接近有序

2、gap越小,大的小的就挪动的越慢,但是也越接近有序

3、gap==1时,就是直接插入排序(我们可以发现当gap等于1时,这个预排序算法与直接插入排序算法的写法是一样的!!)

现在来分析gap该取多少合适?

     首先,gap是不能随便取的,因为比如说有100万个数据,gap取3,显然是不合适的,所以我们的gap一定要跟数据个数n建立联系,gap具体取多少是最合适的没有得到很好的证明,所以我们使用Knuth的思路来将我们的希尔排序完善好!!

void ShellSort(int* a, int n)
{//gap>1  预排序//gap=1 直接插入排序int gap = n;while (gap > 1){gap = gap / 3 + 1;//这是为了保证gap最后一定为0for (int i = 0; i < n - gap; i++){int end = i;int temp = a[i + gap];while (end >= 0){if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置a[end + gap] = a[end];elsebreak;end -= gap;}a[end + gap] = temp;}}
}

需要注意的是:gap = gap / 3 + 1是为了保证gap最后一定会等于1,也就是一定会在最后进行一次直接插入排序,保证有序,而前面gap>1的过程都是在进行预排序!!

3.3 复杂度分析

因为预排是一个逐渐转好的过程,所以我们还按照最坏情况去考虑是不合理的,因此这边是难以计算的,我们看看书上的讲解

 《数据结构(C语言版)》--- 严蔚敏

《数据结构-用面相对象方法与C++描述》--- 殷人昆
 

因为咋们的gap是按照Knuth提出的方式取值的,而且Knuth进行了大量的试验统计,我们暂时就按o(N^1.25)到o(1.6N^1.25)到来算

四、选择排序

4.1 思路

选择排序的思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

      这个其实也跟摸扑克牌有关,但是这次跟直接插入排序不一样的是,直接插入排序是一次摸一张牌然后插入调整,而选择排序是一次性拿了所有牌,再逐个把小的数往前放!

4.2 选择排序的实现

1、在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素

2、若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
3、在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

我们拿到所有的牌后,每次都把最小的牌往前放

void SelectSort(int* a, int n)
{for (int begin = 0; begin < n; begin++){int min = begin;//记录最小元素的下标for (int i = begin+1; i < n; i++){if (a[min] > a[i])min = i;//记录最小的牌的下标}Swap(&a[begin], &a[min]);}
}

 但是每次遍历就记一张最小的牌,效率太低下了,所以我们改造一下该算法,使得该算法每遍历一次就记住最小的牌和最大的牌,然后分别放在两边!!

void SelectSort(int* a, int n)
{int left = 0; int right = n - 1;while (left < right){int min = left;int max = left;for (int i = left+1; i <= right; i++){if (a[min] > a[i])min = i;if (a[max] < a[i])max = i;}//这里要考虑一种情况,就是如果最大的数恰好就在最左端,那么就会导致第二次swap换到后面的就不是最大的数而是最小的数了Swap(&a[min], &a[left]);//如果max和begin重叠,修正一下if (max == left)max = min;Swap(&a[max], &a[right]);left++;right--;}
}

易错点1:min和max要从他们后面的第一张牌开始去一张一张比较 

易错点2:交换的时候,如果最大的元素恰好在最左边,那么就有可能被最小的元素给交换过去了,所以这个时候要注意及时地修正!!

4.3 复杂度分析

时间复杂度:O(N^2)

单趟无论选择一个还是选择两个,都得遍历一遍,复杂度为O(N),整体还得遍历一遍O(N)

空间复杂度:O(1)

 

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

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

相关文章

2402,如何正确的最佳方式对付新冠

这几天,连着发了几天的烧,配合感冒冲剂,荆防颗粒,同新冠进行了5次2小时的斗争. 最后,我发现,同新冠直接斗争,是很傻的行为. 为啥,我要直接同新冠斗争呢? 为啥不让新冠同环境中的消毒剂斗争呢?消毒剂是化学药品,病毒最怕的就是这些玩意! 只要我的局部环境充满了消毒剂,新冠要同…

leetcode:96.不同的二叉搜索树

解题思路&#xff1a; 输入n3 n 0 1个 n 1 1个 n 2 2个 头1头2头3 头1 左子树0节点&#xff08;个数&#xff09;x右子树2个节点&#xff08;个数&#xff09; 头2 左子树1节点&#xff08;个数&#xff09;x右子树1个节点&#xff08;个数&#xff09; 头3 左子…

操作字符串之子串削除-11-${string%%substring}

1.${string%%substring} 从$string的结尾位置截掉最短匹配的$substring 2.实例 操作字符串样例&#xff1a;string123ABCabc456xyzabc 字符串操作默认从右边开始进行 命令&#xff1a; echo ${string%%a*c} [rootkibana ~]# echo ${string%%a*c} 123ABC #从$string的结尾…

集群聊天项目

不懂的一些东西 (const TcpConnectionPtr&&#xff09;作为形参啥意思&#xff1a;接收一个常量引用&#xff0c;函数内部不允许修改该指针所指向的对象。 优势 1.网络层与业务层分离&#xff1a;通过网络层传来的id&#xff0c;设计一个map存储id以及对印的业务处理器&…

mysql 2-16

安全等于<> 最大最小LEAST,GREATEST BETWEEN AND 条件一是下限 IN LIKE关键字 转移字符 逻辑运算符 位运算符 排序数据 升序降序&#xff0c;默认升序 二级排序 8.0新特性 小拓展 多表查询 多表查询 别名 多表查询的分类 非等值连接 自连接 内连接与外连接 sql92实现外连…

【前端工程化面试题】如何优化提高 webpack 的构建速度

使用最新版本的 Webpack 和相关插件: 每个新版本的 Webpack 都会带来性能方面的改进和优化&#xff0c;因此始终确保你在使用最新版本。同时&#xff0c;更新你的相关插件也是同样重要的。 合理配置 Webpack: 优化 Webpack 的配置文件&#xff0c;包括合理设置 entry、output、…

TenorFlow多层感知机识别手写体

文章目录 数据准备建立模型建立输入层 x建立隐藏层h1建立隐藏层h2建立输出层 定义训练方式建立训练数据label真实值 placeholder定义loss function选择optimizer 定义评估模型的准确率计算每一项数据是否正确预测将计算预测正确结果&#xff0c;加总平均 开始训练画出误差执行结…

倒计时52天(待续,,,

寒假学习内容总复习上&#xff1a; 倒计时67天-CSDN博客 1. #include<bits/stdc.h> using namespace std; #define int long long const int N2e56; const int inf0x3f3f3f3f; int month[13]{0,31,28,31,30,31,30,31,31,30,31,30,31}; int a[110]; void solve() {for(…

【超级干货】ArcGIS_空间连接_工具详解

帮助里对空间连接的解释&#xff1a; 根据空间关系将一个要素的属性连接到另一个要素。 目标要素和来自连接要素的被连接属性写入到输出要素类。 如上图所示&#xff0c;关键在于空间关系&#xff0c;只有当两个要素存在空间关系的时候&#xff0c;空间连接才有用武之地。 一…

JavaScript_00001_00000

contents 简介变量与数据类型自动类型转换强制类型转换 简介 变量与数据类型 根据变量定义的范围不同&#xff0c;变量有全局变量和局部变量之分。直接定义的变量是全局变量&#xff0c;全局变量可以被所有的脚本访问&#xff1b;在函数里定义的变量称为局部变量&#xff0c;…

【leetcode热题】对称二叉树

难度&#xff1a; 简单通过率&#xff1a; 42.2%题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目描述 给定一个二叉树&#xff0c;检查它是否是镜像对称的。 例如&#xff0c;二叉树 [1,2,2,3,4,4,3] 是对称的。 1/ \2 …

JavaWeb之Servlet接口

Servlet接口 什么是Servlet&#xff1f; Servlet是一种基于Java技术的Web组件&#xff0c;用于生成动态内容&#xff0c;由容器管理&#xff0c;是平台无关的Java类组成&#xff0c;并且由Java Web服务器加载执行&#xff0c;是Web容器的最基本组成单元 什么是Servlet容器&…

杂文随笔_

己写于亥年正月廿六&#xff0c;校图书馆&#xff0c;天气阴 “你的工作将会是你生活中很大一部分&#xff0c;唯一能使自己得到真正满足的是&#xff0c;做你伟大的工作,做一份伟大的工作的唯一方法是&#xff0c;热爱你所做的工作。“这是乔布斯在斯坦福大学的一次演讲所说的…

【c++】list详细讲解

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟悉list库 > 毒鸡汤&#xff1a;你的脸上云淡…

外部中断0实验

实现现象&#xff1a;下载程序后&#xff0c;操作K3按键使LED1&#xff08;D11&#xff09;状态取反 注意事项&#xff1a;无。 #include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器typedef unsigned int u16; //对数据类型进行声明定义 typed…

定时器1中断实验

实现现象&#xff1a;下载程序后&#xff0c;静态数码管间隔一秒循环显示0-F。使用单片机内部定时器可以实现准确延时。 注意事项&#xff1a; 程序代码&#xff1a; #include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器typedef unsigned int u16; …

Java环境变量

&#xff08;1&#xff09;classpath:让jvm找到将要执行的java文件的目录 它通常包括程序当前目录和Java标准库的路径. 编译后&#xff0c;classpath的当前目录就是target下的classes目录&#xff0c;它是resource和main目录的合并&#xff0c;如果两个类目录相同&#xff0c;那…

关于Build Your Own Botnet的尝试

这是一次失败的尝试、 原文地址&#xff1a;关于Build Your Own Botnet的尝试 - Pleasure的博客 下面是正文内容&#xff1a; 前言 我在上一篇关于DDOS的文章种提到过这个项目&#xff0c;而且说明了由于这个项目是在2020年发布并开源的&#xff0c;并且已经有两年没有进行跟…

身份治理存在权限问题

身份治理正迅速成为 CISO 的首要考虑因素。二十年前&#xff0c;当萨班斯-奥克斯利法案(SoX) 和其他监管指令在互联网泡沫破灭后诞生时&#xff0c;身份治理要求就出现了。合规性控制&#xff0c;例如用户访问审查和有效管理员工访问生命周期的需要&#xff0c;是当时身份治理的…

1.2.1 相机模型—内参、外参

相机模型-内参、外参 更多内容&#xff0c;请关注&#xff1a; github&#xff1a;https://github.com/gotonote/Autopilot-Notes.git&#xff09; 针孔相机模型&#xff0c;包含四个坐标系&#xff1a;物理成像坐标系、像素坐标系、相机坐标系、世界坐标系。 相机参数包含&…