【数据结构初阶】排序算法(上)插入排序与选择排序

文章目录

  • 1.排序概念及运用
    • 1. 1 概念
    • 1. 2 运用
    • 1.3 常见排序算法
  • 2. 插入排序
    • 2. 1 直接插入排序
    • 2. 2 希尔排序
      • 2. 2. 1 希尔排序的时间复杂度
  • 3. 选择排序
    • 3. 1 直接选择排序
    • 3. 2 堆排序
    • 3. 3 Top-K问题


1.排序概念及运用

1. 1 概念

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

1. 2 运用

各大购物平台可以按综合,销量,评论数,新品等许多要素进行排序。
大学可以按照软科,校友会等多种要素进行排序。

1.3 常见排序算法

排序

2. 插入排序

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

实际中我们玩扑克牌时,就用了插入排序的思想:
扑克

2. 1 直接插入排序

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

我们以这个数组为例介绍一下详细步骤:

int a[] = {5, 3, 9, 6, 2, 4, 7, 1, 8};

比如说我们要把这个数组排成升序的。

首先我们找到第二个(此时i=1)元素3,逐一向后比较,直到找到比它小的数,放到这个数的前面,而这里就是放到数组的第一个元素的位置,也就是第一步是5和3交换位置。
此时:

int a[] = {3, 5, 9, 6, 2, 4, 7, 1, 8};

然后i++,对9进行操作,向前找比9小的,发现5就比9小,由于5前面的数字都已经有序了,所以9不在进行比较。
这一步对数组没有更改。

接着6,向前找比6小的,可以找到5,那么最终就是6与9交换了位置,

int a[] = {3, 5, 9, 6, 2, 4, 7, 1, 8};

接着2,向前找直接找到头,放到3的前面。这里要说一下,在每次比较之后,如果还需要进行下一步的比较,应该将比较过的两个数据交换,一步步往前走,而不是找到要到的位置之后进行一次交换,这样会打乱已经排序好的数据。

int a[] = {2, 3, 5, 9, 6, 4, 7, 1, 8};

到了这里,前4个数据就已经有序了,剩下的数据就不在赘述了,都是这个逻辑。

参考代码:

void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){//外层循环控制排序次数,是n-1次,因为第一个数不需要排序int cur = i;//这里并没有采取每次交换的方法,因为那样的效率太低了//这里采取的是先把要比较的那个数存储起来,然后如果需要发生交换时就让前面的数直接向后覆盖//最后找到指定位置时,把存储起来的数值放入数组//把要比较的数据存储起来int tmp = a[cur + 1];while (cur >= 0){if (a[cur] > tmp){//向后进行一次覆盖a[cur + 1] = a[cur];}else	//比较符合预期的话,就说明找到合适的位置break;//注意这时的cur指向的是比要排序的数据小的数据cur--;}//把存储起来的数据放进去a[cur + 1] = tmp;}
}

直接插入排序的特性总结

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N2)
  3. 空间复杂度:O(1)

时间复杂度其实很好计算,它和我们之前改进过的冒泡排序的时间复杂度是一样的,最差情况下要遍历
(n-1)(n-2)+(n-2)(n-2)+...,那么估计就是n2次。

2. 2 希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数(通常是gap=n/3+1),把待排序文件所有记录分成各组,所有的距离相等的记录分在同一组内,并对每一组内的记录进行排序,然后gap=gap/3+1得到下一个整数,再将数组分成各组,进行插入排序,当gap=1时,就相当于直接插入排序。它是在直接插入排序算法的基础上进行改进而来的,综合来说它的效率肯定是要高于直接插入排序算法的

这么说可能会不太容易理解,我们根据一个实例来分析一下步骤:
1
(图中被相同颜色的线连起来数据的是一组)
第一趟排序时,gap=5,我们可以找到2组,对这5组内的2个数据分别进行排序,使他们符合最终排序得相对有序。
第二趟排序时,gap=2,可以找到8组,对这2组内的5个数据分别进行排序,使他们符合最终排序得相对有序。
第三趟排序时,gap=1,可以找到9组,对这1组内的10个数据进行排序,使他们符合最终排序得相对有序。

