数据结构—内部排序(上)

文章目录

  • 8.内部排序(上)
    • (1).排序基础
      • #1.为什么是内部排序
      • #2.排序的稳定性
    • (2).冒泡排序
      • #1.算法思想
      • #2.代码实现
      • #3.稳定性与时间复杂度分析
    • (3).选择排序
      • #1.算法思想
      • #2.代码实现
      • #3.稳定性与时间复杂度分析
    • (4).插入排序
      • #1.算法思想
      • #2.代码实现
      • #3.稳定性与时间复杂度分析
    • (5).希尔排序
      • #1.算法思想
      • #2.代码实现
      • #3.稳定性和时间复杂度分析
    • 小结

8.内部排序(上)

  所以其实学会对一组数据进行排序,是我们很自然的想法,比如我们在玩扑克牌的时候随机摸了一组牌,为了之后打起来方便,你可能也会按照花色和数字进行排序,所以,让我们来了解一下排序吧!

(1).排序基础

#1.为什么是内部排序

  与内部排序相对的,就是外部排序,在数据量极其庞大的情况下,我们可能无法一次性将待排序的数据全部载入内存再排序,而这种涉及到内存与外存数据交换的排序方式就称为外部排序,与之相对的就是内部排序,我们可以很简单的一次性把所有数据载入内存,用各种方式完成排序。

  外部排序的流程比较复杂,因此基于数据结构这门课的这系列博客当中,我们会简要介绍内部排序的基本内容。

#2.排序的稳定性

  排序算法是具有稳定性的说法的,在待排序的数据当中,基于排序规则相同的一系列数据如果能在排序完成后保持原有的顺序,则称这种排序算法是稳定的
  例如5, 1, 2(1), 4(1), 4(2), 2(2), 4(3),稳定排序的结果应当是:1,2(1), 2(2), 4(1), 4(2), 4(3), 5

(2).冒泡排序

#1.算法思想

  冒泡排序是相当自然的排序方法,假设从小到大排序,我们从左往右,依次比较相邻的元素,如果满足左边小于等于右边,就继续比较下面两个元素,如果顺序错误,则交换两个元素,继续交换

#2.代码实现

  如此继续下去可以保证每轮都能找出一个最大值,并且将它放在最后,这样的过程就好像一个个泡泡从水底逐渐冒到了水面上,故称为冒泡排序,它的代码实现如下:

#include <iostream>
#include <vector>
using namespace std;void bubble_sort(vector<int>& v)
{bool isSort{ false };for (int i = v.size() - 1; i >= 0; i--) {for (int j = 0; j < i; j++) {if (v[j] > v[j + 1]) {int tmp{ v[j + 1] };v[j + 1] = v[j];v[j] = tmp;isSort = true;}}if (!isSort) break;}
}int main()
{vector<int> v{ 3, 2, 3, 5, 6, 7, 8, 9, 1, 23 };bubble_sort(v);for (auto& i : v) {cout << i << " ";}cout << endl;return 0;
}

#3.稳定性与时间复杂度分析

  我们每次将冒泡的上界减一,然后每次再找到位置错误的进行交换,一直这样循环下去即可。接下来分析一下冒泡排序的时间复杂度,我们有两重循环,可以得到:
T ( n ) = ∑ k = 1 n ( n − k ) = n ( n − 1 ) 2 ⇒ O ( T ( n ) ) = O ( n 2 ) T(n) = \sum_{k=1}^n (n-k) = \frac{n(n-1)}{2}\Rightarrow O(T(n))=O(n^2) T(n)=k=1n(nk)=2n(n1)O(T(n))=O(n2)
  所以冒泡排序是比较基本的 O ( n 2 ) O(n^2) O(n2)排序算法,然后是稳定性,其实这个好说,因为在冒泡排序中我们只涉及到前后两个顺序错误元素的交换,因此相同的元素在这个过程中是不会被交换的,所以冒泡排序是一种稳定的排序方式

(3).选择排序

#1.算法思想

  选择排序则要更接近我们自己尝试排序的过程,比如3, 2, 3, 5, 6, 7, 8, 9, 1, 23这个序列,我们首先找到最小值1,放到最前面:1, 3, 2, 3, 5, 6, 7, 8, 9, 23,然后从1后取第二个最小值放到第二个位置:1, 2, 3, 3, 5, 6, 7, 8, 9, 23,然后就这么做下去吧!每次找出最小值或者最大值,放到上一次放的下一个位置,就可以完成整个排序流程了,这就是插入排序。

#2.代码实现

  每次选择一个最小或最大的值出来,放到头或尾去,因此这个算法称为选择排序。

#include <iostream>
#include <vector>
using namespace std;void selection_sort(vector<int>& v)
{for (int i = 0; i < v.size() - 1; i++) {int min_idx{ i };for (int j = i + 1; j < v.size(); j++) {if (v[min_idx] > v[j]) {min_idx = j;}}int tmp{ v[i] };v[i] = v[min_idx];v[min_idx] = tmp;}
}int main()
{vector<int> v{ 3, 2, 3, 5, 6, 7, 8, 9, 1, 23 };selection_sort(v);for (auto& i : v) {cout << i << " ";}cout << endl;return 0;
}

  这段代码主要也就做了两件事:找后续值的最小值以及放到对应的位置上去

