一文带你了解所有常用排序算法

目录

快速排序

堆排序

桶排序

归并排序

拓扑排序

本文主要介绍那些我在刷题过程中常用到的排序算法: 快速排序,堆排序,桶排序,归并排序,拓扑排序

其余算法例如冒泡,插入这种效率特别低的算法就不介绍了,用的可能性极小

每一个算法都将采用例题加解释的方式进行介绍


快速排序

所谓快速排序,简单说就是枚举出一个中心点x,用双指针找出左边第一个大于等于x的元素i和右边第一个小于等于x的元素j,交换这两个元素,使得i左边维护的都是小于等于x的元素,j右边都是大于等于x的元素,再缩小范围继续排序

有点抽象是不是,那我们看一下快排的模板

void quick_sort(int q[], int l, int r)
{if (l >= r) return;int i = l - 1, j = r + 1, x = q[l + r >> 1];while (i < j){do i ++ ; while (q[i] < x);do j -- ; while (q[j] > x);if (i < j) swap(q[i], q[j]);}quick_sort(q, l, j), quick_sort(q, j + 1, r);
}

可以看出排序操作是通过swap完成的,找到不符合要求的元素后交换他们的位置,使得i左边的元素都是小于x的,而j右边的元素都是大于x的,这样我们就可以把小于x和大于x的元素分开,但是分开后元素依旧是无序的,因此我们需要递归,缩小范围直到区间内只有一个元素

而递归的边界即可以用j表示也可以用i表示,当用i表示时递归函数如下

quick_sort(q,l,i-1),quick_sort(q,i,r);

那什么时候用i什么时候用j呢,这就涉及到我们的边界问题

关于边界问题我这里引用别人的文章,里面已经说的很详细了:AcWing 785. 快速排序 - AcWing

如果不想了解这么多直接背模板就可以了,模板是可以直接套的

acwing 785.快速排序

给定你一个长度为n的整数数列。

请你使用快速排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

数据范围:
1 ≤ n ≤ 100000

参考代码:

#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int n;
int q[N];void quick_sort(int q[], int l, int r)
{if (l>=r) return;int x = q[(l+r)/2], i = l - 1, j = r + 1;while(i<j){do i++; while(q[i]<x);do j--; while(q[j]>x);if (i<j) swap(q[i],q[j]);}quick_sort(q,l,j);quick_sort(q,j+1,r);
}int main()
{scanf("%d",&n);for (int i=0;i<n;i++)scanf("%d",&q[i]);quick_sort(q,0,n-1);for (int i=0;i<n;i++)printf("%d ",q[i]);return 0;
}

堆排序

堆排序一般是通过priority_queue这个数据结构来实现的,通过优先队列实现大顶堆小顶堆,用起来是极其方便的,而且排序效率高,时间复杂度为nlogn,唯一的缺点就是不好获取队列中间的元素

priority_queue<int,vector<int>,less<int>>q;//小顶堆
priority_queue<int,vector<int>,greater<int>>q;//大顶堆,不填第三个参数默认是大顶堆

优先队列的第三个参数是比较器,我们可以传入我们自定义的比较函数,也可以使用内置的表达方式如less<int>,表示整数越小的优先级越高

leetcode 347.前 K 个高频元素

347. 前 K 个高频元素 - 力扣(LeetCode)

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

利用优先队列维护一个大小为k的小顶堆即可,参考代码如下:

class Solution {
public:static bool cmp(pair<int,int>&n,pair<int,int>&m){return n.second>m.second;}vector<int> topKFrequent(vector<int>& nums, int k)  {unordered_map<int,int>umap;for(auto &p:nums){umap[p]++;}priority_queue<pair<int,int>,vector<pair<int,int>>,decltype(&cmp)>q(cmp);for(auto &a:umap){q.push(a);if(q.size()>k)q.pop();}vector<int>ans;while(!q.empty()){ans.emplace_back(q.top().first);q.pop();}return ans;}
};

关于优先队列的比较器,我在我的题解中有详细介绍:. - 力扣(LeetCode)

简单来说就是如果是内置数据类型可以使用内置的比较函数(如less,greater),如果是自定义类型则需要自定义比较函数,传入自定义函数有两种方式:1.将比较函数写在结构体中 2.用decltype获取函数类型

