排序算法总结(C++)

目录

  • 一、稳定性
    • 二、排序算法
      • 选择、冒泡、插入排序
      • 归并排序
      • 随机快速排序
      • 堆排序
      • 基数排序
      • 计数排序
  • 三、总结

一、稳定性

排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。

稳定性对基础类型对象(如平常的数组)来说毫无意义;稳定性对非基础类型对象(如一些自定义类)有意义,可以保留之前的相对次序。

二、排序算法

  • 基于比较的排序:
    只需要定义好两个对象之间怎么比较即可,对象的数据特征并不关心,很通用(除下面两种);

  • 不基于比较的排序:
    和比较无关的排序,对于对象的数据特征有要求,并不通用(如计数排序、基数排序);

选择、冒泡、插入排序

// 交换数组中的两个元素
void swap(int arr[], int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}// 选择排序
void selectionSort(int arr[], int n) {for (int i = 0; i < n - 1; i++) {int minIndex = i;for (int j = i + 1; j < n; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}swap(arr, i, minIndex);}
}// 冒泡排序
void bubbleSort(int arr[], int n) {for (int i = 0; i < n - 1; i++) {for (int j = 0; j < n - i - 1; j++) {if (arr[j] > arr[j + 1]) {swap(arr, j, j + 1);}}}
}// 插入排序
void insertionSort(int arr[], int n) {if (arr == nullptr || n < 2) {return;}for (int i = 1; i < n; i++) {for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {swap(arr, j, j + 1);// printarr(arr, n); // 打印当前数组状态}}
}

归并排序

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring> // 用于 std::memsetconst int MAXN = 100001;int arr[MAXN];
int help[MAXN];
int n;// 归并排序递归版
void mergeSort1(int l, int r) {if (l == r) {return;}int m = (l + r) / 2;mergeSort1(l, m);mergeSort1(m + 1, r);int i = l, a = l, b = m + 1;while (a <= m && b <= r) {help[i++] = arr[a] <= arr[b] ? arr[a++] : arr[b++];}while (a <= m) {help[i++] = arr[a++];}while (b <= r) {help[i++] = arr[b++];}for (i = l; i <= r; i++) {arr[i] = help[i];}
}// 归并排序非递归版
void mergeSort2() {for (int step = 1; step < n; step <<= 1) {int l = 0;while (l < n) {int m = l + step - 1;if (m + 1 >= n) {break;}int r = std::min(l + (step << 1) - 1, n - 1);int i = l, a = l, b = m + 1;while (a <= m && b <= r) {help[i++] = arr[a] <= arr[b] ? arr[a++] : arr[b++];}while (a <= m) {help[i++] = arr[a++];}while (b <= r) {help[i++] = arr[b++];}for (i = l; i <= r; i++) {arr[i] = help[i];}l = r + 1;}}
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(0);std::cin >> n;for (int i = 0; i < n; i++) {std::cin >> arr[i];}// 选择一种归并排序方式// mergeSort1(0, n - 1);mergeSort2();for (int i = 0; i < n - 1; i++) {std::cout << arr[i] << " ";}std::cout << arr[n - 1] << std::endl;return 0;
}

随机快速排序

#include <iostream>
#include <cstdlib> // 用于 rand() 和 srand()
#include <ctime>   // 用于 time()const int MAXN = 100001;
int arr[MAXN];
int n;// 随机快速排序改进版
void quickSort2(int l, int r) {if (l >= r) {return;}// 随机选择基准值int x = arr[l + rand() % (r - l + 1)];// 调用荷兰国旗问题的划分函数partition2(l, r, x);// 获取等于区域的左右边界int left = first;int right = last;// 递归排序左右两部分quickSort2(l, left - 1);quickSort2(right + 1, r);
}// 荷兰国旗问题的划分函数
int first, last;//分别控制左右两侧的边界移动void partition2(int l, int r, int x) {first = l;last = r;int i = l;while (i <= last) {if (arr[i] == x) {i++;} else if (arr[i] < x) {std::swap(arr[first++], arr[i++]);} else {std::swap(arr[i], arr[last--]);}}
}// 主函数
int main() {std::ios::sync_with_stdio(false);std::cin.tie(0);std::cin >> n;for (int i = 0; i < n; i++) {std::cin >> arr[i];}// 初始化随机数种子std::srand(std::time(nullptr));// 调用快速排序quickSort2(0, n - 1);// 输出排序结果for (int i = 0; i < n - 1; i++) {std::cout << arr[i] << " ";}std::cout << arr[n - 1] << std::endl;return 0;
}

堆排序

#include <iostream>
#include <vector>// 主函数,对数组进行排序
std::vector<int> sortArray(std::vector<int>& nums) {if (nums.size() > 1) {// heapSort1为从顶到底建堆然后排序// heapSort2为从底到顶建堆然后排序// 用哪个都可以// heapSort1(nums);heapSort2(nums);}return nums;
}// i位置的数,向上调整大根堆
// arr[i] = x,x是新来的!往上看,直到不比父亲大,或者来到0位置(顶)
void heapInsert(std::vector<int>& arr, int i) {while (arr[i] > arr[(i - 1) / 2]) {swap(arr, i, (i - 1) / 2);i = (i - 1) / 2;}
}// i位置的数,变小了,又想维持大根堆结构
// 向下调整大根堆
// 当前堆的大小为size
void heapify(std::vector<int>& arr, int i, int size) {int l = i * 2 + 1;while (l < size) {// 有左孩子,l// 右孩子,l+1// 评选,最强的孩子,是哪个下标的孩子int best = (l + 1 < size && arr[l + 1] > arr[l]) ? l + 1 : l;// 上面已经评选了最强的孩子,接下来,当前的数和最强的孩子之前,最强下标是谁best = (arr[best] > arr[i]) ? best : i;if (best == i) {break;}swap(arr, best, i);i = best;l = i * 2 + 1;}
}void swap(std::vector<int>& arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}// 从顶到底建立大根堆,O(n * logn)
// 依次弹出堆内最大值并排好序,O(n * logn)
// 整体时间复杂度O(n * logn)
void heapSort1(std::vector<int>& arr) {int n = arr.size();for (int i = 0; i < n; i++) {heapInsert(arr, i);}int size = n;while (size > 1) {swap(arr, 0, --size);heapify(arr, 0, size);}
}// 从底到顶建立大根堆,O(n)
// 依次弹出堆内最大值并排好序,O(n * logn)
// 整体时间复杂度O(n * logn)
void heapSort2(std::vector<int>& arr) {int n = arr.size();for (int i = n - 1; i >= 0; i--) {heapify(arr, i, n);}int size = n;while (size > 1) {swap(arr, 0, --size);heapify(arr, 0, size);}
}int main() {// 示例测试std::vector<int> nums = {3, 2, 1, 5, 6, 4};std::vector<int> sortedNums = sortArray(nums);std::cout << "排序后的数组: ";for (int num : sortedNums) {std::cout << num << " ";}std::cout << std::endl;return 0;
}

基数排序

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring> // 用于 memsetusing namespace std;const int BASE = 100; // 可以设置进制
const int MAXN = 50001;int help[MAXN];
int cnts[BASE];// 返回 number 在 BASE 进制下有几位
int bits(int number) {int ans = 0;while (number > 0) {ans++;number /= BASE;}return ans;
}// 基数排序核心代码
// arr 内要保证没有负数
// n 是 arr 的长度
// bits 是 arr 中最大值在 BASE 进制下有几位
void radixSort(vector<int>& arr, int n, int bits) {for (int offset = 1; bits > 0; offset *= BASE, bits--) {memset(cnts, 0, sizeof(cnts)); // 初始化计数数组for (int i = 0; i < n; i++) {// 数字提取某一位的技巧cnts[(arr[i] / offset) % BASE]++;}// 处理成前缀次数累加的形式for (int i = 1; i < BASE; i++) {cnts[i] = cnts[i] + cnts[i - 1];}for (int i = n - 1; i >= 0; i--) {// 前缀数量分区的技巧// 数字提取某一位的技巧help[--cnts[(arr[i] / offset) % BASE]] = arr[i];}for (int i = 0; i < n; i++) {arr[i] = help[i];}//      for (int num : arr) {//     cout << num << " ";// }// cout << endl;}
}vector<int> sortArray(vector<int>& arr) {if (arr.size() > 1) {int n = arr.size();// 找到数组中的最小值int min_val = arr[0];for (int i = 1; i < n; i++) {min_val = min(min_val, arr[i]);}int max_val = 0;for (int i = 0; i < n; i++) {// 数组中的每个数字,减去最小值,转成非负数组arr[i] -= min_val;// 记录数组中的最大值max_val = max(max_val, arr[i]);}// 根据最大值在 BASE 进制下的位数,决定基数排序做多少轮radixSort(arr, n, bits(max_val));// 数组中所有数都减去了最小值,最后还原for (int i = 0; i < n; i++) {arr[i] += min_val;}}return arr;
}int main() {int n;cout << "Enter the number of elements: ";// cin >> n;vector<int> arr({170, 45, 75, 90, 802, 24, 2, 66}); // 示例数组cout << "Enter the elements: ";// for (int i = 0; i < n; i++) {//     cin >> arr[i];// }vector<int> sorted_arr = sortArray(arr);cout << "Sorted array: ";for (int num : sorted_arr) {cout << num << " ";}cout << endl;return 0;
}

计数排序

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;// 计数排序方法,接受一个整数数组作为参数,返回排序后的数组
vector<int> countingSort(const vector<int>& arr) {// 如果输入数组为空或者长度小于等于1,直接返回原数组// 因为空数组或只有一个元素的数组本身就是有序的if (arr.size() <= 1) {return arr;}// 找出数组中的最大值和最小值,用于确定计数数组的范围int max_val = arr[0];int min_val = arr[0];for (int num : arr) {// 如果当前数字大于最大值,则更新最大值if (num > max_val) {max_val = num;}// 如果当前数字小于最小值,则更新最小值else if (num < min_val) {min_val = num;}}// 创建计数数组,其长度为最大值与最小值的差加1// 这样计数数组的每个索引就可以对应于原数组中的一个值(经过最小值偏移)vector<int> count(max_val - min_val + 1, 0);// 统计原数组中每个值出现的次数// 通过将每个数减去最小值来得到在计数数组中的偏移索引for (int num : arr) {count[num - min_val]++;}// 将计数数组转换为每个元素的最终位置索引数组// 这里的操作是对计数数组进行累加// 例如,如果count[0]=3,count[1]=2,那么经过这个循环后count[1]=5// 表示原数组中值为1(假设最小值为0)的最后一个元素应该放在新数组的第5个位置(索引为4)for (size_t i = 1; i < count.size(); i++) {count[i] += count[i - 1];}// 创建一个与原数组长度相同的输出数组,用于存储排序后的结果vector<int> output(arr.size());// 从原数组的末尾开始遍历for (int i = arr.size() - 1; i >= 0; i--) {// 根据计数数组中的位置信息,将原数组中的元素放入输出数组中正确的位置// 注意这里要先减1,因为数组索引从0开始output[count[arr[i] - min_val] - 1] = arr[i];// 将计数数组中对应位置的值减1,表示已经处理了一个该值的元素count[arr[i] - min_val]--;}// 返回排序后的数组return output;
}// 主函数,用于测试计数排序算法
int main() {vector<int> arr = {4, 2, 2, 8, 3, 3, 1};// 调用计数排序方法对数组进行排序vector<int> sorted = countingSort(arr);// 输出排序后的数组for (int num : sorted) {cout << num << " ";}cout << endl;return 0;
}

三、总结

在这里插入图片描述

  • 数据量非常小的情况下可以做到非常迅速:插入排序
  • 性能优异、实现简单且利于改进(面对不同业务可以选择不同划分策略)、不在乎稳定性:随机快排
  • 性能优异、不在乎额外空间占用、具有稳定性:归并排序
  • 性能优异、额外空间占用要求O(1)、不在乎稳定性:堆排序

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

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

相关文章

使用Redis作为缓存优化ElasticSearch读写性能

在现代数据密集型应用中,ElasticSearch凭借其强大的全文搜索能力成为许多系统的首选搜索引擎。然而,随着数据量和查询量的增长,ElasticSearch的读写性能可能会成为瓶颈。本文将详细介绍如何使用Redis作为缓存层来显著提升ElasticSearch的读写性能,包括完整的架构设计、详细…

获取wordpress某个栏目的内容数量

获取wordpress某个栏目的内容数量 <?php // 将以下 8 改成你的分类 ID 即可echo get_category(8)->count;?> 在制作wordpress模板时&#xff0c;有时会需要调用某个分类目录下的所有内容数量&#xff0c;通过这段简洁的代码就可以实现。 给WordPress自定义字段加…

uniapp 安卓 APP 后台持续运行(保活)的尝试办法

在移动应用开发领域&#xff0c;安卓系统的后台管理机制较为复杂&#xff0c;应用在后台容易被系统回收&#xff0c;导致无法持续运行。对于使用 Uniapp 开发的安卓 APP 来说&#xff0c;实现后台持续运行&#xff08;保活&#xff09;是很多开发者面临的重要需求&#xff0c;比…

深度学习——知识提炼

第一部分&#xff1a;引言与背景——为什么需要知识提炼&#xff1f; 一、模型压缩的背景 随着深度学习的发展&#xff0c;模型变得越来越大&#xff08;如 ResNet152、BERT、ViT、GPT 等&#xff09;&#xff0c;其参数量动辄数亿甚至上百亿。这些大模型虽然性能强大&#x…

开源之夏·西安电子科技大学站精彩回顾:OpenTiny开源技术下沉校园,点燃高校开发者技术热情

开源之夏2025编程活动正在如火如荼的进行中&#xff0c;当前也迎来了报名的倒计时阶段&#xff0c;开源之夏组织方也通过高校行系列活动进入各大高校&#xff0c;帮助高校开发者科普开源文化、开源活动、开源技术。 6月4日 开源之夏携手多位开源技术大咖、经验型选手走进西安电…

时间复杂度和算法选择

数据范围 时间复杂度 算法选择 n \leq 30 指数级别 O(2^n) 深度优先搜索&#xff08;DFS&#xff09; 剪枝、状态压缩动态规划 n \leq 100 O(n^3) Floyd 算法、动态规划、高斯消元 n \leq 1000 O(n^2) 、 O(n^2 \log n) 动态规划、二分…

数据分析实战2(Tableau)

1、Tableau功能 数据赋能&#xff08;让业务一线也可以轻松使用最新数据&#xff09; 分析师可以直接将数据看板发布到线上自动更新看板自由下载数据线上修改图表邮箱发送数据设置数据预警 数据探索&#xff08;通过统计分析和数据可视化&#xff0c;从数据发现问题&#xf…

CentOS7_Linux下安装Docker和docker-compose

目录 环境要求安装步骤1、修改镜像源配置文件2、卸载旧版本 Docker&#xff08;如有&#xff09;3、安装依赖工具4、添加 Docker 官方仓库5、安装 Docker 引擎6、启动 Docker 并设置开机自启7、验证安装8、配置镜像加速器创建配置文件重启 Docker 生效 9、允许非 root 用户操作…

ubuntu中使用docker

上一篇我已经下载了一个ubuntu:20.04的镜像&#xff1b; 1. 查看所有镜像 sudo docker images 2. 基于本地存在的ubuntu:20.04镜像创建一个容器&#xff0c;容器的名为cppubuntu-1。创建的时候就会启动容器。 sudo docker run -itd --name cppubuntu-1 ubuntu:20.04 结果出…

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…

idea中 maven 本地仓库有jar包,但还是找不到,解决打包失败和无法引用的问题

1、删除本地仓库中的文件 进入本地仓库对应jar包文件目录中删除_remote.repositories文件和结尾为.lastUpdated的文件 2、回到IDEA刷新Maven 3、查看之前引用不了的jar是否引入成功

ALOHA ACT算法与源码笔记

算法 一文通透动作分块算法ACT&#xff1a;斯坦福ALOHA团队推出的动作序列预测算法(Action Chunking with Transformers) 比较简单&#xff0c;算法题目里就写了&#xff1a;Action Chunking with Transformers&#xff0c;比较有特色的地方就是Action Chunking&#xff0c;核…

数字ic后端设计从入门到精通6(含fusion compiler, tcl教学)repeater详解

Repeaters RC延迟与导线长度的关系&#xff1a; 导线的电阻&#xff08;R&#xff09;和电容&#xff08;C&#xff09;都会随着导线长度&#xff08;l&#xff09;的增加而增大。RC延迟是电阻和电容共同作用导致的信号延迟。由于RC延迟与R和C的乘积有关&#xff0c;因此它会随…

Data Warebase 成功押注 PostgreSQL 生态,或成 AI 时代数据底座

本文内容整理自 ProtonBase CEO 王绍翾在 AICon 的主题演讲《Data Warebase: Instant Ingest-Transform-Explore-Retrieve for AI Applications》。作者的职业经历贯穿了 AI 1.0、2.0 和 3.0 的时代&#xff0c;从搜索推荐&#xff0c;到视觉 / 语音 / NLP 智能&#xff0c;再到…

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …

Kubernetes (k8s)版本发布情况

Kubernetes (k8s)版本发布情况 代码放在 GitHub - kubernetes/kubernetes: Production-Grade Container Scheduling and Management https://github.com/kubernetes/kubernetes/releases 文档放在 kubernetes.io各个版本变更等: https://github.com/kubernetes/kubernet…

Python 接口:从协议到抽象基 类(Python使用register的方式)

Python使用register的方式 示例 11-14 把 Tombola.register 当作类装饰器使用。在 Python 3.3 之 前的版本中不能这样使用 register&#xff0c;必须在定义类之后像普通函数那 样调用&#xff0c;如示例 11-14 中最后那行注释所述。 虽然现在可以把 register 当作装饰器使用了…

GRU 参数梯度推导与梯度消失分析

GRU 参数梯度推导与梯度消失分析 1. GRU 前向计算回顾 GRU 单元的核心计算步骤&#xff08;忽略偏置项&#xff09;&#xff1a; 更新门: z_t σ(W_z [h_{t-1}, x_t]) 重置门: r_t σ(W_r [h_{t-1}, x_t]) 候选状态: ̃h_t tanh(W_h [r_t ⊙ h_{t-1}, x_t]) 新…

【字节拥抱开源】字节团队开源视频模型 ContentV: 有限算力下的视频生成模型高效训练

本项目提出了ContentV框架&#xff0c;通过三项关键创新高效加速基于DiT的视频生成模型训练&#xff1a; 极简架构设计&#xff0c;最大化复用预训练图像生成模型进行视频合成系统化的多阶段训练策略&#xff0c;利用流匹配技术提升效率经济高效的人类反馈强化学习框架&#x…