#3.稳定性与时间复杂度分析

  还是先看看时间复杂度吧,这里就不推了,其实还是和冒泡排序相似的过程,选择排序的时间复杂度仍然是 O ( n 2 ) O(n^2) O(n2),所以我们想要突破 O ( n 2 ) O(n^2) O(n2)的时间复杂度,好像非常困难啊。

  然后是稳定性,选择排序实际上是一种不稳定的排序方式,比如我们前面写的代码,对于这个序列:1, 2, 3, 2, 1,当i走到1的时候,2会与后面的1进行一次交换,而这样做之后就破坏了原本两个2的顺序,这样就导致选择排序变得不稳定了起来,比如:
p16

(4).插入排序

#1.算法思想

  插入排序在我们玩扑克牌的时候比较常用,比如我们拿到了一副牌:A,7,4,2,Q,9,J,那我们可能会这么排,首先A不动,看到7,也不动,再到4,插到A和7之间,再把2插入A和4之间,就这么做下去,直到变成A,2,4,7,9,J,Q这样的顺序,这就是插入排序,我们依次向后遍历,然后找到对应元素在前面已经有序的序列中的位置,插入进去,这就是插入排序的基本流程

#2.代码实现

  我们这里把找到合适的位置和移动合在一起,可以减少一次遍历的开销

#include <iostream>
#include <vector>
using namespace std;void insertion_sort(vector<int>& v)
{for (int i = 1; i < v.size(); i++) {int tmp{ v[i] };int j = i - 1;for (; j >= 0 && tmp < v[j]; j--) {v[j + 1] = v[j];}v[j + 1] = tmp;}
}int main()
{vector<int> v{ 1, 2, 3, 2, 1 };insertion_sort(v);for (auto& i : v) {cout << i << " ";}cout << endl;return 0;
}

#3.稳定性与时间复杂度分析

  插入排序的复杂度同样为 O ( n 2 ) O(n^2) O(n2),不过插入排序对于基本有序的序列,插入排序的移动和查找操作次数非常少,所以之后在设计你自己的排序方式的时候,可以尝试在基本有序的情况下使用插入排序优化,而在比较混乱的情况下采取更快的排序方式

  稳定性其实很好分析,我们的排序过程中不存在直接交换两个点的过程,只是依次向左交换,因此插入排序可以保证稳定性。

  还有一个点需要注意的点,插入排序对于链式存储也是比较容易的,因为不涉及到点的随机访问。对了,对于数组的实现,我们或许还可以使用二分查找的方式更快地找到插入的位置,不过时间复杂度是不会变的哦

(5).希尔排序

#1.算法思想

  希尔排序是一种基于插入排序的修改法则,我们在插入排序的过程中每次往前移动一个位置,而如果能一次移动更多位置,或许排序的速度就能被优化了。

  因此希尔排序(递减增量排序)就出现了,我们给出 d 0 , d 1 , d 2 , . . . , d t − 1 d_0,d_1,d_2,...,d_{t-1} d0,d1,d2,...,dt1这么一系列严格递减的正整数增量,且 d t − 1 = 1 d_{t-1}=1 dt1=1
,每次排序的时候,对整个序列按照增量为 d i d_i di进行分组,然后对不同的分组分别进行排序,直到最后再用增量为1的序列对所有数据进行一次排序。

  需要注意的是,我们可以保证这么排序一定至少不会让时间复杂度变高,因为插入排序本身对于基本有序的数据的排序时间复杂度就会更低。

#2.代码实现

  基本上,我们只需要在基本的插入排序前面加一层对于递减增减的序列就好了,然后把对应的j-1改成j-delta,就好了:

#include <iostream>
#include <vector>
using namespace std;void shell_sort(vector<int>& v)
{vector<int> d{ 7, 5, 3, 1 };for (int delta = 0; delta < d.size(); delta++) {int h = d[delta];for (int i = h; i < v.size(); i++) {int tmp{ v[i] };int k{ i - h };for (; k >= 0 && tmp < v[k]; k -= h) {v[k + h] = v[k];}v[k + h] = tmp;}}
}int main()
{vector<int> v{ 1, 2, 3, 2, 1, 2, 3, 2, 1 };shell_sort(v);for (auto& i : v) {cout << i << " ";}cout << endl;return 0;
}

#3.稳定性和时间复杂度分析

  希尔排序的时间复杂度很难分析,它的时间复杂度基本基于你的增量序列选择,这里要注意,增量序列要避免出现因子,比如9和3同时出现就会多一次没有必要的排序。

  Knuth在1969年推荐了这样一个序列: d i + 1 = 3 d i + 1 d_{i+1}=3d_i+1 di+1=3di+1,经过分析,比较的次数不超过 O ( n 3 2 ) O(n^{\frac{3}{2}}) O(n23)我们终于突破了 O ( n 2 ) O(n^2) O(n2)!