最后一次排序就相当于直接插入排序,但是由于元素集合越接近有序,直接插入排序算法的时间效率越高,所以效率是远高于直接插入排序的

void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){//对gap进行迭代gap = gap / 3 + 1;//这个i是每轮排序中,排序的组数,是每组的第一个数据的下标for (int i = 0; i < n - gap; i++)	//i最大就是n-gap-1,再往后也找不到了{//分组,在每个组里使用直接插入排序int cur = i;	//cur是用来控制每个组内成员数据与它之前的组内成员数据进行比较的int tmp = a[cur + gap];	//实际要比较的数据是[cur+gap]这个数据//对组内成员进行操作while (cur >= 0){if (a[cur] < tmp)//cur的上一个是cur+gap,不是cur+1a[cur + gap] = a[cur];elsebreak;//cur迭代时要-gap来找到下一个cur -= gap;}a[cur + gap] = tmp;}}
}

2. 2. 1 希尔排序的时间复杂度

希尔排序的时间复杂度估算:
外层循环的时间复杂度可以直接给出,为:O(log2n)或者O(log3n),即O(log n)
内层循环的时间复杂度与gap的取值有关:
希尔篇排序

因此,希尔排序在最初和最后的排序的次数都为n,即前一阶段排序次数是逐渐上升的状态,当到达某一顶点时,排序次数逐渐下降至n,而该顶点的计算暂时无法给出具体的计算过程

希尔排序时间复杂度不好计算,因为 gap 的取值很多,导致很难去计算,因此很多书中给出的希尔排序的时间复杂度都不固定。
《数据结构(C语言版)》— 严蔚敏书中给出的时间复杂度为:
严蔚敏

3. 选择排序

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

3. 1 直接选择排序

  1. 在元素集合 array[0]--array[n-1]中选择关键码最大(小)的数据元素
  2. 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
  3. 左右各缩小一个元素的范围,重复上述步骤,直到集合剩余1个元素

直接选择排序十分便于理解,就不进行步骤分析了。

void SelectSort(int* a, int n)
{int end = n - 1;int begin = 0;while (begin < end){//mini和maxi存储找到的最大值和最小值的下标int mini = begin;int maxi = begin;for (int j = begin; j <= end; j++){//遍历数组时寻找最大值和最小值的下标if (a[mini] > a[j])mini = j;if (a[maxi] < a[j])maxi = j;}//处理特殊情况,下文解释if (begin == maxi)maxi = mini;//交换,Swap函数是用于交换两个数的自定义函数,不过多赘述//注意在传参的同时,对begin和end进行了迭代Swap(&a[mini], &a[begin++]);Swap(&a[end--], &a[maxi]);}
}

解释一下这个特殊情况:如果begin==maxi,并且没有进行if语句中的操作会发生什么?
以这个数组为例:

int a[] = { 9,3,2,1,4 };

此时maxi为0,mini为3,执行两个Swap语句,首先9和1互换,然后4和第一个数字1交换,很显然就出了问题,因为最大值被换走了,而maxi依然指向原来的位置。而maxi=mini之后的步骤就是:1和9交换,然后4和9交换,交换完成后就是:

int a[] = { 1,3,2,4,9 };

这样就能顺利完成任务了。
直接选择排序的特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N2)
  3. 空间复杂度:O(1)

3. 2 堆排序

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

注意:显然堆排序需要用到堆这个数据结构,其实现也不再赘述

步骤为:先将整个数组建堆,然后将堆顶和最后一个元素交换,此时可以保证最后一个元素就是最大(或最小)的,然后把end--,再进行调整,再重复上述过程,就能得到顺序的数组。

