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

目录

快速排序

堆排序

桶排序

归并排序

拓扑排序

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

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

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


快速排序

所谓快速排序,简单说就是枚举出一个中心点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;
        
    }
};

写在末尾

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

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

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

相关文章

创意无限,设计所需——Affinity Designer for Mac/win强大登场

在当今数字设计领域&#xff0c;寻找一款功能强大、操作简便的矢量图设计软件并不容易。然而&#xff0c;Affinity Designer 凭借其出色的性能和令人惊艳的功能&#xff0c;在众多设计师中脱颖而出&#xff0c;成为了首选软件之一。今天&#xff0c;让我们一起来探索 Affinity …

【深度学习】与【PyTorch实战】

目录 一、深度学习基础 1.1 神经网络简介 1.2 激活函数 1.3 损失函数 1.4 优化算法 二、PyTorch基础 2.1 PyTorch简介 2.2 张量操作 2.3 构建神经网络 2.4训练模型 2.5 模型评估 三、PyTorch实战 3.1 数据加载与预处理 3.2 模型定义与训练 3.3 模型评估与调优 3…

618购物节快递量激增,EasyCVR视频智能分析助力快递网点智能升级

随着网络618购物节的到来&#xff0c;物流仓储与快递行业也迎来业务量暴增的情况。驿站网点和快递门店作为物流体系的重要组成部分&#xff0c;其安全性和运营效率日益受到关注。为了提升这些场所的安全防范能力和服务水平&#xff0c;实施视频智能监控方案显得尤为重要。 一、…

领券拿外卖返利红包,最低0元吃外卖

小蚕荟是利用本地资源和自媒体优势构建的“本地生活服务”平台&#xff0c;总部位于杭州&#xff0c;旨在为用户提供热门的吃喝玩乐本地生活服务类产品。布局已覆盖杭州、南京、上海等一二线城市。 小蚕荟是一款专为用户吃外卖省钱的生活工具&#xff0c;单单可返利15元起&…

【教学类-58-03】黑白三角拼图03(4*4宫格)总数算不出+随机抽取10张

背景需求&#xff1a; 【教学类-58-01】黑白三角拼图01&#xff08;2*2宫格&#xff09;256种-CSDN博客文章浏览阅读318次&#xff0c;点赞10次&#xff0c;收藏12次。【教学类-58-01】黑白三角拼图01&#xff08;2*2宫格&#xff09;256种https://blog.csdn.net/reasonsummer/…

数组-求和为k的连续子数组

一、题目描述 二、题目思路 这里注意&#xff1a;题目要求时间、空间复杂度都为O(n)&#xff0c;所以不能直接通过双层循环来暴力解(时间复杂度为O&#xff08;n&#xff09;)&#xff0c;可以使用Map实现。 1. 遍历数组计算sum(i)&#xff0c;Map记录sum值第一次出现的位置&…

STM32 MAP文件结合固件文件分析

文章目录 加载域的结束地址并不是固件的结束地址&#xff1f;ROM中执行域的描述RAM中执行域的描述问题分析 中断向量表在固件中的存储位置代码段在固件中的位置只读数据Regin$$Table RW Data段其中的内部机理 总结 MAP 文件分析可以参考之前的文章 程序代码在未运行时在存储器…

LeetCode刷题之HOT100之多数元素

2024/5/21 起床走到阳台&#xff0c;外面绵柔细雨&#xff0c;手探出去&#xff0c;似乎感受不到。刚到实验室&#xff0c;窗外声音放大&#xff0c;雨大了。昨天的两题任务中断了&#xff0c;由于下雨加晚上有课。这样似乎也好&#xff0c;不让我有一种被强迫的感觉&#xff0…

SpringCloud Alibaba Nacos分类配置--多方案配置隔离

文章目录 Nacos 分类配置(实现配置隔离)1.DataID 方案需求分析/图解配置实现测试 2.Group 方案需求分析/图解配置实现修改application.yml修改bootstrap.yml测试 3.Namespace 方案需求分析/图解配置实现修改application.yml修改bootstrap.yml测试 Namespace/Group/Data ID 关系…

