各种“排序”的方法

文章目录

  • 插入排序
    • 1. 直接插入排序(O(n^2))
      • 举例1:
      • 举例2:
      • 直插排序的"代码"
      • 直插排序的“时间复杂度”
    • 2. 希尔排序(O(n^1.3))
        • 方法一
        • 方法二(时间复杂度更优)
  • 选择排序
    • 堆排序
    • 直接选择排序

我们学过冒泡排序,堆排序等等。(回顾一下:排升序,建大堆;排降序,建小堆。)

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

在生活中也会遇到很多排序:购物筛选排序(按照价格、综合、销量、距离等排序)、百度热搜(根据热搜程度)、院校排名等等。

在这里插入图片描述

插入排序

1. 直接插入排序(O(n^2))

基本思想:将(待排序的记录)按照(关键码值的大小)逐个插入到(已经排好序的有序队列)中。直到所有的记录插入完为止,得到一个新的有序序列。

先将arr[end]指向:(有序队列的最后一个数据),然后将(需要和有序数据进行比较的数据:即arr[end+1])暂时存储在tmp中,接下来将arr[end]和tmp里的数据进行比较。
在这里插入图片描述
注意在一次循环中,tmp的值是不变的(除非end++,才会使tmp的值改变(tmp=arr[end]))

举例1:

【假设想要排升序】
在这里插入图片描述

  • 现在arr[end]的值大于tmp,应该排在后面,所以现在将end的值放在end+1的位置,这个时候会发现end+1位置的值被覆盖了,所以我们需要提前将它的值存储在tmp中(这就是将值存储在tmp中的原因)。接下来需要将tmp的值放在end的位置(这个是arr[end]的前面没有其他值的情况,我们可以直接将tmp的值赋给arr[end])