有兴趣的同学可以做一下 leetcode 295. 数据流的中位数 这题,考察的是大小顶堆的使用

桶排序

桶排序有点类似于我们的哈希表,原理就是将元素一一映射到数组的一个位置上,每一个元素的映射规则都相同,映射结果唯一

f(x1)=y1 f(x2)=y2           

有且仅有x1==x2时才有y1==y2

桶排序的映射函数并不复杂,往往是在原索引的基础上加上一个偏移量,在数组中完成映射

例如0<=nums[i]<100那么我们就可以通过nums[i]的大小确定映射到哪个桶,因为nums[i]<100,所以我们用大于等于100的数去偏移就不会产生冲突nums[i]->bucket[nums[i]+100]

leetcode 215. 数组中的第K个最大元素数组中的第K个最大元素

215. 数组中的第K个最大元素 - 力扣(LeetCode)

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

其中 -104 <= nums[i] <= 104

看到这个数据范围,我们可以发现这题可以使用桶排序,将nums[i]的值作为索引再加一个偏移量即可

参考代码:

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {vector<int>bucket(21001,0);for(int i=0;i<nums.size();i++){bucket[nums[i]+10000]++;}int cnt=0;for(int i=21000;i>=0;i--){cnt+=bucket[i];if(cnt>=k){return i-10000;}}return -1;}
};

归并排序

归并排序和快速排序的思想有点类似,都是用递归实现,只不过归并注重的是归,快排注重的是递

先看模板

void merge_sort(int q[], int l, int r)
{if (l >= r) return;int mid = l + r >> 1;merge_sort(q, l, mid);merge_sort(q, mid + 1, r);int k = 0, i = l, j = mid + 1;while (i <= mid && j <= r)if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];else tmp[k ++ ] = q[j ++ ];while (i <= mid) tmp[k ++ ] = q[i ++ ];while (j <= r) tmp[k ++ ] = q[j ++ ];for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}

思路非常简单:就是将数组分为两部分,分别对两部分进行有序化处理,有序化之后通过两两比较将两个数组合并成一个数组,再将其赋值给原数组

leetcode 148.排序链表

148. 排序链表 - 力扣(LeetCode)

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表  

如果要求原地排序的话其实这题并不好想,当排序用在链表这种结构上时,逻辑要求会更高,但是总体思路是不变的:一分为二,分别有序化,最后再两两比较合并链表

参考代码:

