算法与数据结构——简单排序算法(选择、冒泡、插入)

news/2024/9/28 1:10:26

简单排序算法

时间复杂度均为O(n2)

选择排序

选择排序(selection sort)的工作原理非常简单:开启一个循环,每轮从未排序区间选择最小的元素,将其放到已排序的区间的末尾。

算法流程

设数组长度为n,选择排序的算法流程如下。

  1. 初识状态下,所有元素未排序,即未排序(索引)区间为[1, n-1]。
  2. 选取区间[0,n-1]中的最小元素,将其与索引0处的元素交换。完成后,数组前1一个元素已排序。
  3. 选取区间[1,n-1]中的最小元素,将其与索引1处的元素交换,完成后,数组前2个元素已排序。
  4. 以此类推。经过n - 1轮选择与交换后,数组前 n-1个元素已排序。
  5. 仅剩的一个元素必定是最大元素,无需排序,因此数组排序完成。
/*选择排序*/
void selectionSort(vector<int> &nums){int n = nums.size();// 外循环:未排序区间为[i , n-1]for (int i = 0; i < n - 1; ++i){int  k = i;// 内循环:找到未排序区间的最小元素for (int j = i; j < n; ++j){if (nums[j] < nums[k])k = j;}// 将该最小元素与未排序区间的首个元素交换swap(nums[i], nums[k]);}
}

算法特性

  • 时间复杂度为O(n2)、非自适应排序:外循环共n-1轮,第一轮的未排序区间长度为n,最后一轮的未排序区间长度为2,即各轮外循环分别包含 𝑛、𝑛 − 1、…、3、2 轮内循环,求和为 (𝑛−1)(𝑛+2)/2 。
  • 空间复杂度为O(1)、原地排序:指针i和j使用常数大小的额外空间。
  • *非稳定排序**:如下图所示,元素nums[i]有可能被交换至与其相等的元素右边,导致两者的相对顺序发生改变。

冒泡排序

冒泡排序(bubble sort)通过连续地比较与交换相邻元素实现排序。整个过程就像气泡从底部升到顶部一样,因此得名冒泡排序。

冒泡过程可以利用元素交换操作来模拟:从数组最左端开始向右遍历,依次比较相邻元素大小,如果相邻左边的元素大于右边,则交换两者位置,这样最大的数就会逐渐冒到右端。

算法流程

设数组长度为n,冒泡排序步骤如下。

  1. 首先,对n个元素执行“冒泡”,将数组的最大元素交换至正确位置
  2. 接下来,对剩余n-1个元素执行“冒泡”,将剩余元素中的最大元素交换至正确位置
  3. 以此类推,经过n-1轮“冒泡”后,前n-1大的元素都被交换至正确位置
  4. 仅剩的一个元素必定是最小元素,无需排序,因此数组排序完成。

/*冒泡排序*/
void bubbleSort(vector<int> &nums){int n = nums.size();// 外循环:未排序区间为 [0, i]for (int i = n - 1; i > 0; --i){// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端for (int j = 0; j < i; ++j){if (nums[j] > nums[j + 1])// 交换 nums[j] 与 nums[j + 1]swap(nums[j], nums[j + 1]);}}
}

效率优化

可以发现,如果某轮“冒泡”中没有执行任何交换操作,说明数组已经完成排序,可以直接返回结果。因此可以增加一个标志位flag来监测这种情况,一旦出现就立即返回。

经过优化,冒泡排序的最差时间复杂度和平均之间复杂度仍为O(n2);但当输入数组完全有序时,可达到最佳时间复杂度O(n)。

/*冒泡排序(标志优化)*/
void bubbleSortWithFlag(vector<int> &nums){int n = nums.size();// 外循环:未排序区间为 [0, i]for (int i = n - 1; i > 0; --i){bool flag = true; // 初始化标志位// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端for (int j = 0; j < i; ++j){if (nums[j] > nums[j + 1]){// 交换 nums[j] 与 nums[j + 1]swap(nums[j], nums[j + 1]);flag = false; // 记录交换元素}}if (flag)break;  // 此轮“冒泡”未交换任何元素,直接跳出}
}

算法特性

  • 时间复杂度为O(n2)、自适应排序:各轮“冒泡”遍历的数组长度依次为 𝑛 − 1、𝑛 − 2、…、2、1 ,总和为 (𝑛 − 1)𝑛/2 。在引入 flag 优化后,最佳时间复杂度可达到 𝑂(𝑛) 。
  • 空间复杂度为O(1)、原地排序:指针i和j使用常数大小的额外空间。
  • 稳定排序:在“冒泡”中遇到相等元素不交换。

插入排序

插入排序(insertion sort)是一种简单的排序算法,它的工作原理与手动整理一副牌的过程非常相似。

具体来说,我们在未排序区间选择一个基准元素,将该元素与其左侧已排序区间的元素逐一比较大小,并将该元素插入到正确的位置。

下图展示了数组插入元素的操作流程。设基准元素为base,我们需要将从目标索引到base之间的所有元素右移一位,然后将base赋值给目标索引。