在交换值之后,将end++,tmp的值为arr[end+1] (它不用修改,因为end已经修改了)
在这里插入图片描述

  • 如果arr[end]<tmp,并不需要交换呢?(比如5和9)那就直接end++,紧接着tmp的值也变啦。

  • 紧接着又是将9和6交换,按照之前的方法(将arr[end]的值赋给arr[end+1],然后将tmp的值赋给arr[end]
    在这里插入图片描述

  • 为什么我们还需要将交换之后的arr[end]的值和这个序列前面的值比较呢?------防止它比前面已经排好序的值小

接下来按照前面的方法,再将9和2进行比较

举例2:

在这里插入图片描述

直插排序的"代码"

我们需要一个循环来控制end++,往后走;
还需要一个循环来控制:当把arr[end]赋值给arr[end+1]之后,需要将end- -,将前面的数值和tmp继续比较。

在这里插入代码片`void InsertSort(int* arr, int n)
{for (int i = 0; i < n - 1; i++)  //为什么是i<n-1?{int end = i;int tmp = arr[end + 1];while (end >= 0){if (arr[end] > tmp){arr[end + 1] = arr[end];end--;}else{break;}}arr[end + 1] = tmp;}
}`

在这里插入图片描述

  • 为什么是i<n-1呢?
    因为tmp存储的是arr[end+1],我们要确保它是最后一个不会越界,即end+1<n,又因为end=i,所以i+1<n,所以i<n-1

直插排序的“时间复杂度”

直接插入排序的时间复杂度是O(n^2)

不过O(n^2)是最差的情况。
在这里插入图片描述

最差:想将降序----->升序 O(n^2)
最好:升序------->升序 O(n)

2. 希尔排序(O(n^1.3))

希尔排序就是对直接插入排序的一种优化。想要改善降序---->升序的时间复杂度。

希尔排序分为两步:1.预排序 2.直接插入排序

我们可以将很长的一段数组序列变为n段较短的序列,然后对每段“单独”进行直接插入排序(这个叫做预排序)。预排结束之后,小的数据大部分都在前半部分了(只是不按顺序),接着我们再对整体的数组(已经趋于有序的数组)序列进行一次直插排序,这样不仅完成了排序,还优化了时间复杂度。

方法一
  • 首先我们需要决定一个值:gap,然后将大段分为n个小段(每段的首和尾相隔gap个元素,即第一个值往后数gap个,那个数据是这段的最后一个元素)。

什么情况下预排:当gap>1的时候
什么情况下整段直插:当gap==1的时候

在这里插入图片描述

  1. 从上图可以知晓:要将一大段数据分为很多组数据,在寻找第二组数据时,需要将上次的首元素下标++。这一步由外层循环控制【遍历每个子序列的起始位置(从 0 到 gap-1)】for(i=0;i<gap;i++)(为什么i<gap?)

  2. 接下来是每组数据中,知道了首元素,然后找这组数据中的剩余数据,即end+gap,这个由第二层循环控制:for(int j=0;j<n-gap;j+=gap);(为什么内层的j<n-gap?)

  3. 再里面的元素之间比较就没有什么特别了,使用的是直接插入排序(它也有循环)。

【gap不能太大(若太大的话,会导致每组的数据比较少,但组数比较多),也不能太小(那样的话每组数据会有很多,那这样的话时间复杂度又变高了)】我们可以让gap由元素个数决定,gap=n/3+1,为什么最后还要+1呢?(如果遇到2/3的情况,那商就是0,也就是gap=0,那间距为0,谁和谁比呢?)而我们最后需要让gap=1,让已经预排之后的整段数据进行直插排序,所以需要+1.

【gap等于多少,这段数据最终就会被分为几组数据】如果n特别特别大,n/3之后也特大,先将+1给忽略掉

  • 之前我们说到为什么外层循环中i<gap而不是i<n呢?
    在这里插入图片描述
    我们第一组数据的第二个元素是arr[end+gap]也就是arr[0+gap],既然arr[gap]在第一组数据中已经进行比较过一次,那之后就没必要再将它比较一次了。大家观察上图会发现每个数据都进行比较过,如果外层用 i < n,会导致重复处理同一子序列,效率降低。

  • 还有一个问题是:为什么第二层循环需要j<n-gap,按照方法一个组中,每个数据它们都相邻gap个(n=8,gap=3第一组元素下标:0,3,6,下标为6的元素后面还有1个元素,但是剩余元素个数不够gap个,那就只能在这次的循环中将后面的元素舍弃。所以在找每组的数据时(即在第二层循环),循环结束的条件是最后一个元素后面的元素个数小于gap个(有剩余的元素或者剩余个数为0(也就是刚好后面没有元素了)),也就是它的下标小于n-gap

在这里插入图片描述

第一组的数据:7,4,1
第二组的数据:6,3
第三组的数据:5,2

  • 然后,先排第一组的数据,再排2,3组的数据

(1)初始情况:arr[end]指向第一个元素,tmp指向(与第一个相隔gap的元素).即tmp=arr[end+gap]

  • arr[end]>tmp,那么将arr[end]的值赋给tmp所指向的那个值,即arr[end+gap]=arr[end],赋值之后,将end=end-gap
  • arr[end]>tmp,不用修改值。直接将end+=gap
    对于每组数据的值比较的注释
    (2)排完第二组的数据之后,外层的i++,到第二个数据【下标为1】(它作为第二组的首元素),然后进入第二层循环,在进行比较。

到此为止,这个进行了一趟比较

void ShellSort(int* arr, int n)
{int gap = n / 3 + 1;for (int i = 0; i < gap; i++){int j = i;for (j =i; j < n - gap; j += gap){int end = j;int tmp = arr[end + gap];while (end>=0)  //将这组数据的每个数据比较,while结束的条件:end>=0;{if (arr[end] > tmp){arr[end + gap] = arr[end];//想继续和前面有序的数据比较,将end-gapend -= gap;}//到这里,arr[end]<tmp,那就不用赋值,直接将下标+gap,tmp的值随之改变,继续比较else {break;}}//在循环中,我们只是将大的数据往后赋值,那当初那个比较小的tmp赋给谁呢?//这组数据如何能比较结束(break)呢?肯定是此时的arr[end]并没有tmp大,不需要交换了//所以,我们把tmp值赋值给之前一个比较的值(由于是end-=gap,所以上一个是end+gap)arr[end + gap] = tmp;}}
}

在这里插入图片描述

在这里插入图片描述

之后我们将gap不断缩小,继续比较,直至最后gap==1时,进行最后的,对整段进行直插
在这里插入图片描述

void ShellSort(int* arr, int n)
{int gap = n;while (gap>1){gap = gap / 3 + 1;for (int i = 0; i < gap; i++){int j = i;for (j = i; j < n - gap; j += gap){int end = j;int tmp = arr[end + gap];while (end >= 0)  //将这组数据的每个数据比较,while结束的条件:end>=0;{printf("%d ", gap);if (arr[end] > tmp){arr[end + gap] = arr[end];//想继续和前面有序的数据比较,将end-gapend -= gap;}//到这里,arr[end]<tmp,那就不用赋值,直接将下标+gap,tmp的值随之改变,继续比较else {break;}}//在循环中,我们只是将大的数据往后赋值,那当初那个比较小的tmp赋给谁呢?//这组数据如何能比较结束(break)呢?肯定是此时的arr[end]并没有tmp大,不需要交换了//所以,我们把tmp值赋值给之前一个比较的值(由于是end-=gap,所以上一个是end+gap)arr[end + gap] = tmp;}}}
}

注意:为什么gap>1还可以对整段进行直插排序呢?
大家先想一想gap==1的条件:肯定是上一个gap>1因而进入了循环,进行了gap=gap/3+1,而gap/3=0,gap/3+1=1,接下来进行了直插。不会有其他意外。

n=10:
第一次,gap=n=10,大于1,进入循环之后,gap=gap/3+1=4,之后进行排序。
第二次,gap=4,大于1,进入循环,gap=gap/3+1=4/3+1=2,之后进行排序
注意gap=2的时候它仍然大于1 ,进入循环了,然后才进行的gap=gap/3+1,得到了gap=1,进行最后的直插

方法二(时间复杂度更优)

在上面的基础上进行优化:
之前是将一组一组比,现在仍然是相隔gap个的两个元素进行比较,但是当它俩比较完之后,我们不用找arr[0+gap]再隔gap的值。我们直接找arr[0]的下一个:arr[1],再找arr[1+gap],将他俩比较。那到什么时候停止呢?当i<n-gap时。
在这里插入图片描述

void ShellSort(int* arr, int n)
{int gap = n;while (gap>1){gap = gap / 3 + 1;for (int i=0; i < n - gap; i++){int end = i;int tmp = arr[end + gap];while (end >= 0)  //将这组数据的每个数据比较,while结束的条件:end>=0;{if (arr[end] > tmp){arr[end + gap] = arr[end];//想继续和前面有序的数据比较,将end-gapend -= gap;}//到这里,arr[end]<tmp,那就不用赋值,直接将下标+gap,tmp的值随之改变,继续比较else {break;}}//在循环中,我们只是将大的数据往后赋值,那当初那个比较小的tmp赋给谁呢?//这组数据如何能比较结束(break)呢?肯定是此时的arr[end]并没有tmp大,不需要交换了//所以,我们把tmp值赋值给之前一个比较的值(由于是end-=gap,所以上一个是end+gap)arr[end + gap] = tmp;}}
}

在这里插入图片描述
希尔排序的时间复杂度是:O(n^1.3)
在这里插入图片描述

选择排序

堆排序

这是堆排序的文章,点击链接即可跳转

直接选择排序

这个名字中的“选择”已经暴露了它的方法,直接选择排序是从整个数组中(arr[0]到arr[n-1])选出来min或者max的元素,然后放在整个数组的第一个位置。然后再从arr[1]到arr[n-1]选出来min或max,放在第二个位置。【将min/max放在第一个位置,那原本的数据怎么办,所以不是直接将这个值放在第一个位置,而是将两个值交换】

  • 思路:

    1. 先设定两个变量,begin和mini(它俩是下标),begin作为min或者max放置的位置的下标。mini作为遍历数组时此时位置的下标。[需要知道跳出循环的条件,即放的位置<n]
      在这里插入图片描述
  • 但是又一个bug是:每找一次最大/最小值,就需要将数组遍历一遍(和冒泡排序一样费时间O(n^2)),时间复杂度很大,我们进行优化。

在遍历一遍数组的时候,我们可以将min和max同时找出来,将它们放在数组的两端。这样就可以省略一半的时间。【但需要注意的是,这个上面那个的循环结束条件就不一样了,我们不用再遍历后半部分,就是已经放好特定值的那部分】

void SelectSort(int* arr, int n)
{int begin=0;for (begin = 0; begin < n/2; begin++) //为什么begin<n/2呢?因为begin是前半部分的下标,只用占整个数组的一半{int maxi = begin;int mini = begin;//确定了这趟mini要放的位置,接下来这个循环用来遍历找最小值/最大值,用j来遍历int j = begin;for (j = begin+1; j < n-begin; j++)  //为什么j<n-begin//第一遍遍历的时候,需要遍历所有的//第二次遍历,就不用遍历最后一个,已经放了特定的值//第三次遍历,倒数两个都不用遍历了{if (arr[j] < arr[mini])mini=j;if (arr[j] > arr[maxi])maxi=j;}//这一步是什么作用?if (maxi == begin){maxi = mini;}Swap(&arr[begin], &arr[mini]);Swap(&arr[n - 1 - begin], &arr[maxi]);}
}

其中这一步的作用是什么呢?

if (maxi == begin)
{maxi = mini;
}

举个例子:
在这里插入图片描述

这个的问题就在于:maxi刚好和begin重合了,而arr[begin]先一步和arr[mini]交换了,所以我们需要在交换之前处理一下:如果它俩重合了,那就先让maxi走到mini的位置。
在这里插入图片描述

if(maxi==begin)maxi=mini;

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

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

相关文章

【Linux网络与网络编程】08.传输层协议 UDP

传输层协议负责将数据从发送端传输到接收端。 一、再谈端口号 端口号标识了一个主机上进行通信的不同的应用程序。在 TCP/IP 协议中&#xff0c;用 "源IP"&#xff0c;"源端口号"&#xff0c;"目的 IP"&#xff0c;"目的端口号"&…

python求π近似值

【问题描述】用公式π/4≈1-1/31/5-1/7..1/(2*N-1).求圆周率PI的近似值。 从键盘输入一个整数N值&#xff0c;利用上述公式计算出π的近似值&#xff0c;然后输出π值&#xff0c;保留小数后8位。 【样例输入】1000 【样例输出】3.14059265 def countpi(N):p0040nowid0for i i…

第十六届蓝桥杯省赛JavaB组题解

A 逃离高塔 第一道填空题很简单&#xff0c;根据题意跑一边循环即可&#xff0c;一共是202个符合条件的数 public static void main(String[] args) {Scanner scanner new Scanner(System.in);int ans0;for(long i0;i<2025;i){if((i*i*i)%103)ans;}System.out.println(ans)…

汽车车窗升降系统全生命周期耐久性验证方案研究

随着汽车行业的快速发展&#xff0c;消费者对于汽车品质和安全性的要求日益提高。汽车车窗升降系统作为汽车电子系统中的重要组成部分&#xff0c;其可靠性和耐久性直接影响到用户的使用体验和行车安全。车窗升降系统在日常使用中频繁操作&#xff0c;承受着各种复杂的工况&…

嵌入式Linux——8 串口

目录 1.终端&#xff08;tty&#xff09; /dev/tty*&#xff1a;物理/虚拟终端 /dev/pts/*&#xff1a;伪终端 /dev/tty&#xff1a;当前进程的控制终端 /dev/tty0&#xff1a;当前活动的虚拟控制台 2.行规程模式&#xff08;line discipline&#xff09; 比较行规程和原…

Docker日志查看与资源监控指令全解:从基础到高阶运维实践

Docker日志查看与资源监控指令全解&#xff1a;从基础到高阶运维实践 一、日志管理&#xff1a;穿透容器内部的眼睛1.1 基础日志操作核心命令&#xff1a;docker logs日志驱动配置 1.2 高级日志处理JSON日志解析多容器日志聚合 二、资源监控&#xff1a;掌握容器生命体征2.1 实…

初学STM32之编码器测速以及测频法的实现

资料来着江协科技 这篇是编码器测速&#xff0c;江科大的源码在测速的时候&#xff0c;定时器TIM2是一直在跑的&#xff0c;不受其它控的&#xff0c;它就一直隔1S读一次CNT的值。它也不管是否有输入信号。源码程序修改一下是可以实现对PWM信号以测频法的方式读取。 笔者稍微改…

oracle怎么查看是否走了索引

SELECT * FROM CRM_STATION_APPEAL_RESULT WHERE COMPLAINT_ID ce1a1d8f-e2a2-4126-8cb7-14384cb24468; 这是查询语句&#xff0c;怎么看这个查询是否走了索引呢 EXPLAIN PLAN FOR SELECT * FROM CRM_STATION_APPEAL_RESULT WHERE COMPLAINT_ID ce1a1d8f-e2a2-4126-8cb7-14…

C++进阶——C++11_{ }初始化_lambda_包装器

目录 1、{ }初始化 1.1 C98的{ } 1.2 C11的{ } 1.3 C11中的std::initializer_list 总结一下&#xff1a; 2、lambda 2.1 lambda的语法 2.2 捕捉列表 2.3 lambda的应用 2.4 lambda的原理 3、包装器 3.1 function 3.2 bind 1、{ }初始化 1.1 C98的{ } C98中一般数组…

【微知】Mellanox网卡网线插入后驱动的几个日志?(Cable plugged;IPv6 ... link becomes ready)

概要 本文是一个简单的信息记录。记录的是当服务器网卡的光模块插入后内核的日志打印。通过这种日志打印&#xff0c;可以在定位分析问题的时候&#xff0c;知道进行过一次模块插拔。 日志 截图版&#xff1a; 文字版&#xff1a; [32704.121294] mlx5_core 0000:01:00.0…

单片机Day05---静态数码管

目录 一、原理图&#xff1a;​编辑 二、思路梳理&#xff1a; 三&#xff1a;一些说明&#xff1a; 1.点亮方式&#xff1a; 2.数组&#xff1a; 3.数字与段码对应&#xff1a; 四&#xff1a;程序实现&#xff1a; 一、原理图&#xff1a; 二、思路梳理&#xff1a; …

Cesium.js(6):Cesium相机系统

Camera表示观察场景的视角。通过操作摄像机&#xff0c;可以控制视图的位置、方向和角度。 帮助文档&#xff1a;Camera - Cesium Documentation 1 setView setView 方法允许你指定相机的目标位置和姿态。你可以通过 Cartesian3 对象来指定目标位置&#xff0c;并通过 orien…

【Python技术生态全景:十大核心应用领域深度解析】

目录 前言&#xff1a;Python的统治力一、基础应用领域1. Web开发数据科学 二、前沿技术领域机器学习深度学习 三、行业解决方案量化金融生物信息 四、创新应用方向物联网开发区块链开发 五、效率工具生态自动化运维游戏开发 结语&#xff1a;Python的边界与突破技术局限未来演…

leetcode 2787. Ways to Express an Integer as Sum of Powers

题目描述 这道题是0-1背包问题。可以理解为&#xff0c;有一个最大容量是n的背包&#xff0c;有n个物品&#xff0c;第i个物品的重量是i^x&#xff0c;问装满背包有多少种装法。题目要求必须是互不相同的数的x次幂的和等于n&#xff0c;那就表示每个数只能用一次&#xff0c;也…

面试经验分享 | 成都渗透测试工程师二面面经分享

可以看看我的置顶文章和专栏找我哦 概况 面试过程 面试官的问题 问题1、你觉得当前OAuth2.0下的攻击手段有哪些&#xff1f;结合具体案例详细讲讲 问题2、php/java反序列化漏洞的原理?程序员/运维如何避免此类漏洞或如何防御? 问题3、如果一台服务器被入侵后,你会如何做应急…

模仿axios的封装效果来封装fetch,实现baseurl超时等

因为要在cocos游戏项目里面发送网络请求获取数据&#xff0c;并且还有可能用到websocket发送请求&#xff0c;所以这里封装一个fetch放便使用&#xff1a; // fetch封装// 基础配置 const BASE_URL 你的url const TIMEOUT 5000// 请求封装 const http async (url: string, …

小米运维面试题及参考答案(80道面试题)

请讲解一下 linux top 后进程的状态 在 Linux 系统中,使用top命令可以查看系统中正在运行的进程的相关信息,进程通常有以下几种状态: 运行(R):表示进程正在 CPU 上运行或者正在运行队列中等待运行。处于运行状态的进程正在积极地使用 CPU 资源来执行其任务。睡眠(S):进…

a sort.py demo

这份代码展示了如何使用 sort.py。注意&#xff0c;此处&#xff0c;我将文件名改为 my_sort.py。 你并不能直接 copy 使用&#xff0c;因为环境&#xff0c;包&#xff0c;还有模型。 此处使用 SSD-MobileNetv2 进行物体检测&#xff0c;将结果传入以 np 数组的形式传入sort…

使用Redis解决:集群的Session共享问题

使用Redis解决&#xff1a;集群的Session共享问题 session共享问题&#xff1a;多台Tomcat并不共享session存储空间&#xff0c;当请求切换到不同的tomcat服务时导致数据丢失的问题。 问题背景 ​无状态HTTP协议&#xff1a;HTTP协议本身是无状态的&#xff0c;服务器无法直接识…

Linux 内核知识体系[1]

1 Linux内核知识体系 2.Linux内核学习路线 2.1基础知识准备 操作系统基础&#xff1a;了解操作系统的概念和基本原理&#xff0c;包括进程管理、内存管理、文件系统、输入输出等。 书籍&#xff1a;《操作系统&#xff1a;设计与实现》&#xff08;Andrew S. Tanenbaum&…