【算法速查】一篇文章带你快速入门八大排序(上)

在这里插入图片描述

君兮_的个人主页

即使走的再远,也勿忘启程时的初心

C/C++ 游戏开发

Hello,米娜桑们,这里是君兮_,首先在这里祝大家中秋国庆双节同乐!!今天用一篇文章为大家把八大排序算法都过一遍,当然由于篇幅的原因不是每一种算法都详解,这篇文章更多是作为让初学者有一个初步的了解以及学过的人某个排序算法忘了的话的快速回忆,后续我也会把每种算法的重点以及难点挑出来单独为大家讲解的

  • 好了废话不多说,开始我们今天的学习吧!!

    八大排序算法

    • 什么是排序?
      • 常见的排序算法
    • 插入排序
      • 直接插入排序
      • 希尔排序
    • 选择排序
      • 直接选择排序
      • 堆排序
        • 向下调整
        • 建大堆
        • 堆排
    • 总结

什么是排序?

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

常见的排序算法

  • 排序在日常生活中的应用的重要性无需多说,小到每一次成绩的排名大到国家总GDP值排名处处是排序,目前,常见的排序有八种,我们接下来都会逐一介绍,现在通过一个图来简单认识一下这几种排序
    在这里插入图片描述

插入排序

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

直接插入排序

  • 直接插入排序基本动图如下:
    在这里插入图片描述
  • 以此图的升序为例,插入排序的思想是:
  • *1.假设前n-1项是有序的,比较第n项与n-1项,当第n项元素比第n-1项大时,无需改变位置,当第n项元素比第n-1项小时,交换两个元素的位置,继续与前面的有序数据进行比较,直到遇到比第n项元素小的元素或者排到已经需要排序的数据的头,停止排序,此时视为完成一趟插入排序
  • 2.继续排序,此时前n项数据是有序的,第n+1项数据重复上述的步骤,多趟插入,直到到所需排序数据的结尾
  • 代码实现