小结

  最近实在是太忙,今天只能暂时先把内部排序的上半部分发出来了,下半部分我们会讲讲归并排序、快速排序和基数排序的内容。

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

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

相关文章

AI:86-基于深度学习的人体姿态估计与运动分析

🚀 本文选自专栏:人工智能领域200例教程专栏 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的代码,详细讲解供大家学习,希望可以帮到大家。欢迎订阅支持,正在不断更新中,…

Python:Unittest框架快速入门:用例、断言、夹具、套件、HTML报告、ddt数据驱动

快速看了套Unittest的入门教程 软件测试全套资料赠送_哔哩哔哩_bilibili软件测试全套资料赠送是快速入门unittest测试框架&#xff01;全实战详细教学&#xff0c;仅此一套&#xff01;的第1集视频&#xff0c;该合集共计11集&#xff0c;视频收藏或关注UP主&#xff0c;及时了…

IP-guard WebServer 远程命令执行漏洞

IP-guard WebServer 远程命令执行漏洞 免责声明漏洞描述漏洞影响漏洞危害网络测绘Fofa: app"ip-guard" 漏洞复现1. 构造poc2. 访问文件3. 执行命令 免责声明 仅用于技术交流,目的是向相关安全人员展示漏洞利用方式,以便更好地提高网络安全意识和技术水平。 任何人不…

flink1.18.0 自适应调度器 资源弹性缩放 flink帮你决定并行度

jobmanager.scheduler Elastic Scaling | Apache Flink 配置文件修改并重启flink后,webui上会显示调整并行度的按钮,他可以自己调整,你也可以通过webUI手动调整: 点击 之后: 调整完成后:

Postman常见报错与解决方法,持续更新~

postman中文文档 基本操作&#xff1a;从控制台查看请求报错 如果 Postman 无法发送你的请求&#xff0c;或者如果它没有收到你发送请求的 API 的响应&#xff0c;你将收到一条错误消息。此消息将包含问题概述和指向控制台的链接&#xff0c;你可以在其中访问有关请求的详细信…

企业计算机中了mkp勒索病毒怎么办,服务器中了勒索病毒如何处理

计算机技术的不断发展给企业的生产生活提供了极大便利&#xff0c;但也为企业带来了网络安全威胁。近期&#xff0c;云天数据恢复中心陆续接到很多企业的求助&#xff0c;企业的计算机服务器遭到了mkp勒索病毒攻击&#xff0c;导致企业的所有工作无法正常开展&#xff0c;给企业…

HTTP协议详解-下(Tomcat)

如何构造 HTTP 请求 对于 GET 请求 地址栏直接输入点击收藏夹html 里的 link script img a…form 标签 通过 form 标签构造GET请求 <body><!-- 表单标签, 允许用户和服务器之间交互数据 --><!-- 提交的数据报以键值对的结果来组织 --><form action&quo…

JavaEE初阶学习:Linux 基本使用和 web 程序部署

1.Linux的基本认识 Linux 是一个操作系统.(搞管理的系统) 和Windows都是同类产品~~ Linux 实际的场景: 1.服务器 2.嵌入式设备 3.移动端(手机)Android 其实就是Linux 1991年,还在读大学的 芬兰人 Linus Benedict Torvalds,搞了一个Linux 这样的系统0.01版,正式发布了~ 后…

网康NS-ASG安全网关任意文件读取

此文件没有对身份进行校验即可下载任意文件 构造payload访问漏洞url&#xff1a; ​​/admin/cert_download.php?filegjxbstxdt.txt&certfile../../../../../../../../etc/passwd漏洞证明&#xff1a; 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&…

如何在 Python 中执行 MySQL 结果限制和分页查询

Python MySQL 限制结果 限制结果数量 示例 1: 获取您自己的 Python 服务器 选择 “customers” 表中的前 5 条记录&#xff1a; import mysql.connectormydb mysql.connector.connect(host"localhost",user"您的用户名",password"您的密码"…

Ubuntu诞生已经19年了

导读2004 年 10 月 20 日&#xff0c;Ubuntu 4.10 正式发布&#xff0c;代号‘Warty Warthog’。 2004 年 10 月 20 日&#xff0c;Ubuntu 4.10 正式发布&#xff0c;代号‘Warty Warthog’。 ▲ Ubuntu 4.10 与最新版 Ubuntu 23.10 的对比 作为 Ubuntu 第一个版本&#xff0…

Git GUI、SSH协议和IDEA中的Git使用详解

目录 前言 一、Git GUI的使用 1. 什么是Git GUI 2. 常见的Git GUI工具 3.使用 4.使用Git GUI工具的优缺点 优点&#xff1a; 缺点&#xff1a; 二、SSH协议 1.什么是SSH协议 2.SSH的主要特点和作用 3.SSH密钥认证的原理和流程 4. SSH协议的使用 三、IEDA使用git …