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

文章目录

  • 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.mzph.cn/news/139956.shtml

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

相关文章

C语言——打印1000年到2000年之间的闰年

闰年&#xff1a; 1、能被4整除不能被100整除 2、能被400整除 #define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int year;for(year 1000; year < 2000; year){if((year%4 0) && (year%100!0) || (year%400 0)){printf("%d ",ye…

【论文精读】DMVSNet

今天读的是一篇发表在ICCV 2023上的文章&#xff0c;作者来自华中科技大学。 文章地址&#xff1a;点击前往 项目地址&#xff1a;Github 文章目录 Abstract1 Introduction2 Relative Work3 Motivation3.1 Estimated bias and interpolated bias3.2 One-sided V.S. Saddle-shap…

图书网站信息采集

首先&#xff0c;你需要安装Haskell的HTTP库&#xff0c;比如http-conduit。你可以使用cabal包管理器来安装它。 然后&#xff0c;你需要定义一个函数来处理HTTP请求。这个函数需要接受一个URL和一个代理服务器的地址作为参数。 import Network.HTTP.ConduitgetURL :: String…

【Devchat 插件】创建一个GUI应用程序,使用Python进行加密和解密

VSCode 插件 DevChat——国内开源的 AI 编程&#xff01; 写在最前面DevChat是什么&#xff1f;什么是以提示为中心的软件开发 &#xff08;PCSD&#xff09;&#xff1f;为什么选择DevChat&#xff1f;功能概述情境构建添加到上下文生成提交消息提示扩展 KOL粉丝专属福利介绍D…

QT QStackedWidget

QStackedWidget是一个特殊的布局容器&#xff0c;它可以管理多个页面&#xff0c;并且只能显示其中一个页面。这些页面是QWidget或其派生类的实例&#xff0c;并通过调用addWidget()函数添加到堆栈中。 例如&#xff1a; #include <QWidgets> #include <QStackedWid…

ElasticSearch学习和使用 (使用head软件可视化es数据)

使用步骤 直接使用 Elasticsearch的安装和使用 下载Elasticsearch6.2.2的zip包&#xff0c;并解压到指定目录&#xff0c;下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2运行bin目录下的elasticsearch.bat启动Elasticsearch安…

CCNA课程实验-13-PPPoE

目录 实验条件网络拓朴需求 配置实现基础配置模拟运营商ISP配置ISP的DNS配置出口路由器OR基础配置PC1基础配置 出口路由器OR配置PPPOE拨号创建NAT(PAT端口复用) PC1测试结果 实验条件 网络拓朴 需求 OR使用PPPoE的方式向ISP发送拨号的用户名和密码&#xff0c;用户名&#xf…

四种常见分布式限流算法实现!

转载&#xff1a;四种常见分布式限流算法实现&#xff01; - 知乎 大家好&#xff0c;我是老三&#xff0c;最近公司在搞年终大促&#xff0c;随着各种营销活动“组合拳”打出&#xff0c;进站流量时不时会有一个小波峰&#xff0c;一般情况下&#xff0c;当然是流量越多越好&…

flutter逆向 ACTF native app

前言 算了一下好长时间没打过CTF了,前两天看到ACTF逆向有道flutter逆向题就过来玩玩啦,花了一个下午做完了.说来也巧,我给DASCTF十月赛出的逆向题其中一道也是flutter,不过那题我难度降的相当之低啦,不知道有多少人做出来了呢~ 还原函数名 flutter逆向的一大难点就是不知道l…

删除杀软回调 bypass EDR 研究

01 — 杀软或EDR内核回调简介 Windows x64 系统中&#xff0c;由于 PatchGuard 的限制&#xff0c;杀软或EDR正常情况下&#xff0c;几乎不能通过 hook 的方式&#xff0c;完成其对恶意软件的监控和查杀。那怎么办呢&#xff1f;别急&#xff0c;微软为我们提供了其他的方法&a…

