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

news2024/10/6 0:32:29

目录

快速排序

堆排序

桶排序

归并排序

拓扑排序

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

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

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


快速排序

所谓快速排序,简单说就是枚举出一个中心点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.coloradmin.cn/o/1688631.html

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

相关文章

文心智能体应用示例:职场反PUA专家的诞生

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

LangChain llamaindex

LangChain 参考&#xff1a; 全流程 | Windows 系统本地部署开源模型阿里通义千问 QWEN 1.5&#xff0c;结合 LangChain-Chatchat 框架和向量数据库 FAISS、Milvus - 知乎

蓝桥杯-数三角(ac代码时间复杂度分析)

问题描述 小明在二维坐标系中放置了 ( n ) 个点&#xff0c;他想在其中选出一个包含三个点的子集&#xff0c;这三个点能组成三角形。然而这样的方案太多了&#xff0c;他决定只选择那些可以组成等腰三角形的方案。请帮他计算出一共有多少种选法可以组成等腰三角形&#xff1f…

Convolutional Occupancy Networks【ECCV2020】

论文&#xff1a;https://arxiv.org/pdf/2003.04618 代码&#xff1a;GitHub - autonomousvision/convolutional_occupancy_networks: [ECCV20] Convolutional Occupancy Networks 图 1&#xff1a;卷积占据网络。传统的隐式模型 (a) 由于其全连接网络结构&#xff0c;表现能力…

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

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

4月空调行业线上市场销售数据分析

随着生活品质的提升&#xff0c;消费者对家用空调的诉求不仅仅满足于基本制冷制热功能&#xff0c;而是在环保节能、功能升维、舒适送风、智能科技、焕新设计等多维度提出需求。这种多样化的需求推动了空调产品的创新和升级&#xff0c;这不仅提高了空调的市场竞争力&#xff0…

Linux程序开发(六):进程编程和系统日志守护进程

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

“闻起来有股答辩的味道”,答辩到底是什么味?

“闻起来有股答辩的味道”&#xff0c;答辩到底是什么味&#xff1f; 一位名叫“小鸡全家桶”的作者虚构了这样一个学校故事&#xff0c;故事说&#xff0c;由于学生的考试试卷印刷得特别模糊&#xff0c;导致里面的插图根本看不清&#xff0c;学生感到懵逼&#xff0c;监考老…

oracle主机虚拟内存不足导致实例宕机

检查alert日志&#xff0c;发现pga资源分配时报内存不足&#xff1a; 由于pga内存不足&#xff0c;而导致的数据库宕机。 查看操作系统日志&#xff1a; 发现在宕机之前&#xff0c;出现了虚拟内存不足的情况。 检查确认设置的虚拟内存大小为50多G&#xff1a; 按道理不该出现…

从零开始:Spring Boot项目中如何集成并使用Infinispan

一、介绍 Infinispan 其实就是一个分布式缓存和数据网格平台&#xff0c;提供了高度可扩展和高性能数据缓存解决方案。Infinispan可以作为本地缓存或分布式缓存使用&#xff0c;支持事务、查询、处理大数据等功能。简单地说&#xff0c;Infinispan 可以理解为是 MySQL 的内存版…

玩转OpenHarmony智能家居:如何实现开发版“碰一碰”设备控制

一、简介 “碰一碰”设备控制&#xff0c;依托NFC短距通信协议&#xff0c;通过碰一碰的交互方式&#xff0c;将OpenAtom OpenHarmony&#xff08;简称“OpenHarmony”&#xff09;标准系统设备和全场景设备连接起来&#xff0c;解决了应用与设备之间接续慢、传输难的问题&…

浅析智能体开发(第二部分):智能体设计模式和软件架构

大语言模型&#xff08;LLM&#xff09;驱动的智能体&#xff08;AI Agent&#xff09;展现出许多传统软件所不具备的特征。不仅与传统软件的设计理念、方法、工具和技术栈有显著的差异&#xff0c;AI原生&#xff08;AI Native&#xff09;的智能体还融入了多种新概念和技术。…

springboot-阿里羚羊 服务端埋点

官方文档 集成Java SDK 手动引入jar包「quickaplus-log-collector-java-sdk-1.0.1-SNAPSHOT.jar」 <dependency><groupId>com.alibaba.lingyang</groupId><artifactId>quickaplus-log-collector-java-sdk</artifactId><version>1.0.1&l…

心识宇宙 x TapData:如何加速落地实时数仓,助力 AI 企业智慧决策

使用 TapData&#xff0c;化繁为简&#xff0c;摆脱手动搭建、维护数据管道的诸多烦扰&#xff0c;轻量代替 OGG、DSG 等同步工具&#xff0c;「CDC 流处理 数据集成」组合拳&#xff0c;加速仓内数据流转&#xff0c;帮助企业将真正具有业务价值的数据作用到实处&#xff0c…

Java(十)---抽象类和接口

文章目录 前言知识回顾1.抽象类1.1.抽象类语法1.2 抽象类特性 2.接口2.1.接口的概念2.2 语法规则2.3 接口使用2.4 接口特性2.5 实现多个接口 3.Object类3.1 获取对象信息3.2.对象比较equals方法 4.接口使用实例4.1.Comparable4.2.Comparator4.3.Cloneable深拷贝和浅拷贝 前言 …

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

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

2024/5/22 学习杂记

为什么功率放大电路在模电中经常提到&#xff1f; 模拟信号&#xff1a;它是连续变化的电信号&#xff0c;它在时间上和幅度上都是连续的&#xff0c;能够代表信息的连续变化。大多数物理量为模拟信号&#xff0c;如&#xff1a;温度、压力、流量… 非电物理量通过传感器变换成…

RabbitMQ 消息队列安装及入门

市面常见消息队列中间件对比 技术名称吞吐量 /IO/并发时效性&#xff08;类似延迟&#xff09;消息到达时间可用性可靠性优势应用场景activemq万级高高高简单易学中小型企业、项目rabbitmq万级极高&#xff08;微秒&#xff09;高极高生态好&#xff08;基本什么语言都支持&am…

告别付费!这款开源软件让你免费看高清电视直播!

文章目录 📖 介绍 📖🏡 演示环境 🏡📝 开源详情 📝🎯 软件介绍🚀 软件特点🎈 获取方式 🎈⚓️ 相关链接 ⚓️📖 介绍 📖 🔮 揭秘一款神奇的软件,让你轻松畅游电视直播的海洋,无需付费,无需繁琐设置,即可畅享海量高清节目!想要知道它是什么吗?跟…

如何改变echo在Linux下的输出颜色

文章目录 问题回答常规输出字体加粗斜体字带下划线闪烁效果 参考 问题 我正在尝试使用 echo 命令在终端中打印文本。 我想把文本打印成红色。我该怎么做&#xff1f; 回答 你可以使用 ANSI escape codes 定义控制输出颜色的变量。 ANSI escape codes是一种用于在文本中设置…