void HeapSort(int* a, int n)
{//建堆for (int i = (n - 2) / 2; i >= 0; i--)	//只需要排序到根节点为倒数第二层的最右边的节点就可以了AdjustDwon(a, n, i);int end = n - 1;while (end){//交换并调整Swap(&a[end], &a[0]);AdjustDwon(a, end, 0);end--;}
}
void AdjustDwon(int* a, int n, int root);

注:由于AdjustDown函数并没有要求传入的是堆,只是接受一个数组,所以可以这么写,当然创建一个堆来进行排序也是可以的。

void HeapSort(int* a, int n)
{HP hp;for (int i = 0; i < n; i++){HPPush(&hp, a[i]);}int i = 0;while (!HPEmpty(&hp)){a[i++] = HPTop(&hp);HPPop(&hp);}HPDestroy(&hp);
}

最终排序的是升序还是降序与建的是大堆(升序)还是小堆(降序)有关。

另外,为什么采取向下调整算法建堆?因为向上调整算法建堆的时间复杂度是O(n*log2n),而向下调整算法建堆的时间复杂度是O(n)。其计算较为复杂,可以看一眼,没必要记:
向上
向下

3. 3 Top-K问题

(这个问题不属于数据结构排序的内容,但与堆排序有关,所以放到这里)

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

  1. 用数据集合中前K个元素来建堆
    前k个最大的元素,则建小堆
    前k个最小的元素,则建大堆
  2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
    将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素

比如说我们要最大的K个数,基本思路就是建大小为K一个小堆,将前K个数据依次入堆并调整,然后开始向后遍历,如果遍历到的数据比堆顶大,就将堆顶换成这个数据,然后调整,循环进行这个过程,最后这个堆中的数据就是最大的K个数据了。

//这个函数调用一次就行了
//这个函数可以生成一个有10000个随机数的data.txt文件
void CreateNDate()
{// 造数据int n = 10000;srand(time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (int i = 0; i < n; ++i){int x = rand() % 1000000;	//给数据添加最大值fprintf(fin, "%d\n", x);}fclose(fin);
}void PrintTopK(int k)
{FILE* fl = fopen("data.txt", "r");if (!fl){perror("fopen");exit(1);}int* arr = (int*)malloc(k * sizeof(int));if (!arr){perror("malloc");exit(2);}for (int i = 0; i < k; i++)fscanf(fl, "%d", &arr[i]);//建堆for (int i = (k - 2) / 2; i >= 0; i--){AdjustDown(arr, i, k);}int tmp = 0;//遍历while (fscanf(fl, "%d", &tmp) != EOF){if (tmp > arr[0]){arr[0] = tmp;AdjustDown(arr, 0, k);}}for (int i = 0; i < k; i++){printf("%d ", arr[i]);}
}

这个函数中用到了文件操作和fscanf函数。
这里介绍一下fscanf函数:

int fscanf ( FILE * stream, const char * format, ... );

第一个为流,这里就是这个文件,剩下的和scanf函数是一样的格式化输入放到第三个参数中,只是是从文件的流中获取数据,而不是从标准输入流。

怎么测试这个算法的结果是否正确?
我们打开第一个函数生成的文件(这个文件怎么找可以在这篇博客中找到,不再赘述):
数据
随便找几个数据,为它们添上几位数:
添加
生成随机数时,我们给它添加了范围,所以最后的输出应该就是这几个改过的数字,这样就可以方便调试代码了。

时间复杂度:O(n)=k+(n-k)log2k

谢谢你的阅读,喜欢的话来个点赞收藏评论关注吧!
我会持续更新更多优质文章

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

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

相关文章

PySimpleGUI:简化 Python 中的 GUI 开发

作为一个算法工程师&#xff0c;避免不了需要标注数据&#xff08;当然还有其他需求&#xff09;&#xff0c;标注数据时还是需要一个很好的工具&#xff0c;此时需要你来写一个图形用户界面&#xff08;GUI&#xff09;&#xff0c;太难了&#xff5e; 然而&#xff0c;PySim…

