NO.55十六届蓝桥杯备战|排序|插入|选择|冒泡|堆|快速|归并(C++)

插⼊排序

插⼊排序(Insertion Sort)类似于玩扑克牌插牌过程,每次将⼀个待排序的元素按照其关键字⼤⼩插⼊到前⾯已排好序的序列中,按照该种⽅式将所有元素全部插⼊完成即可

#include <iostream>  
using namespace std;  const int N = 1e5 + 10;  
int n;  
int a[N];  void insert_sort()  
{  // 依次枚举待排序的元素  for(int i = 2; i <= n; i++) // 第⼀个位置默认就是有序的  {  int key = a[i];  // 前⾯⽐ key ⼤的,统⼀右移  int j = i - 1;  while(j >= 1 && a[j] > key)  {  a[j + 1] = a[j];  j--;  }  a[j + 1] = key;  }  
}int main()  
{  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  insert_sort();  for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

时间复杂度

  • 当整个序列有序的时候,插⼊排序最优,此时时间复杂度为O(n) 。
  • 当整个序列逆序的时候,每个元素都要跑到最前⾯,时间复杂度为O(n2)

选择排序

选择排序(Selection Sort)是⼀种特别直观的排序算法。每次找出未排序序列中最⼩的元素,然后放进有序序列的后⾯

#include <iostream>  
using namespace std;  
const int N = 1e5 + 10;  
int n;  
int a[N];  
void selection_sort()  
{  for(int i = 1; i < n; i++) // 待排序区间的⾸位置  {  // [i, n] 区间就是待排序的区间  int pos = i;for(int j = i + 1; j <= n; j++) // 查找待排序区间最⼩的元素的下标  {  if(a[j] < a[pos])  {  pos = j;  }  }  swap(a[i], a[pos]);  }  
}  
int main()  
{  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  selection_sort();  for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

【时间复杂度】

  • ⽆论数组有序还是⽆序,在未排序区间内找最⼩值的时候,都需要遍历⼀遍待排序的区间,因此时间复杂度为O(n2)

冒泡排序

冒泡排序(Bubble Sort)也是⼀种简单的排序算法。它的⼯作原理是每次检查相邻两个元素,如果前⾯的元素与后⾯的元素满⾜给定的排序条件,就将相邻两个元素交换。当没有相邻的元素需要交换时,排序就完成了。
由于在算法的执⾏过程中,较⼤的元素像是⽓泡般慢慢浮到数列的末端,故叫做冒泡排序

#include <iostream>
using namespace std;  
const int N = 1e5 + 10;  
int n;  
int a[N];  
// 优化后的冒泡排序  
void bubble_sort()  
{  // 依次枚举待排序区间的最后⼀个元素  for(int i = n; i > 1; i--)  {  bool flag = false;  // [1, i] 就是待排序区间  for(int j = 1; j < i; j++)  {  if(a[j] > a[j + 1])  {  swap(a[j], a[j + 1]);  flag = true;  }  }  if(flag == false) return;  }  
}  
int main()  
{  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  bubble_sort();  for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

时间复杂度

  • 如果数组有序,只会扫描⼀遍,时间复杂度为O(n) 。
  • 如果逆序,所有元素都会向后冒⼀次到合适位置,时间复杂度为O(n2)

堆排序

堆排序(Heap Sort)是指利⽤堆这种数据结构所设计的⼀种排序算法。本质上是优化了选择排序算法,如果将数据放在堆中,能够快速找到待排序元素中的最⼩值或最⼤值。
堆排序的过程分两步:

  1. 建堆。升序建⼤堆,降序建⼩堆。
    建堆过程,从倒数第⼀个⾮叶⼦节点开始,倒着⼀直到根结点位置,每个结点进⾏向下调整。
  2. 排序。删除堆顶的逻辑。
    每次将堆顶元素与堆中最后⼀个元素交换,然后将堆顶元素往下调整。直到堆中剩余⼀个元素,所有元素就都有序了。
    因此,堆排序仅需⽤到向下调整算法
#include <iostream>  
using namespace std;  
const int N = 1e5 + 10;  
int n;  
int a[N];  
void down(int parent, int len)  
{  int child = parent * 2;  while(child <= len)  {  if(child + 1 <= len && a[child + 1] > a[child]) child++;  if(a[parent] >= a[child]) return;swap(a[parent], a[child]);  parent = child;  child = parent * 2;  }  
}  
void heap_sort()  
{  // 1. 建堆  for(int i = n / 2; i >= 1; i--)  {  down(i, n);  }  // 2. 排序  for(int i = n; i > 1; i--) // 枚举堆⾥⾯最后⼀个元素的位置  {  swap(a[1], a[i]);  down(1, i - 1);  }  
}  
int main()  
{  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  heap_sort();  for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

时间复杂度
时间复杂度需要计算两部分:⼀部分是建堆的时间复杂度,另⼀部分是排序。

  • 排序部分的时间复杂度很好估计,每次都是从堆中选择⼀个最⼤值,然后与最后⼀个元素交换,然后调整。⼀次选择的时间复杂度为log n ,⼀共执⾏n 次,⼤概就是n log n 级别。
  • 建堆的时间复杂度:
    计算向下调整算法建堆时间复杂度
    ![[Pasted image 20250322161630.png]]

则需要移动结点总的移动步数为:每层结点个数*向下调整次数
T ( h ) = 2 h − 1 − h T(h) = 2^h-1-h T(h)=2h1h
根据⼆叉树的性质:n = 2^h - 1 和h = log2 (n + 1)
T ( n ) = n − log ⁡ 2 ( n + 1 ) ≈ n T(n) = n - \log_{2}(n+1) \approx n T(n)=nlog2(n+1)n

综上所述,堆排序的时间复杂度主要取决于排序的过程,也就是n log n

快速排序

快速排序(Quick Sort),既然敢起这样的名字,说明它是常⻅排序算法中较为优秀的。事实上,在很多情况下,快排确实是效率较⾼的算法

算法原理

常规快排:在待排序区间中任取⼀个元素作为枢轴(pivot,或称基准值,通常选取区间⾸元素),然后按照基准值⼤⼩将区间中元素分割成左右两部分,左侧区间中元素⼩于基准值,右侧区间中元素⼤于等于基准值,即基准值已经放在其该放的位置上,该过程称为⼀次划分。划分结束后,再递归排基准值左侧,递归排基准值右侧即可
![[Pasted image 20250322163740.png]]

优化⼀:三路划分

三路划分优化:其实可以做到按照基准元素,将所有元素分成三个区间。左部分全部⼩于pivot,中间部分全部等于pivot,右部分全部⼤于pivot。然后中间部分就不⽤管了,直接递归处理左右部分

  1. 从区间中任取一个元素作为基准值,一般取区间最左侧元素,然后按照基准值对区间中元素进行划分
    ![[Pasted image 20250322164110.png]]

  2. 递归排基准值左侧;递归排基准值右侧
    ⽽这个三路划分,就是典型的数组分三块算法

优化⼆:随机选择基准元素

选择基准元素的⽅式:

  • 每次选择待排序序列的最左边元素
    那么,当整个序列有序的时候,每次递归就会划分出特别⻓的⼀段右区间,递归的层数是n次,每次要遍历整个数组⼀遍,时间复杂度就退化成n^2 。
    每次选择最右边元素也是同理。
  • 每次选择待排序序列的中间元素
    可以构造⼀个特殊的序列,使得每次取中间元素的时候都会取到最⼩值,依旧会退化成n^2。
  • 随机选择基准元素
    每次选择基准元素的时候,都从待排序的序列中随机选择⼀个数。在随机性的前提下,可以证明算法的时间复杂度趋近于nlogn 。
    因此,每次选择基准元素时,都使⽤随机函数选择
C++中的随机函数

C++提供了函数srand和rand,搭配使⽤可以返回⼀个随机值

#include <iostream>  
#include <ctime>  
using namespace std;  
int main()  
{  srand(time(0)); // 种下⼀个随机数种⼦  for(int i = 1; i <= 10; i++)  {  cout << rand() << endl; // 每次⽣成⼀个随机值  }  return 0;  
}

![[Pasted image 20250322171920.png]]

left和right指向第一个和最后一个元素,假设p等于4
![[Pasted image 20250322172121.png]]

i从第一个元素往最后一个元素遍历
现在ai是4,和p一样大,i++
![[Pasted image 20250322180638.png]]

ai是2,比p小,交换到前面,++l和i交换,i++
![[Pasted image 20250322180758.png]]

现在ai是4,和p一样大,i++
![[Pasted image 20250322180822.png]]

现在ai是5,比p大,交换到最右边,i和–r交换
![[Pasted image 20250322180911.png]]

i这是下标为4,ai为1,比p小,++l和i交换,i++
![[Pasted image 20250322181036.png]]

这是i等于5,r也是5,遍历结束,此时p=4,两个4已经在正确的位置上
中间区域也就是相等的区域,是l+1到r-1
左边比p小的区域是left到l,右边比p大的区域是r到right
接着遍历left到l和r到right

#include <iostream>  
#include <ctime>  
using namespace std;  
const int N = 1e5 + 10;  
int n;  
int a[N];  
// 优化⼀:随机选择基准元素  
int get_random(int left, int right)  
{  return a[rand() % (right - left + 1) + left];  
}  void quick_sort(int left, int right)  
{  if(left >= right) return;  // 1. 选择⼀个基准元素  int p = get_random(left, right);  // 2. 数组分三块 - 荷兰国旗问题  int l = left - 1, i = left, r = right + 1;  while(i < r)  {  if(a[i] < p) swap(a[++l], a[i++]);  else if(a[i] == p) i++;  else swap(a[--r], a[i]);  }  // [left, l] [l + 1, r - 1] [r, right]  quick_sort(left, l);  quick_sort(r, right);  
}  
int main()  
{  srand(time(0));  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  quick_sort(1, n);for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

时间复杂度

  • 如果每次基准元素都选择得当,数组划分的比较均匀,时间复杂度=递归层数*N*logN
  • 如果划分不当,数组分布比较极端,时间复杂度退化成N^2

归并排序

归并排序(Merge Sort)是⽆论数据有什么特性,时间复杂度就能稳定O(n log n) 的排序算法
归并排序⽤的是分治思想,不知道分治是啥也没关系,后续算法课会讲到。它的主要过程分两步:

  1. 将整个区间从中间⼀分为⼆,先把左边和右边排排序;
  2. 然后将左右两个有序的区间合并在⼀起。
    其中,如何让左右两边有序,就继续交给归并排序,因此归并排序是⽤递归来实现的;合并两个有序区间的操作,在顺序表中讲过类似的算法题
#include <iostream>  
using namespace std;  
const int N = 1e5 + 10;  
int n;  
int a[N];  
int tmp[N]; // 辅助归并排序时,合并两个有序数组  
void merge_sort(int left, int right)  
{  if(left >= right) return;  // 1. 先⼀分为⼆  int mid = (left + right) >> 1;  // [left, mid] [mid + 1, right]  // 2. 先让左右区间有序  merge_sort(left, mid);  merge_sort(mid + 1, right);// 3. 合并两个有序数组  int cur1 = left, cur2 = mid + 1, i = left;  // [left, mid] [mid + 1, right]  while(cur1 <= mid && cur2 <= right)  {  if(a[cur1] <= a[cur2]) tmp[i++] = a[cur1++];  else tmp[i++] = a[cur2++];  }  while(cur1 <= mid) tmp[i++] = a[cur1++];  while(cur2 <= right) tmp[i++] = a[cur2++];  for(int j = left; j <= right; j++)  {  a[j] = tmp[j];  }  
}  int main()  
{  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  merge_sort(1, n);  for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

【时间复杂度】
每次递归都是标准的⼀分为⼆,因此时间复杂度为O(n log n)

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

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

相关文章

【Oracle资源损坏类故障】:详细了解坏块

目录 1、物理坏块与逻辑坏块 1.1、物理坏块 1.2、逻辑坏块 2、两个坏块相关的参数 2.1、db_block_checksum 2.2、db_block_checking 3、检测坏块 3.1、告警日志 3.2、RMAN 3.3、ANALYZE 3.4、数据字典 3.5、DBVERIFY 4、修复坏块 4.1、RMAN修复 4.2、DBMS_REPA…

计算机网络高频(二)TCP/IP基础

计算机网络高频(二)TCP/IP基础 1.什么是TCP/IP⭐⭐ TCP/IP是一种网络通信协议,它是互联网中最常用的协议之一。TCP/IP有两个基本的协议:TCP(传输控制协议)和IP(互联网协议)。 TCP(Transmission Control Protocol,传输控制协议)是一种可靠的、面向连接的协议。它负…

【大模型算法工程】大模型应用工具化、忠诚度以及知识库场景下PDF双栏解析问题的讨论

1. 大模型时代应用工具化以及无忠诚度现象讨论 接触大模型久了&#xff0c;也慢慢探到一些大模型能力表现非常自然和突出的场景&#xff0c;比如AI搜索&#xff08;依赖大模型的理解总结能力&#xff09;、AI对话&#xff08;即chat&#xff0c;依赖大模型的生成能力&#xff0…

Java EE(13)——网络编程——UDP/TCP回显服务器

前言 本文主要介绍UDP和TCP相关的API&#xff0c;并且基于这两套API实现回显服务器 UDP和TCP UDP和TCP属于网络五层模型中传输层的协议 特点&#xff1a; UDP&#xff1a;无连接&#xff0c;不可靠&#xff0c;面向数据包&#xff0c;全双工 TCP&#xff1a;有连接&#xff…

【蓝桥杯】12111暖气冰场(多源BFS 或者 二分)

思路 这题可以用BFS做&#xff0c;也可以用二分来做。 用二分这里只提供一个思路&#xff1a;对时间来二分查找&#xff0c;check函数就是检查在特定的时间 t 0 t_0 t0​内每一个暖气炉的传播距离能否覆盖所有格子。 用BFS做&#xff1a; 由几个点开始向外扩散&#xff0c;知道…

使用bat批量获取WORD中包含对应字符的段落,段落使用回车换行

get_word_paragraphs.vbs 获取命令行参数 If WScript.Arguments.Count 0 ThenWScript.Quit 1 End If 获取 Word 文档路径 docPath WScript.Arguments(0) 创建 Word 应用程序对象 Set objWord CreateObject("Word.Application") objWord.Visible False 打开 Word …

DeepSeek自学手册:《从理论(模型训练)到实践(模型应用)》|73页|附PPT下载方法

导 读INTRODUCTION 今天分享是由ai呀蔡蔡团队带来的DeepSeek自学手册&#xff1a;《从理论&#xff08;模型训练&#xff09;到实践&#xff08;模型应用&#xff09;》&#xff0c;这是一篇关于DeepSeek模型训练、应用场景及替代方案的综合指南文章&#xff0c;主要介绍了Deep…

WEB API 设计规范

REST API 简介 REST 是 Representational State Transfer 的缩写&#xff0c;它将资源作为核心概念&#xff0c;通过 HTTP 方法对资源进行操作。其本身是一套围绕资源进行操作的架构规范。在实际应用中&#xff0c;更多的是体现在 API 的设计上。 企业在进行产品设计开发时&a…

QT软件匠心开发,塑造卓越设计服务

在当今这个数字化飞速发展的时代&#xff0c;软件已经成为我们生活中不可或缺的一部分。而QT&#xff0c;作为一款跨平台的C图形用户界面应用程序开发框架&#xff0c;凭借其强大的功能和灵活性&#xff0c;在众多软件开发工具中脱颖而出。我们深知&#xff0c;在软件开发领域&…

标贝科技入选2025年市级数据要素市场化配置改革“揭榜挂帅”名单

近日&#xff0c;山东省大数据局、青岛市大数据局公布2025年数据要素市场化配置改革“揭榜挂帅”名单。标贝科技联合崂山区电子政务和大数据中心申报的“政务热线通话录音数据价值挖掘与权益保护”项目成功入选。这一成果不仅彰显了标贝科技在数据领域的创新实力&#xff0c;更…

Flutter TextField 从入门到精通:掌握输入框的完整指南

目录 1. 引言 2. TextField 的基本用法 3. 主要属性 4. 自定义 TextField 样式 4.1 自定义边框与提示文本 4.2 增加前缀/后缀图标 4.3 只允许输入数字 4.4 表单验证系统 4.5 动态样式修改 4.6 防抖搜索&#xff08;Debounce&#xff09; 5. 结论 相关推荐 1. 引言…

蓝桥杯备赛 背包问题

背包问题 ![[背包问题.png]] 01背包 1.题意概要&#xff1a;有 n n n个物品和一个容量为 V V V的背包&#xff0c;每个物品有重量 w i w_i wi​和价值 v i v_i vi​ 两种属性&#xff0c;要求选若干物品放入背包使背包中物品的总价值最大且背包中物品的总重量不超过背包的容…

MyBatis-Plus 自动填充:优雅实现创建/更新时间自动更新!

目录 一、什么是 MyBatis-Plus 自动填充&#xff1f; &#x1f914;二、自动填充的原理 ⚙️三、实际例子&#xff1a;创建时间和更新时间字段自动填充 ⏰四、注意事项 ⚠️五、总结 &#x1f389; &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢…

arduino R4 SD卡读写测试

使用买来的 st7789LCD 显示器背面就带着一个 tf 卡槽&#xff0c;可以直接连接 tf 卡。使用 Sdfat 库就可以实现对 sd 卡的读写操作。这里尝试测试 sd 卡的读写功能。 LCD 显示器的初始化 //定义LCD的对象 Adafruit_ST7789 tft Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);tf…

【武汉·4月11日】Parasoft联合光庭信息研讨会|邀您共探AI赋能新机遇

Parasoft联合光庭信息Workshop邀您共探AI赋能新机遇 AI浪潮已至&#xff0c;你准备好了吗&#xff1f; 在智能网联汽车飞速发展的今天&#xff0c;AI技术正以前所未有的速度重塑行业生态。如何把握AI机遇&#xff0c;赋能企业创新&#xff1f; 4月11日&#xff0c;自动化软件…

VLLM专题(三十九)—自动前缀缓存(二)

前缀缓存(Prefix Caching)是一种在LLM推理中广泛使用的优化技术,旨在避免冗余的提示词(prompt)计算。其核心思想很简单——我们缓存已处理请求的键值缓存(kv-cache)块,并在新请求的前缀与之前请求相同时重用这些块。由于前缀缓存几乎是一种“免费的午餐”,并且不会改变…

自动驾驶系统的车辆动力学建模:自行车模型与汽车模型的对比分析

在自动驾驶系统的车辆动力学建模中&#xff0c;自行车模型&#xff08;Bicycle Model&#xff09;和更复杂的汽车模型&#xff08;如双轨模型或多体动力学模型&#xff09;各有其适用场景和优缺点。以下是两者的详细对比及选择原因解析&#xff1a; 1. 模型定义与核心差异 特性…

C语言入门教程100讲(6)类型修饰符

文章目录 1. 什么是类型修饰符&#xff1f;2. 常见的类型修饰符3. 类型修饰符的使用3.1 short 和 long3.2 signed 和 unsigned 4. 类型修饰符的组合5. 示例代码代码解析&#xff1a;输出结果&#xff1a; 6. 常见问题问题 1&#xff1a;short 和 long 的具体大小是多少&#xf…

Linux-Ubuntu 系统学习笔记 | 从入门到实战

&#x1f4d8; Linux-Ubuntu 系统学习笔记 | 从入门到实战 &#x1f4dc; 目录 环境安装基本操作Linux操作系统介绍文件系统常用命令用户权限管理编辑器vimGCC编译器动态库与静态库Makefile 1. 环境安装 &#x1f31f; 下载镜像 推荐使用清华大学开源镜像站下载Ubuntu镜像&a…

防火墙带宽管理

拓扑 配置 [fw]interface GigabitEthernet 0/0/0 [fw-GigabitEthernet0/0/0]service-manage all permit [fw]interface GigabitEthernet 1/0/0 [fw-GigabitEthernet1/0/0]ip address 12.0.0.1 24 [fw]interface GigabitEthernet 1/0/1 [fw-GigabitEthernet1/0/1]ip ad…