基于springboot+vue+Mysql的逍遥大药房管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

EfficientSAM分割对象后求其中图像中的高

1 分割对象 EfficientSAM https://github.com/yformer/EfficientSAM 2 计算在图像中最高点即y值最小点 import os import cv2def read_images(folder_path):image_files [f for f in os.listdir(folder_path) iff.endswith(".jpg") or f.endswith(".png&quo…

文心智能体-恋爱专家

⭐简单说两句⭐ ✨ 正在努力的小叮当~ &#x1f496; 超级爱分享&#xff0c;分享各种有趣干货&#xff01; &#x1f469;‍&#x1f4bb; 提供&#xff1a;模拟面试 | 简历诊断 | 独家简历模板 &#x1f308; 感谢关注&#xff0c;关注了你就是我的超级粉丝啦&#xff01; &a…

邮件系统数据面临的安全问题及解决方法

随着电子邮件的普及&#xff0c;邮件系统已成为企业、学校、个人等用户之间进行信息交流的重要工具。然而&#xff0c;随着数据量的增加和用户对邮件系统的依赖&#xff0c;邮件系统数据安全问题也逐渐凸显。下面U-Mail技术张工就给大家讲解一下邮件系统数据面临的主要安全问题…

CCF-GESP 等级考试 2023年9月认证C++四级真题

2023年9月 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 第 1 题 ⼈们所使⽤的⼿机上安装的App通常指的是&#xff08; &#xff09;。 A. ⼀款操作系统B. ⼀款应⽤软件C. ⼀种通话设备D. 以上都不对 第 2 题 下列流程图的输出结果是&#xff1f;( ) A. 9B.…

【30天精通Prometheus:一站式监控实战指南】第4天:node_exporter从入门到实战:安装、配置详解与生产环境搭建指南,超详细

亲爱的读者们&#x1f44b;   欢迎加入【30天精通Prometheus】专栏&#xff01;&#x1f4da; 在这里&#xff0c;我们将探索Prometheus的强大功能&#xff0c;并将其应用于实际监控中。这个专栏都将为你提供宝贵的实战经验。&#x1f680;   Prometheus是云原生和DevOps的…

搜索插入位置 ---- 二分查找

题目链接 题目: 分析: 因为数排序数组, 所以具有"二段性", 可以使用二分查找题目中, 我们如果找到目标值 , 则返回下标, 如果没找到目标值, 应该返回的是>target的第一个位置, 所以应该将数组分成< target 和 > target当<target时, 应该移动left, left…

3DMax

先转换为可编辑多边形 按“1”选择为点&#xff0c;点击目标焊接&#xff08;CtrlShiftw&#xff09;&#xff0c;然后点击一个顶点拉到另一个定点上&#xff1b; 选择一个面&#xff0c;点击塌陷&#xff08;CtrlAltC&#xff09;&#xff0c;四点合并为一个点&#xff1b; …

《艺术大观》知网艺术刊:可加急, 出刊上网快

《艺术大观》 《艺术大观》征文通知 《艺术大观》期刊诚邀学者、艺术家和文化工作者积极投稿&#xff0c;共同探索艺术领域的前沿问题&#xff0c;促进学术交流和艺术创作的发展。我们欢迎各类艺术形式的研究与评论&#xff0c;包括但不限于绘画、雕塑、音乐、舞蹈、戏剧、电…

代码随想录算法训练营第三十四天 | 理论基础、455.分发饼干、376、摆动序列、53.最大子序和

目录 理论基础 455.分发饼干 思路 代码 376.摆动序列 思路 代码 53.最大子序和 思路 代码 理论基础 代码随想录 455.分发饼干 代码随想录 思路 可以是大饼干优先满足大胃口&#xff0c;也可以是小饼干优先满足小胃口。 代码 class Solution:def findContentChildre…

springsecurity入门登录授权

①我们需要自定义登陆接口&#xff0c;也就是在controller目录新建LoginController类&#xff0c;在controller方法里面去调用service接口&#xff0c;在service接口实现AuthenticationManager去进行用户的认证&#xff0c;注意&#xff0c;我们定义的controller方法要让Spring…