Java语言程序设计基础篇_编程练习题**18.34 (游戏:八皇后问题)

目录 题目&#xff1a;**18.34 (游戏:八皇后问题) 代码示例 代码解析 输出结果 使用文件 题目&#xff1a;**18.34 (游戏:八皇后问题) 八皇后问题是要找到一个解决方案&#xff0c;将一个皇后棋子放到棋盘上的每行中&#xff0c;并且两个皇后棋子之间不能相互攻击。编写个…

B-树(不是B减树)原理剖析(1)

目录 B树的主要特性&#xff1a; B树的操作&#xff1a; B树的优点&#xff1a; 为什么要发明出B-树&#xff1f; B树的概念和原理剖析 原理图讲解(部分讲解在图中) 初始化结点&#xff1a; 处理数据数量计算(了解) 底层代码实现(加深理解) 前些日子我们学了AVl树&…

MySQL InnoDB undo log生成逻辑分析

引用《InnoDB存储引擎》中有一句话&#xff0c;特别重要&#xff1a; 用户通常对undo有这样的误解&#xff1a;undo用于将数据库物理地恢复到执行语句或事务之前的样子---但事实并非如此。 undo是逻辑日志&#xff0c;因此只是将数据库逻辑地恢复到原来的样子。所有的修改都被…

通信工程学习:什么是NFV网络功能虚拟化

NFV&#xff1a;网络功能虚拟化 NFV&#xff08;Network Function Virtualization&#xff09;&#xff0c;即网络功能虚拟化&#xff0c;是一种通过虚拟化技术实现网络功能的技术手段。它借鉴了x86服务器的架构&#xff0c;将传统的网络硬件设备如路由器、交换机、防火墙、负载…

neo4j:ubuntu环境下的安装与使用

一、neo4j安装 1. 下载安装包 进入网站&#xff1a;https://neo4j.com/deployment-center/#community 在上图中选择下载即可&#xff08;社区版免费&#xff09; 注意&#xff1a;neo4j的版本要和电脑安装的jdk版本对应&#xff0c;jdk版本使用java --version查看&#xff1a;…

华为认证HCIA篇--网络通信基础

大家好呀&#xff01;我是reload。今天来带大家学习一下华为认证ia篇的网络通信基础部分&#xff0c;偏重一些基础的认识和概念性的东西。如果对网络通信熟悉的小伙伴可以选择跳过&#xff0c;如果是新手或小白的话建议还是看一看&#xff0c;先有个印象&#xff0c;好为后续的…

复制他人 CSDN 文章到自己的博客

文章目录 0.前言步骤 0.前言 在复制别人文章发布时&#xff0c;记得表明转载哦 步骤 在需要复制的csdn 文章页面&#xff0c;打开浏览器开发者工具&#xff08;F12&#xff09;Ctrl F 查找"article_content"标签头 右键“Copy”->“Copy element”新建一个 tx…

【Godot4自学手册】第四十八节创建雨粒子效果

今天我们要利用GPU粒子节点玩雨粒子效果&#xff0c;下雨天。 一、添加GPU粒子系统 添加GPUParticles2D节点。选择根节点&#xff0c;单击添加按钮&#xff0c;选择GPUParticles2D&#xff0c;完成添加。 二、修改属性 1.设置粒子数量。 在GPUParticles2D检查器中将Amount设…

速记篇 |TCP/IP五层模型怎么背,OSI七层模型怎么背?

背景 记忆TCP/IP五层模型和OSI七层模型可以通过理解每一层的功能、作用以及它们之间的逻辑关系来进行。下面分别给出这两个模型的记忆方法和要点&#xff1a; TCP/IP五层模型 TCP/IP五层模型是一个简化的模型&#xff0c;从下到上依次为&#xff1a; 1.物理层&#xff08;Physi…