// 插入排序
void InsertSort(int* a, int n)  //a为所需排序的数组,n为该数组数据的个数
{for (int i = 0; i < n - 1; i++){//从0开始,多趟插入int end = i;//要插入的元素int tmp = a[end + 1];//单趟插入排序while (end >= 0){//要插入的元素比此时有序数组的最后一个元素小if (tmp < a[end]){//交换 a[end + 1] = a[end];//与前一个元素再比较end--;}//插入的元素比最后一个元素大 该趟插入排序结束elsebreak;}//最后一个元素就是我们要插入的元素a[end + 1] = tmp;}}
  • 此时可能会有一些初学者和我才开始学插入排序时一样有些疑惑,当tmp<a[end]时,a[end+1]=a[end],a[end]=a[end],数据中不就有个元素被覆盖了吗?不应该把tmp的值赋给此时的a[end]吗?

  • 这一步其实可有可无,注意看,如果下一次tmp还小于a[end],此时a[end+1]也就是上一轮的a[end]就会被赋值,当tmp大于a[end]时,循环结束a[end+1]也会被赋值,因此无论是哪种情况这段代码都是进行了相应的处理的,因此是否把tmp赋值给a[end]也就无关紧要了。

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

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

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

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

  • 4. 稳定性:稳定

希尔排序

  • 希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数gap(gap>=1),把待排序文件中所有记录分成几个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后重复上述分组和排序的工作。当到达gap=1时,所有记录再在统一组内排好序
  • 简单来说,就是对所需排序的数据先进行几次预排序,使该组数据基本满足小的元素在前,大的元素在后(也就是越接近有序),最后再对整体进行一次直接插入排序
    在这里插入图片描述
  • 希尔排序代码如下
// 希尔排序
void ShellSort(int* a, int n) // a为待排序数组,n为数据个数
{//让gap等于n,第一次排序时gap为n/2,之后gap/=2,直到gap==1,进行最后一次希尔排序,完成对所需排序数据的排序int gap = n;while (gap>1){gap /= 2;for (int i = 0; i < n - gap; i++){//从第0个元素开始int end = i;//每个元素间隔为gapint tmp = a[end + gap];//每一趟预排序while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];//所需比较元素的间隔为gapend-=gap;}elsebreak;}a[end + gap] = tmp;}}
  • 很多人会有这样的一个疑问,对于希尔排序的多趟排序,为什么结束的位置是n-gap,难道后面的数据就不排了吗?尤其是当开始时,gap是非常大的,后面还有很多元素仍然处于无序的状态。
  • 这样做的目的是防止越界,同时,如果你仔细分析一下这段代码,其实看似后面还有很多元素未排序,其实都已经排好了,我们以当gap最大为n/2时为例,此时最后一个元素为a[n-gap-1] 而tmp为a[n-1]可见其实所有的元素都已经比较了。
  • 希尔排序的特性总结:
  • 1. 希尔排序是对直接插入排序的优化。
  • 2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就
    会很快。这样整体而言,可以达到优化的效果。
  • 3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,我们现在通俗的认知为 O(N^1.3)
  • 4. 稳定性:不稳定
  • 有些初学者觉得既然直接插入排序是稳定的,那么只是比直接插入排序多了几次预排序的希尔排序也是稳定的吧,下面我来为大家举个反例
    在这里插入图片描述
  • 排在前面的9现在却排到了后面,现在你还觉得希尔排序是稳定的吗?由于在分组预排序时可能会让两个相同的元素分到不同的组中,造成上图这种情况,因此,希尔排序是不稳定的!

选择排序

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

直接选择排序

  • 基本思想:
    在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素
    若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
    在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素
  • 简单来说,就是通过遍历的方式找出该组数据中最大的元素和最小的元素,并把它们交换到该组数据的结尾和开头,再进一步找到该组数据次大的元素和次小的元素,交换到倒数第二个位置和第二个位置,直到该组数据只剩下一个元素或者没有元素。
    在这里插入图片描述
  • 上图以每次找最小为例,实际是找最大和最小同时进行的
// 选择排序
void SelectSort(int* a, int n)
{int begin = 0;int end = n - 1;while (begin < end)//等于与大于时只剩下1个或0个元素,停止选择排序{//暂时让最大和最小的都指向beginint minSize = begin;int maxSize = begin;//最大最小都指向begin,从begin+1开始for (int i = begin+1; i <= end; i++){//找该组数据中最大的元素,通过maxSize保存其下标if (a[i] > a[maxSize]){maxSize = i;}//找该组数据中最小的元素,通过minSize保存其下标if (a[i] < a[minSize]){minSize = i;}			}Swap(&a[begin], &a[minSize]);//交换最小和此时的begin//修正if (maxSize == begin)maxSize = minSize;Swap(&a[end], &a[maxSize]);//交换最大和此时的endbegin++;//找次大和次小的end--;}
}
  • 其中有个小小的细节需要注意,我们来看看出现以下情况时可能会发生的错误
    在这里插入图片描述
  • 发生以上的情况,我们的排序就全乱了,因此需要上面代码中的修正
//修正if (maxSize == begin)maxSize = minSize;
  • 当我们发现最大的值就是下标为begin的值后,此时begin已经与minSize交换,我们让maxSize=minSize,找到我们被交换走的最大值。
  • 直接选择排序的特性总结:
  • 1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  • 2. 时间复杂度:O(N^2)
  • 3. 空间复杂度:O(1)
  • 4. 稳定性:不稳定

堆排序

  • 堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是
    通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆

  • 以排升序为例

  • 1、将待排序的数据构造成一个大堆,当前堆的根节点(堆顶)就是该组数组中最大的元素;

  • 2、将堆顶元素和最后一个元素交换,将剩下的节点重新构造成一个大堆;

  • 3、重复步骤2,每次循环构建都能找到当前堆中的最大值,并通过交换的方式把它放到该大堆的尾部,直至所有元素全部有序

  • 具体实现

向下调整

  • 无论是建大堆还是进行堆排序都需要一个能够实现把大的数据向下调整的算法,那么是如何实现的呢?
  • 基本思想

1、从根节点开始,选出左右孩子中值较大的一个
2、如果选出的孩子的值大于父亲的值,那么就交换两者的值,不大于,说明此时孩子和父亲都处于合适的位置,不再向下调整
3、将大的孩子看做新的父亲,继续向下调整,直到调整到叶子节点为止

  • 注意:
  • 上面的向下调整的思想的前提是——根结点的左右子树必须都为大堆。
    在这里插入图片描述
  • 向下调整图示
    在这里插入图片描述
    在这里插入图片描述
  • 此时我们是采用的从根节点开始向下调整节点,通过图我们也能发现从根节点开始的向下建堆并不能保证把最大的元素放到堆顶,这是下面建堆部分的知识,我们下面再讲,这里我们的一次向下调整就完成了,代码实现如下

// 堆排序
void AdjustDwon(int* a, int n, int parent)
{int child = parent * 2 + 1;//到叶子节点就停止while (child < n){//选出孩子中最大的if (child + 1 < n && a[child + 1] > a[child]){child++;}//孩子比父亲大就交换,并使此时的孩子成为下一次循环的父亲if (a[parent] < a[child]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}//父亲更大,由于左右子树一定是大堆,不需要进一步朝下进行,直接退出循环else{break;}}}

建大堆

  • 在开始我们的堆排序之前,我们得先通过向下排序的方式把我们需要排序的数据建立一个大堆
  • 上面我们通过对向下调整的图示也看出了,从根节点开始进行向下调整建大堆存在一定的局限性,因此我们建大堆选择从倒数第一个非叶子节点开始,从后往前,将其作为父亲,依次向下调整,一直调整到根的位置
//这里n是数组元素个数,i作为下标-1,同时在由孩子找父亲时,parent=(child-1)/2 因此这里的i为(n-2)/2
for (int i = (n - 1 - 1) / 2; i >=0; i--)
{AdjustDwon(a, n, i);
}

在这里插入图片描述
在这里插入图片描述

  • 我们通过从下到上向下调整可以避免出现上面那种孩子的孩子比父亲大建不了大堆的问题,但是当我们建好堆后,只能说它变得相对有序了,但是仍然会出现这样一个问题,如果判断左孩子和右孩子的位置?我们只保证了根节点的左右子树均为大堆,却没有保证不同子树间左右的大小问题,因此,我们为了最终排序的目的,还需要进行一步堆排序。

堆排

  • 堆排序的思想:

1、建好堆之后,将堆顶的数字与最后一个数字交换(建的是大堆,堆顶一定是最大的数)
2、将最后一个数字排除,剩下的n-1个元素再向下调整成堆再循环进行第一步
3、直到最后只剩一个数时就停止排序。

  • 为什么这样能成功堆排序呢?

首先,我们能确保的是,堆顶的数一定是最大的,因此把它交换的数排在此时的最后一位是合理的。
其次,我们交换上去的数字会向下调整到正确的位置,这样既保证了根节点的左右子树是大堆又能使每个交换上去的节点处于合适的位置。
重点是从后往前排的,我们一定能确保最后的数是交换下去的最大的,次大的依次类推就能保证我们排好的数据有序。

void HeapSort(int* a, int n)
{//建大堆for (int i = (n - 1 - 1) / 2; i >=0; i--){AdjustDwon(a, n, i);}//堆排,交换最后一个元素与堆顶元素,再把此时的堆顶元素向下调整int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDwon(a, end, 0);end--;}
}
  • 堆排序特性总结
  • 1. 堆排序使用堆来选数,效率就高了很多。
  • 2. 时间复杂度:O(N*logN)
  • 3. 空间复杂度:O(1)
  • 4. 稳定性:不稳定

总结

  • 由于篇幅的原因,我们今天先介绍前四种排序算法,剩下的放到下篇继续,算法这一块光靠看代码不是那么容易理解的,因此我花了大量的时间画图分析,希望能对你有所帮助
  • 当然,这篇文章创作的初衷是希望帮助初学者对排序算法有一个大致的了解,对已经学过的人起到在需要使用的时候快速回忆的效果,因此可能还有一部分细节不全,之后我会挑出重点单独出博客讲解
  • 有任何的问题和对文章内容的疑惑欢迎在评论区中提出,当然也可以私信我,我会在第一时间回复的!!

新人博主创作不易,如果感觉文章内容对你有所帮助的话不妨三连一下再走呗。你们的支持就是我更新的动力!!!

**(可莉请求你们三连支持一下博主!!!点击下方评论点赞收藏帮帮可莉吧)**

在这里插入图片描述

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

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

相关文章

AI智能问答系统源码/AI绘画商业系统/支持GPT联网提问/支持Midjourney绘画

一、AI创作系统 SparkAi创作系统是基于国外很火的ChatGPT进行开发的AI智能问答系统和AI绘画系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图…

opencv for unity package在unity中打开相机不需要dll

下载OpenCV for Unity 导入后&#xff0c;里面有很多案例 直接打开就可以运行 打开相机

CSP-J第二轮试题-2021年-1.2题

文章目录 参考&#xff1a;总结 [CSP-J 2021] 分糖果题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 #2样例输入 #2样例输出 #2 样例 #3样例输入 #3样例输出 #3 提示答案1答案2-优化 [CSP-J 2021] 插入排序题目描述输入格式输出格式样例 #1样例输入 #1样…

为什么字节大量用GO而不是Java?

见字如面&#xff0c;我是军哥。 我看很多程序员对字节编程语言选型很好奇&#xff0c;为此我还特地问了在字节的两位4-1的技术大佬朋友&#xff0c;然后加上自己的思考&#xff0c;总结了一下就以下 2 个原因&#xff1a; 1、 选型上没有历史包袱 字节的早期的程序员大多来自于…

Linux常见操作命令(1)

​ 前言&#xff1a;作者也是初学Linux&#xff0c;可能总结的还不是很到位 ♈️今日夜电波&#xff1a;达尔文—林俊杰 0:30━━━━━━️&#x1f49f;──────── 4:06 &#x1f504; ◀️ …

Interactive-slam imGui slam3dTool防坑手册

问题一、 glfw error 65544: X11: RandR gamma ramp support seems broken error : failed to compile shader /home/ros_proj/catkin_ws/src/interactive_slam/data/shader/rainbow.vert 0:1(10): error: GLSL 3.30 is not supported. Supported versions are: 1.10, 1.20, 1…

快看看你的手机有没有:谷歌Android全面封杀此类软件!

谷歌坐不住了&#xff0c;因为Android应用商店中&#xff0c;充斥着大量可窃取用户数据的应用&#xff0c;所以必然要出手整治了。 一款名叫“SonicSpy”软件是整个事情的导火索&#xff0c;而该应用是典型的窃取用户数据的应用&#xff0c;其除了可以从手机中提取个人数据外&…

cesium gltf控制

gltf格式详解 glTF格式本质上是一个JSON文件。这一文件描述了整个3D场景的内容。它包含了对场景结构进行描述的场景图。场景中的3D对象通过场景结点引用网格进行定义。材质定义了3D对象的外观,动画定义了3D对象的变换操作(比如选择、平移操作)。蒙皮定义了3D对象如何进行骨骼…

使用华为eNSP组网试验⑵-通过端口地址进行静态路由

有了网络模拟器可以对很多网络应用场景进行模拟&#xff0c;既方便学习又有利于实际的网络实施。 之前因为没有用过&#xff0c;用过了才知道eNSP的好处。但是与思科模拟器不同&#xff0c;连接是自动连接&#xff0c;不能确定端口&#xff0c;比如使用指定的光纤端口或者RJ45的…

【Redis】redis基本数据类型详解(String、List、Hash、Set、ZSet)

目录 RedisString(字符串)List(列表)Hash(字典)Set(集合)ZSet(有序集合) Redis Redis有5种基本的数据结构&#xff0c;分别为&#xff1a;string&#xff08;字符串&#xff09;、list&#xff08;列表&#xff09;、set&#xff08;集合&#xff09;、hash&#xff08;哈希&a…

Fiddler抓取手机https包的步骤

做接口测试时&#xff0c;有时我们需要使用fiddler进行抓包分析&#xff0c;那么如何抓取https包。主要分为以下七步&#xff1a; 1.设置fiddler选项&#xff1a;Tools->Options,按如下图勾选 2.下载并安装Fiddler证书生成器 下载地址&#xff1a;http://www.telerik.com/…

python使用mitmproxy和mitmdump抓包在电脑上抓包(二)

在我的上篇文章中&#xff0c;主要记录如何安装mitmproxy和抓取https流量。参考链接&#xff1a; python使用mitmproxy和mitmdump抓包在电脑上抓包-CSDN博客 本篇主要使用python配合mitmdump来抓包和处理返回包&#xff0c;更加灵活&#xff0c;这也是mitmproxy(mitmdump)的最…

熔断、限流、降级 —— SpringCloud Alibaba Sentinel

Sentinel 简介 Sentinel 是阿里中间件团队开源的&#xff0c;面向分布式服务架构的高可用流量防护组件&#xff0c;主要以流量为切入点&#xff0c;从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性 Sentinel 提供了两个服务组件…

opencv实现目标跟踪及视频转存

创建跟踪器 def createTypeTracker(trackerType): 读取视频第一帧&#xff0c;选择跟踪的目标 读第一帧。 ok, frame video.read() 选择边界框 bbox cv2.selectROI(frame, False) 初始化跟踪器 tracker_type ‘MIL’ tracker createTypeTracker(tracker_type) 用第一…

手机电脑数码小程序商城的作用是什么

手机几乎是每个成年人人手一个以上&#xff0c;市场非常大&#xff0c;加之产品更新迭代速度快&#xff0c;每年都会推出多个型号、造型等&#xff0c;因此对高收入群体或爱机人群来说&#xff0c;新手机往往一年或二年时间就会换&#xff0c;或者直接购买当备用机等。 每个城…

Acwing 143. 最大异或对

Acwing 143. 最大异或对 题目描述思路讲解代码展示 题目描述 思路讲解 这道题的启示是&#xff1a;字典树不单单可以高效存储和查找字符串集合,还可以存储二进制数字 思路:将每个数以二进制方式存入字典树,找的时候从最高位去找有无该位的异. 代码展示 #include<iostream…

华为云云耀云服务器L实例评测 | 实例场景体验之搭建个人博客:通过华为云云耀云服务器构建个人博客

华为云云耀云服务器L实例评测 &#xff5c; 实例场景体验之搭建个人博客&#xff1a;通过华为云云耀云服务器构建个人博客 介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云云耀…

【调度算法】进程调度算法、内存页面置换算法、LRU算法、LFU算法、磁盘调度算法等重点知识汇总

目录 进程调度算法 内存页面置换算法 LRU算法实现 LFU算法实现 磁盘调度算法 进程调度算法 当 CPU 空闲时&#xff0c;操作系统就选择内存中的某个「就绪状态」的进程&#xff0c;并给其分配 CPU。 什么时候会发生 CPU 调度呢&#xff1f;通常有以下情况&#xff1a; 当…

CSS详细基础(五)选择器的优先级

本节介绍选择器优先级&#xff0c;优先级决定了元素最终展示的样式~ 浏览器是通过判断CSS优先级&#xff0c;来决定到底哪些属性值是与元素最为相关的&#xff0c;从而作用到该元素上。CSS选择器的合理组成规则决定了优先级&#xff0c;我们也常常用选择器优先级来合理控制元素…

Leetcode---364场周赛

题目列表 2864. 最大二进制奇数 2865. 美丽塔 I 2866. 美丽塔 II 2867. 统计树中的合法路径数目 一、最大二进制奇数 这题只要你对二进制有了解(学编程的不会不了解二进制吧)&#xff0c;应该问题不大&#xff0c;这题要求最大奇数&#xff0c;1.奇数&#xff1a;只要保证…