如何从零开始手写一个消息中间件(从宏观角度理解消息中间件的技术原理)

如何从零开始手写一个消息中间件&#xff08;从宏观角度理解消息中间件的技术原理&#xff09; 什么是消息中间件消息中间件的作用逐一拆解消息中间件的核心技术消息中间件核心技术总览IOBIONIOIO多路复用AIOIO多路复用详细分析selectpollepoll Java中的IO多路复用 协议序列化消…

FD-Align论文阅读

FD-Align: Feature Discrimination Alignment for Fine-tuning Pre-Trained Models in Few-Shot Learning&#xff08;NeurIPS 2023&#xff09; 主要工作是针对微调的和之前的prompt tuining&#xff0c;adapter系列对比 Motivation&#xff1a; 通过模型对虚假关联性的鲁棒…

Python 的 datetime 模块

目录 简介 一、date类 &#xff08;一&#xff09;date 类属性 &#xff08;二&#xff09;date 类方法 &#xff08;三&#xff09;实例属性 &#xff08;四&#xff09;实例的方法 二、time类 &#xff08;一&#xff09;time 类属性 &#xff08;二&#xff09;tim…

JavaScript_动态表格_删除功能

1、动态表格_删除功能 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>动态表格_添加和删除功能</title><style>table{border: 1px solid;margin: auto;width: 100%;}td,th{text-align: …

图像相似度对比方法

1.哈希方法&#xff0c;其中包括均值哈希、插值哈希、感知哈希方法。计算出图片的哈希值&#xff0c;一般使用汉明 距离计算两个图片间的差距。 2.直方图算法&#xff0c;其中包括灰度直方图算法&#xff0c;RGB直方图算法&#xff0c; 3.灰度图算法&#xff1a;MSE、SSIM、…

ELK之Logstash解析时间相差8h的问题

一、问题描述 服务器当前时间为&#xff1a;2022年 06月 28日 星期二 11:24:22 CST 而logstash解析的时间为2022-06-28T03:15:25.545Z与实际时间相差8h 一、解决办法&#xff1a; 需改logstash的配置文件&#xff1a; 原理就是&#xff1a;定义一个中间变量timestamp&…

第十三章《搞懂算法:神经网络是怎么回事》笔记

目前神经网络技术受到追捧&#xff0c;一方面是由于数据传感设备、数据通信技术和数据存储技术 的成熟与完善&#xff0c;使得低成本采集和存储海量数据得以成为现实;另一方面则是由于计算能力的大幅提升&#xff0c;如图形处理器(Graphics Processing Unit&#xff0c;GPU)在神…

【数据结构】拓扑序列求法

概念不多说了&#xff0c;有疑问的搜一下&#xff0c;这里直接放求法&#xff1a; 找到入度为0的节点输出并删除该节点&#xff0c;并删除与该点链接的边重复第一步 例子 输出a&#xff0c;删除a输出b&#xff0c;删除b输出c&#xff0c;删除c 最终结果为abcdef 注意 拓扑排…

实战Leetcode(四)

Practice makes perfect&#xff01; 实战一&#xff1a; 这个题由于我们不知道两个链表的长度我们也不知道它是否有相交的节点&#xff0c;所以我们的方法是先求出两个链表的长度&#xff0c;长度长的先走相差的步数&#xff0c;使得两个链表处于同一起点&#xff0c;两个链…

杂记 | 使用FRP搭建内网穿透服务(新版toml配置文件,搭配反向代理食用)

文章目录 01 需求与回顾02 下载程序包03 编辑.toml文件3.1 编辑frps.toml3.2 编辑frpc.toml 04 启动服务4.1 启动服务端4.2 启动客户端 05 配置反向代理&#xff08;可选&#xff09;06 windows设置为默认启动&#xff08;可选&#xff09;6.1 创建启动脚本6.2 设置为开机自启 …