计算机毕业设计之:云中e百货微信小程序设计与实现(源码+文档+定制)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

微信小程序转化为uni-app项目

前言&#xff1a; 之前自己做一个uni-app的项目的时候前端需要实现一个比较复杂的动态tab和swiper切换的功能&#xff0c;但是由于自己前端抠脚的原因没有写出来&#xff0c;然后自己在网上搜索的时候发现了有个微信小程序里面的页面及极其的符合我的需求。那么问题来了我该如何…

『功能项目』QFrameWork拾取道具UGUI【69】

本章项目成果展示 我们打开上一篇68QFrameWork扔到地上UGUI的项目&#xff0c; 本章要做的事情是实现当物品在地上时&#xff0c;点击物品将对应物品转移到道具栏中 制作一个提示UI界面 添加Button组件设置为点击即将父物体隐藏 拖拽到文件夹中在场景中删除 创建脚本&#xf…

springboot实战学习(9)(配置mybatis“驼峰命名“和“下划线命名“自动转换)(postman接口测试统一添加请求头)(获取用户详细信息接口)

接着学习。之前的博客的进度&#xff1a;完成用户模块的注册接口的开发以及注册时的参数合法性校验、也基本完成用户模块的登录接口的主逻辑的基础上、JWT令牌"的组成与使用以及完成了"登录认证"&#xff08;生成与验证JWT令牌&#xff09;具体往回看了解的链接…

python虚拟环境创建使用

环境变量中配置 vi /etc/profile 注意安装完python环境之后要添加以下代码&#xff0c;配置虚拟环境的命令才能正确使用&#xff1a; PATH$PATH:/usr/local/python3 PATH$PATH:/usr/local/python3/bin 创建&#xff1a;virtualenv venv 激活虚拟环境&#xff1a;source ./v…

从预测性维护到智能物流:ARM边缘计算控制器的工业实践

工业4.0时代的到来&#xff0c;边缘计算技术成为连接物理世界与数字世界的桥梁。ARM架构的边缘计算控制器凭借其低功耗、高能效和灵活性等特点&#xff0c;在工业自动化领域展现出巨大潜力。本文将通过几个实际应用案例来探讨ARM边缘计算控制器是如何提升生产线效率和安全性的&…

【数据结构之线性表】有序表的合并(链表篇)

链表有序表的合并 思路图 将链表L1和L2按照顺序合并到L3中&#xff08;注&#xff1a;三个链表都是带头结点的&#xff09; A、要实现有序合并&#xff0c;必须先比较L1,L2两表中结点的大小&#xff0c;这里我们暂时先不讨论&#xff0c;直接根据图中来进行思路整理&#xff…

plt常用函数介绍二

目录 fig.add_subplot()ax.set()plt.legend()plt.subplots_adjust()plt.suptitle()plt.grid() fig.add_subplot() fig.add_subplot() 是 Matplotlib 中 Figure 对象的方法&#xff0c;用于在图形中添加子图&#xff08;subplot&#xff09;。 其语法为&#xff1a; subplot(…

linux网络编程8

24.9.25学习目录 一.原始套接字&#xff08;续&#xff09;1.sendto发送数据原始套接字1.ARP 二.Web编程1.概述2.HTML 一.原始套接字&#xff08;续&#xff09; 混杂模式&#xff1a; 指一台机器的网卡能够接受所有经过它的数据包&#xff0c;不论其目的地址是否是它&#xf…

程序人生:软件测试 非技术性面试题【建议每个测试人观看】

1、自我介绍&#xff1a;三分钟左右 2、为什么从郑州/太原离职&#xff1f; 3、你的职业规划是什么样的&#xff1f; 4、对下一家公司有什么自己的想法吗&#xff1f; 5、你觉得作为一名测试工程师&#xff0c;应该具备什么样的素养&#xff1f; 6、你觉得管理层&#xff…