class Solution {
private:ListNode* merge(ListNode*l1,ListNode*l2){ListNode*dummy=new ListNode(0);//虚拟节点ListNode*ans=dummy;while(l1&&l2){//排序auto &node=l1->val<l2->val?l1:l2;dummy->next=node;dummy=dummy->next;node=node->next;}//多余的接到后面if(l1)dummy->next=l1;else dummy->next=l2;return ans->next;}
public:ListNode* sortList(ListNode* head,ListNode*tail=nullptr) {if(head==nullptr)return head;if(head->next==tail)//只剩两个节点时{head->next=nullptr;return head;}ListNode*fast=head;ListNode*slow=head;while(fast!=tail&&fast->next!=tail){slow=slow->next;fast=fast->next->next;}ListNode*mid=slow;//找到中间节点,将链表分成两份return merge(sortList(head,mid),sortList(mid,tail));}
};

拓扑排序

拓扑排序:对有向无环图(DAG)中的所有顶点进行线性排序,使得对于任意一对顶点u和v,如果图中存在一条从u指向v的有向边,那么在拓扑序列中u出现在v之前

一句话说就是只有入度为0的点才能被选择,通过删除边(减少入度)实现节点的排序

最经典的拓扑排序就是先修课问题

 leetcode 207. 课程表

207. 课程表 - 力扣(LeetCode)

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程  bi 。

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。

请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

 入度数组数组记录每一门课的入度多少,通过bfs队列实现

参考代码:

class Solution {
public:bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {int hasLearn=0;queue<int>q;vector<int>indegree(numCourses,0);vector<vector<int>>gragh(numCourses);for(int i=0;i<prerequisites.size();i++){indegree[prerequisites[i][0]]++;gragh[prerequisites[i][1]].push_back(prerequisites[i][0]);}//初始化for(int i=0;i<numCourses;i++){if(indegree[i]==0)q.push(i);}//插入入度为0的课while(!q.empty()){int index=q.front();q.pop();hasLearn++;for(auto p:gragh[index])//减少需要当前课的课的入度{--indegree[p];if(indegree[p]==0){q.push(p);}}}return hasLearn==numCourses;}
};

写在末尾

跟着做完这几题可以说是对排序算法入门了,后面要做的就是多练习多总结

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

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

相关文章

行业首发 | MS08067-SecGPT(送邀请码)

一、简介 MS08067-SecGPT基于LLM大模型技术专门为网络安全领域设计的智能助手&#xff0c;集问答、分析、工具为一体的对话式安全专家&#xff0c;支持可以创建多会话问答。目的是辅助用户完成网络安全相关的工作&#xff0c;学员通过问答方式体验到SecGPT所具备的威胁情报分…

flume使用实例

1、监听端口a1.sources.r1.type netcat 配置文件nc-flume-console.conf # Name the components on this agent a1 表示jvm进程名 a1.sources r1 a1.sinks k1 a1.channels c1 # Describe/configure the source a1.sources.r1.type netcat a1.sources.r1.bind node…

VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION--论文笔记

论文笔记 论文来源 Very Deep Convolutional Networks for Large-Scale Image Recognition 代码来源 还没上传 数据集 这里采用的是猫狗数据集 还没上传 1论文摘要的翻译 在这项工作中&#xff0c;我们研究了卷积网络深度对其在大规模图像识别设置中的准确性的影响。我…

防火墙技术基础篇:解析入侵检测与预防系统(IDPS)功能

防火墙技术基础篇&#xff1a;解析入侵检测与预防系统&#xff08;IDPS&#xff09;功能 入侵检测与预防系统&#xff08;Intrusion Detection and Prevention Systems, IDPS&#xff09;作为防火墙技术的核心组成部分&#xff0c;扮演着保护网络安全的关键角色。本文将全面讲…

OSPF状态机及网络接口类型

、OSPF 状态机 Down一旦接收到hello 包进人下一个状态机 Init 初始化接收到的hello 包中&#xff0c;若存在本地的 RID&#xff0c;进入下一状态 2way 双向通讯--邻居关系建立的标志 条件匹配:点到点网络直接进入下一个状态机 MA 网络将进行 DR/BDR 选举(40S) 非 DR…

哪个网盘最适合个人文件长期储存?用派盘最好

派盘是一款面向个人和企业的本地云存储解决方案,专为长期文件存储而设计。这种存储方式利用了本地硬盘的存储容量,通过“云化”的方式,可以将本地硬盘变成云存储空间。它具有强大的数据保护功能,确保了数据的私密性和安全性。 派盘的主要特点 高效的存取速度:由于使用本地…

这种电脑原来这么耗电……震惊了粉丝小姐姐

前言 在今年1月份的时候&#xff0c;一位来自重庆的小姐姐加了小白&#xff0c;咨询电脑的问题&#xff1a; 哦豁&#xff0c;这个电脑看着确实闪闪发光&#xff0c;是真的很漂亮&#xff5e;&#xff08;嗯&#xff0c;小姐姐也很漂亮&#xff09; 电脑无法开机&#xff0c;按…

什么是流量削峰?如何解决秒杀等业务的削峰场景

文章推荐 1 作为程序员&#xff0c;开发用过最好用的AI工具有哪些&#xff1f; 2 Github Copilot正版的激活成功&#xff0c;终于可以chat了 3 idea,pycharm等的ai assistant已成功激活 4 新手如何拿捏 Github Copilot AI助手&#xff0c;帮助你提高写代码效率 5 Jetbrains的a…

数字驱动,教育先行——低代码揭秘教育机构管理数字化转型

数字化时代为教育带来了许多变革和挑战&#xff0c;同时也为教育创新提供了无限可能。数字化转型可以帮助教育机构应对这些变革和挑战&#xff0c;提高教育效率和质量&#xff0c;满足学生个性化需求&#xff0c;优化教育管理和服务&#xff0c;并提高教育机构的竞争力。 并且…

docker 安装 yapi

文章目录 docker 安装 yapi一、拉取镜像二、创建目录三、添加配置文件四、初始化数据库表五、启动 yapi六、测试以及修改默认密码 没有 MongDB 的可以先看这个教程&#xff1a;MongDB安装教程 docker 安装 yapi 版本&#xff1a; 1.9.5 一、拉取镜像 docker pull yapipro/y…

以及Spring中为什么会出现IOC容器?@Autowired和@Resource注解?

以及Spring中为什么会出现IOC容器&#xff1f;Autowired和Resource注解&#xff1f; IOC容器发展史 没有IOC容器之前 首先说一下在Spring之前&#xff0c;我们的程序里面是没有IOC容器的&#xff0c;这个时候我们如果想要得到一个事先已经定义的对象该怎么得到呢&#xff1f;…

131. 面试中关于架构设计都需要了解哪些内容?

文章目录 一、社区系统架构组件概览1. 系统拆分2. CDN、Nginx静态缓存、JVM本地缓存3. Redis缓存4. MQ5. 分库分表6. 读写分离7. ElasticSearch 二、商城系统-亿级商品如何存储三、对账系统-分布式事务一致性四、统计系统-海量计数六、系统设计 - 微软1、需求收集2、顶层设计3、…

【Django】从零开始学Django(持续更新中)

PyCharm的版本必须为专业版&#xff0c;社区版不具备Web开发功能的。 一. Django建站基础 Django采用MTV的框架模式&#xff0c;即模型(Model)、模板(Template)和视图(Views)&#xff0c;三者之间各自负责不同的职责。 ●模型&#xff1a;数据存取层&#xff0c;处理与数据相关…

信号:MSK调制和GMSK调制

目录 一、MSK信号 1. MSK信号的第k个码元 2.MSK信号的频率间隔 3.MSK信号的相位连续性 3.1 相位路径 3.2初始相位ψk 4.MSK信号的产生 原理框图 5.MSK信号的频谱图 二、高斯最小频移键控(GMSK) 1.频率响应 2.GMSK调制产生方式 2.1 高斯滤波器法 2.2 正交调制器法…

海外私人IP和原生IP有什么区别,谁更有优势?

一、什么是海外私人IP&#xff1f;什么是原生IP&#xff1f; 1、海外私人IP&#xff1a; 海外私人IP是由专门的服务提供商提供的IP地址&#xff0c;这些IP地址通常与特定地理位置或国家相关联。这些IP地址独享私人而不用与其他用户共享。海外私人IP广泛应用与跨境电商中&#x…

【Qt】修改QToolButton图标颜色

1. 目的 修改QToolButton的图标颜色&#xff0c;单一颜色&#xff0c;效果类似于Qt Creator左边选项卡。 2. 代码 QIcon MainWindow::setIconColor(QIcon icon, QColor color) {QPixmap pixmap icon.pixmap(QSize(64,64));QPainter painter(&pixmap);painter.setCompo…

汇编:函数以及函数参数传递

汇编语言中的函数&#xff08;或过程&#xff09;是指一段可以被调用和执行的代码块&#xff1b;它们用于组织和重用代码&#xff0c;并使程序结构更加清晰&#xff1b;由于汇编语言没有高层次语言的语法糖&#xff0c;编写和调用函数涉及直接的堆栈操作和寄存器管理&#xff1…

多项式重构的平滑和法线估计-------PCL

多项式重构的平滑和法线估计 /// <summary> /// 多项式重构的平滑和法线估计 /// </summary> /// <param name"cloud"></param> /// <returns>输出一个包含平滑后的点云数据以及相应法线信息的数据结构</returns> pcl::PointCl…

28v电源 28V电源系统 28v航空电源系统概述

28V电源是指一种工作电压为28V的直流电源系统&#xff0c;主要用于航空电子、航天、J事和高端工业应用中。它通常用于为复杂的电子设备和系统供电&#xff0c;如飞机上的导航、通信、控制面板、计算机系统等。这些设备需要稳定的电压输入&#xff0c;而28V电压既能够保证电力供…

第12周作业--HLS入门

目录 一、HLS入门 二、HLS入门程序编程 创建项目 1、点击Vivado HLS 中的Create New Project 2、设置项目名 3、加入文件 4、仿真 3、综合 一、HLS入门 1. HLS是什么&#xff1f;与VHDL/Verilog编程技术有什么关系? HLS&#xff08;High-Level Synthesis&#xff0c…