算法流程

  1. 初识状态下,数组的第1个元素已完成排序。
  2. 选取数组的第2个元素作为base,将其插入到正确的位置后,数组的前2个元素已排序。
  3. 选取第3个元素作为base,将其插入到正确位置后,数组的前3个元素一排序。
  4. 以此类推,在最后一轮中,选取最后一个元素作为base,将其插入到正确位置后,所有元素均已排序。
/*插入排序*/
void insertionSort(vector<int> &nums){// 外循环:已排序区间为[0, i-1]for (int i = 1; i < nums.size(); ++i){int base = nums[i], j = i - 1;// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置while (j >= 0 && base < nums[j]){nums[j + 1] = nums[j]; // 将nums[j]向右移动一位j--;}nums[j + 1] = base; // 将base赋值到正确位置}
}

算法特性

  • ** 时间复杂度为O(n2)、自适应排序**:在最差情况下,每次插入操作分别需要循环 𝑛 − 1、𝑛 − 2、…、2、1 次,求和得到 (𝑛 − 1)𝑛/2 ,因此时间复杂度为 𝑂(𝑛2)。在遇到有序数据时,插入操作会提前终止,当输入数组完全有序时,插入排序达到最佳时间复杂度O(n)。
  • 空间复杂度为O(1)、原地排序:指针i和j使用常数大小的额外空间。
  • 稳定排序:在插入操作过程中,我么会将元素插入到相等元素的右侧,不会改变它们的顺序。

插入排序的优势

插入排序的时间复杂度为O(n2),而后面将会提到的快速排序时间复杂度为O(nlogn)。尽管插入排序的时间复杂度更高,但在数据量较小的情况下,插入排序通常更快

这个结论与线性查找和二分查找的适用情况类似。快速排序这类O(nlogn)的算法属于基于分治策略的排序算法,往往包含更多单元计算操作。而在数据量较小时,n2和nlogn的数值比较接近,复杂度不占主导地位,每轮中的单元操作数量起到决定性作用。

实际上许多编程语言(例如Java)的内置排序函数采用了插入排序,大致思路为:对于长数组,采用基于分治策略的排序算法,例如快速排序;对于短数组,直接使用插入排序。

虽然冒泡排序、选择排序和插入排序的时间复杂度都为O(n2),但在实际情况中,插入排序的适用频率显著高于冒泡排序和选择排序

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

广州C++信奥老师解一本通题 1260:1282:最大子矩阵

​ 【题目描述】已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1 1)子矩阵。 比如,如下4 4的矩阵 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2的最大子矩阵是9 2 -4 1 -1 8这个子矩阵的大小是15。【输入】输入是一个NN的…

安装PyCharm

1访问官网https://www.jetbrains.com/pycharm/ 2下载下来如下图3傻瓜式安装,以下图标全部选中 4.打开后配置解释器interpreter

煤矿AI智能视频分析识别系统

煤矿AI智能视频分析识别系统是在现场已有监控系统的基础上,煤矿AI智能视频分析识别系统通过计算机视觉技术对现场人员行为以及物体状态进行实时分析检测。煤矿AI智能视频分析识别系统对皮带跑偏、撕裂、堆煤、异物、非法运人、有煤无煤状态等异常情况,以及人员工服穿戴、反光…

shell脚本之删除固定天数之前的文件

shell脚本之删除固定天数之前的文件 需求:删除30天之前的文件该需求一般用于自动清理程序日志,程序日志按每天分割之类的场景。#!/bin/bash #文件目录 addr=/apps/logs #需要删除的文件名称 file_name="*.log" #天数 days=30#查询并删除文件 find $addr -type f -m…

如何解决 :libstdc++.so.6: version `GLIBCXX_3.4.30‘ not found

如何解决 :libstdc++.so.6: version `GLIBCXX_3.4.30‘ not found 问题描述: 当您尝试在Linux系统上运行某个程序或软件时,有时会遇到一个错误,提示libstdc++.so.6: version GLIBCXX_3.4.30 not found。这个错误表明您的系统缺少某个特定版本的C++标准库,具体来说就是GLIBC…

MAGICORE:基于多代理迭代的粗到细精炼框架,提升大语言模型推理质量

大语言模型(LLM)的推理能力可以通过测试时聚合策略来改进,即为每个问题生成多个样本并对它们进行聚合以找到更好的答案。这些方法往往会达到饱和点,超过这个点后额外的样本不会带来更多收益。精炼(refinement)提供了另一种选择,它使用模型生成的反馈不仅采样更多解决方案,还提高…

JVM工具-jstat

jstat(JVM Statistics Monitoring Tool):查看 JVM 的统计信息 jstat -gc 1输出字段:S0C、S1C:Survivor 0 和 Survivor 1 区域的当前容量(KB)S0U、S1U:Survivor 0 和 Survivor 1 区域的已使用空间(KB)EC:Eden 区域的当前容量(KB)EU:Eden 区域的已使用空间(KB)OC:Old 区…

如何基于云产品搭建一个高可用性的网站

如何基于云产品搭建一个高可用性的网站? 从传统机房服务器迁移至云计算平台,无论是可用性的提高,还是维护成本的降低,都是云计算带来的不可否认的优势。本案示例通过使用天翼云常用的三个云产品 ECS+ELB+RDS 来帮助用户快速地在云端搭 建具备高可用性的网站 wordpress。本文…