【基础算法总结】分治—快排

news2024/10/6 0:53:35

分治—快排

  • 1.分治
  • 2.颜色分类
  • 3.排序数组
  • 4.数组中的第K个最大元素
  • 5.库存管理 III

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.分治

分治思想就如同它的名字一样:分而治之

将一个大问题划分成若干个相同或者相识的子问题。然后在将子问题在划分成若干个相同或者相识的子问题,直到划分到不能在划分。然后解决子问题,子问题都解决完了,大问题也就被解决完了。快速排序和归并排序就用的分治思想。

在这里插入图片描述

以前我们学快速排序是在数组中选择一个基准元素,然后将数组分成左右两个区间,左区间比基准元素小,右区间比基准元素大。然后递归的去左区间和有区间排序,这种做法是将数组分成了两份。但是对于重复元素非常多的数组即使使用快速排序也会超时。因此这里我们学习新的划分方法,将数组划分成三份。

还是选一个基准元素将数组划分成三份,左区间元素都比基准元素小。中间区间元素和基准元素相同,右区间元素都比基准元素大。因为中间都是等于key的一定就是在最终位置,所以接下来递归还是只考虑左区间和右区间。

在这里插入图片描述

2.颜色分类

题目链接:75. 颜色分类

题目分析:

在这里插入图片描述
说起来这道题并不是真正的分治快速排序,而是把数组按照一定规则划分成三块的。当把这道题解决后,快排写的就非常简单。

这道题就相当于选择一个基准元素1,把小于1的放左边,大于1的放右边,等于1的放中间。

算法原理:

双指针可以将数组分成两块,具体怎么分,双指针系列第一道题移动零。这里我们需要三个指针将数组分成三块!

每个指针的作用:
i指针:遍历整个数组
left:标记 0 区域的最右侧
right:标记 2 区域的最左侧

在这里插入图片描述

三个指针将数组分成4份:
[0,left] :全都是0
[left+1,i-1]:全都是1
[i,right-1]:待扫描的元素
[rigth+1,n-1]:全都是2

在这里插入图片描述

接下来就讨论nums[i]是0或1或2应该怎么办?

nums[i]为0的时候,要把0加入到左边区域,left指向的是 0 最右侧区域,此时left+1,然后将 i 位置和 left+1 元素交换,然后i+1。

在这里插入图片描述

还有一种极端情况 i 就在 left+1的为位置,并且正好是0。但是这种处理方法和上面一样。

在这里插入图片描述

nums[i]为1的时候,i 指针往后走就行了

在这里插入图片描述

nums[i]为2的时候,我们要将2加入到右边区间,也就是加入到 right - 1 的位置。交换 i 位置和 right -1 位置的元素。但是此时需要注意的是 交换给 i 位置的元素是待扫描的元素,因此 i 指针不能往后走!

在这里插入图片描述

class Solution {
public:
    void sortColors(vector<int>& nums) {

        int n = nums.size();
        int i = 0, left = -1, right = n;
        while(i < right)
        {
            if(nums[i] == 0)
                swap(nums[++left], nums[i++]);
            else if(nums[i] == 2)
                swap(nums[--right], nums[i]);
            else
                ++i;
        }
    
    }
};

3.排序数组

题目链接:912. 排序数组

题目描述:

在这里插入图片描述
算法原理:

在数组中选择一个基准元素key,根据key将数组分成左右区间,左区间元素小于等于key,右区间元素大于key。这个key就处于排序的最终位置。然后在将左区间排排序,右区间排排序,重复上述过程。整体就有序了。时间复杂度(nlogn)

但是如果数组都是重复元素,此时选择基准元素间将数组分成左右两区间就不行了。时间复杂度退化成O(n^2)

在这里插入图片描述

所以我们学习一个更优的做法,将 数组分三块 思想来实现快速排序

我们先选一个基准元素key,将数组分成三块。左区间小于key,中间区间等于key,右区间大于key。中间区间已经在排序后的最终位置,所以只用去去左区间排序,右区间排序。重复上述过程,整体就有序了。

数组如何分三块和颜色分类一模一样。定义一个i 指针 扫描数组,left指针 指向左区间小于key的最右侧, right指针 指向右区间大于key的最左侧。然后分情况讨论就好了。

在这里插入图片描述

即使数组全部都是重复元素,我们经过一次数组划分,整个数组都是中间区间,左右区间不存在,也不用在递归下去了,直接结束。时间复杂度O(n)

在这里插入图片描述

优化:用随机的方式选择基准元素
之前常用的三数取中,还是不够随机。要想时间复杂度逼近O(nlogn)就要用随机的方式选择基准元素。

随机选择就是让数组中每个元素被选择的概率相同,然后返回被选择的元素。

使用产生随机数的函数 rand(),让生成的随机数%这个区间的长度,然后加上left,这是在这个区间内的随机数的下标,然后返回对应下标的元素。
在这里插入图片描述

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        srand((unsigned int)time(nullptr));
        QuickSort(nums,0,nums.size()-1);
        return nums;

    }


    void QuickSort(vector<int>& nums, int l, int r)
    {
        if(l >= r) return;
        //数组分三块
        int key = getRandom(nums, l ,r);
        int i = l, left = l - 1, right = r + 1;
        while(i < right)
        {
            if(nums[i] < key)
                swap(nums[++left], nums[i++]);
            else if(nums[i] == key)
                ++i;
            else
                swap(nums[--right], nums[i]);
        }

        QuickSort(nums, l , left);
        QuickSort(nums, right, r);

    }

    int getRandom(vector<int>& nums, int left, int right)
    {
        return nums[rand() % (right - left + 1) + left];
    }

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

题目链接:215. 数组中的第K个最大元素

题目分析:

在这里插入图片描述
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。其实就是一个TopK问题。

TopK问题四种问法:

第 k 个最大的元素
第 k 个最小的元素
前 k 个最大的元素
前 k 个最小的元素

可以使用堆排序, 时间复杂度O(nlogn)

还有一种就是快速选择算法,快速选择算法是基于快排实现的。时间复杂度O(n)。

算法原理:

一定要把数组分三块+随机选择基准元素掌握,才能懂这道题。

核心操作还是选择一个基准元素key,将数组分成< key , = key ,> key 三块区域。在这道题中我们是要找到第K大元素,这个第K大元素有可能落在三个区域中的任何一个区域。如果有一种方法能够确定第K大元素能够落在那个区域,那另外两个区域就不用考虑了。仅需在确定的区域里面递归找。所以说它是比快排更快的一种算法。
在这里插入图片描述

接下来重点就是如何确定第K大元素落在左边区域、中间区域、还是右边区域。
此时我们先统计出每个区域中元素的个数,假设左边区域元素个数为 a,中间区域元素个数为 b,右边区域元素个数为 c。
在这里插入图片描述

此时就分三种情况讨论:

因为求第K大,所以可以先考虑右边区域,因为右边区域都是大元素聚集地,第K大最有可能在右边区域。

  1. 第一种情况:如果第K大是落在右边区域,此时 c >= K,因为c表示大元素有多少个,而K表示找第K大的元素。如果 c >= K ,那第K大一定是落在右边区域。此时我们仅需到[right,r],找第K大
  2. 第二种情况:如果到了第二情况那第一种情况一定是不成立的。如果第K大是落在中间区域,此时 b + c >= K,又因为第一种情况不满足,所有第K大一定是落在中间区域。此时就找到了也不用递归了。直接返回就可以
  3. 第三种情况:第一、第二种情况全部不成立,第K大一定落在左边区域,去**[l,left]找**,但是此时并不是去找第K大了,本来是想在整个区间找第K大,但是第K大元素确定不在中间区域和右边区域,中间和右边区域都要被抛弃,此时去找左边区间找的是第 K - b - c 大的元素

在这里插入图片描述

class Solution {
public:

    int findKthLargest(vector<int>& nums, int k) {

        srand((unsigned int)time(nullptr));
        return QuickSort(nums,0,nums.size()-1,k);
    }


    int QuickSort(vector<int>& nums, int l, int r, int k)
    {
    	//不用考虑区间不存在的情况,因为下面的判断K落在那个区域,只要满足进入的一定是有的
        if(l == r) return nums[l];

        // 1.随机选择基准元素
        int key = GetRandom(nums, l, r);

        // 2.根据基准元素将数组分三块
        int i = l, left = l - 1, right = r + 1;
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) ++i;
            else swap(nums[--right], nums[i]);
        }

        //3.计算每个区间都有多少元素,然后选择区间递归
        int b = right - 1 - left , c = r - right + 1; 
        if(c >= k) return QuickSort(nums, right, r ,k);
        else if(b + c >= k) return key;
        else return QuickSort(nums, l, left, k - b - c);

    }

    int GetRandom(vector<int>& nums, int left, int right)
    {
        return nums[rand() % (right - left + 1) + left];
    }



    // int findKthLargest(vector<int>& nums, int k) {
    //     //前k个建小堆
    //     priority_queue<int,vector<int>,greater<int>> pq(nums.begin(),nums.begin()+k);

    //     //N-k与堆顶元素比较,大于堆顶就入堆,再次调整成小堆
    //     for(size_t i=k;i<nums.size();++i)
    //     {
    //         if(nums[i]>pq.top())
    //         {
    //             pq.pop();
    //             pq.push(nums[i]);
    //         }
    //     }

    //     return pq.top();
     
    // }
};

5.库存管理 III

题目链接:LCR 159. 库存管理 III

题目分析:

在这里插入图片描述

实际上这也是一个TopK问题,让找前K个最小元素。注意返回结果并不考虑顺序问题。

算法原理:

解法一:排序 ,时间复杂度O(nlogn)
解法二:堆 ,时间复杂度O(nlogk)
解法三:快速选择算法,时间复杂度O(n)

数组分三块+随机选择基准元素。
选择一个基准元素key,将数组分成< key , = key ,> key 三块区域。找前K个最小的元素,落在三个区域中任何一个。统计除每个区域元素个数,然后选择去那个区域找。

分三种情况:
因为前K下最小元素最有可能出现在左边区域,因此先判断左边区域

  1. a >= K ,去[l,left] 找前K个最小元素
  2. b + a >= K ,直接返回
  3. 1、2都不成立,去[right,r] 找 K - a - b 个最小元素

在这里插入图片描述

class Solution {
public:
    vector<int> inventoryManagement(vector<int>& nums, int k) {

        srand((unsigned int)time(nullptr));
        QuickSort(nums, 0, nums.size() - 1, k);
        return {nums.begin(),nums.begin() + k};
    }

    void QuickSort(vector<int>& nums, int l, int r, int k)
    {
        if(l >= r) return;

        // 1. 随机选择基准元素
        int key = GetRandom(nums, l, r);
        // 2. 数组分三块
        int i = l, left = l - 1, right = r + 1;
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) ++i;
            else swap(nums[--right], nums[i]);
        }

        // 3. 分情况讨论
        int a = left - l + 1, b = right - left - 1;
        if(a >= k) QuickSort(nums, l, left, k);
        else if(a + b >= k) return;
        else QuickSort(nums, right, r, k - a - b);
    }

    int GetRandom(vector<int>& nums, int left, int right)
    {
        return nums[rand() % (right - left + 1) + left];
    }
};

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

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

相关文章

Node.js安装及配置

文章目录 1.安装Node.js2.创建目录3.配置环境变量4.配置全局安装路径和缓存路径(可选)配置Webstorm 1.安装Node.js https://registry.npmmirror.com/binary.html?pathnode 推荐安装18.x版本 2.创建目录 下载解压后进入目录&#xff0c;创建node_global和node_cache两个空文…

在嵌入式商用里面哪款RTOS(实时操作系统)比较多人用?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 传统的RTOS和嵌入式Linu…

【氮化硼】 h-BN薄膜的控制辐射损伤和边缘结构

摘要: 本文展示了通过化学剥离法合成的六方氮化硼(h-BN)膜在80 kV电子束辐照下表现出比石墨烯更高的抗辐射损伤能力。研究表明,即使在长时间电子束辐照下,单层h-BN也不会形成空位缺陷或发生非晶化。实验观察到在h-BN薄膜中,锯齿形边缘结构占主导地位,且这些边缘主要由氮…

6个操作简单又好用的实用办公工具

分享6个操作简单又好用的实用办公工具&#xff0c;手机和电脑上的都有&#xff0c;好好使用可以让工作效率翻倍&#xff01; 1.方方格子 一个大型的的【Excel工具箱】&#xff0c;支持32位和64位Office&#xff0c;可直接作为插件使用&#xff0c;功能覆盖非常全面&#xff0c…

MySQL实战-4 | 深入浅出索引(上)(下)

什么是数据库索引&#xff0c;索引又是如何工作的呢&#xff1f; 一句话简单来说&#xff0c;索引的出现其实就是为了提高数据查询的效率&#xff0c;就像书的目录一样。一本 500 页的书&#xff0c;如果你想快速找到其中的某一个知识点&#xff0c;在不借助目录的情况下&…

MySQL:设计数据库与操作

设计数据库 1. 数据建模1.1 概念模型1.2 逻辑模型1.3 实体模型主键外键外键约束 2. 标准化2.1 第一范式2.2 链接表2.3 第二范式2.4 第三范式 3. 数据库模型修改3.1 模型的正向工程3.2 同步数据库模型3.3 模型的逆向工程3.4 实际应用建议 4. 数据库实体模型4.1 创建和删除数据库…

学习笔记(linux高级编程)10

IPC 进程间通信 interprocess communicate 三大类&#xff1a; 1、古老的通信方式 无名管道 有名管道 信号 2、IPC对象通信 system v BSD suse fedora kernel.org 消息队列(用的相对少&#xff0c;这里不讨论) 共享内存 信号量集 3、socket通信 网络通信 特…

【MySQL】mysql访问

mysql访问 1.引入MySQL 客户端库2.C/C 进行增删改3.查询的处理细节4.图形化界面访问数据库4.1下载MYSQL Workbench4.2MYSQL Workbench远程连接数据库 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&a…

基于CNN的股票预测方法【卷积神经网络】

基于机器学习方法的股票预测系列文章目录 一、基于强化学习DQN的股票预测【股票交易】 二、基于CNN的股票预测方法【卷积神经网络】 文章目录 基于机器学习方法的股票预测系列文章目录一、CNN建模原理二、模型搭建三、模型参数的选择&#xff08;1&#xff09;探究window_size…

8619 公约公倍

这个问题可以通过计算最大公约数 (GCD) 和最小公倍数 (LCM) 来解决。我们需要找到一个整数&#xff0c;它是 a, b, c 的 GCD 的倍数&#xff0c;同时也是 d, e, f 的 LCM 的约数。 以下是解决这个问题的步骤&#xff1a; 1. 计算 a, b, c 的最大公约数。 2. 计算 d, e, f 的最…

SAP MM模块的ATP检查

前面几篇文章都演示和说明ATP的一些设置和操作&#xff0c;通常情况下ATP的检查PP模块&#xff0c;SD模块用的相对来说是比较多的&#xff0c;但是实际上MM模块也会遵循ATP的可用性的检查规则。 当我们在做311、301等移动类型时&#xff0c;系统会根据相应的可用性检查规则&am…

大模型应用开发实战基础

大模型应用开发实战基础 1. 背景 大模型如日中天&#xff0c;各行各业都受它影响&#xff0c;但是作为程序员&#xff0c;除了让它翻译代码不知道用它干什么&#xff0c;就像是拿着锤子的木匠&#xff0c;找不到钉子在哪。一边听着别人说2024是AI元年&#xff0c;一边又不知所…

基于X86+FPGA的精密加工检测设备解决方案

应用场景 随着我国高新技术的发展和国防现代化发展&#xff0c;航空、航天等领域需 要的大型光电子器件&#xff0c;微型电子机械、 光 电信息等领域需要的微型器件&#xff0c;还有一些复杂零件的加工需求日益增加&#xff0c;这些都需要借助精密甚至超精密的加工检测设备 客…

Asp.NET identity以及Owin

》》》Identity是集成到Owin框架中中 ● Microsoft.AspNet.Identity.Core&#xff1a;Identity的核心类库&#xff0c;实现了身份验证的核心功能&#xff0c;并提供了拓展接口。● Microsoft.AspNet.Identity.EntityFramework&#xff1a;Identity数据持久化的EF实现。   ● …

强化学习的数学原理:最优贝尔曼公式

大纲 贝尔曼最优公式是贝尔曼公式的一个特殊情况&#xff0c;但其也非常重要。 本节课很重要的两个概念和一个工具&#xff1a; 工具不用多说&#xff0c;就是贝尔曼最优公式&#xff0c;概念则是 optimal state value&#xff08;最优状态价值&#xff09; 和 optimal polic…

文件中各个函数返回----EOF----NULL---非零值>>>>>区分

fopen 返回值&#xff1a;操作正常返回文件指针&#xff0c; 失败返回NULL fclose 返回值&#xff1a;操作正常返回 0 失败返回EOF 不关闭文件会丢失 fgetc 返回值&#xff1a; 成功读入字符 失败返回EOF fputc 返回值&#xff1a;成功输出的字符 失败返回EOF fgets …

香橙派OrangePi AIpro初体验:当小白拿到一块开发板第一时间会做什么?

文章目录 香橙派OrangePi AIpro初体验&#xff1a;当小白拿到一块高性能AI开发板第一时间会做什么前言一、香橙派OrangePi AIpro概述1.简介2.引脚图开箱图片 二、使用体验1.基础操作2.软件工具分析 三、香橙派OrangePi AIpro.测试Demo1.测试Demo1&#xff1a;录音和播音(USB接口…

MySQL的并发控制、事务、日志

目录 一.并发控制 1.锁机制 2.加锁与释放锁 二.事务&#xff08;transactions&#xff09; 1.事物的概念 2.ACID特性 3.事务隔离级别 三.日志 1.事务日志 2.错误日志 3.通用日志 4.慢查询日志 5.二进制日志 备份 一.并发控制 在 MySQL 中&#xff0c;并发控制是确…

pandas数据分析(5)

pandas使用Numpy的np.nan代表缺失数据&#xff0c;显示为NaN。NaN是浮点数标准中地Not-a-Number。对于时间戳&#xff0c;则使用pd.NaT&#xff0c;而文本使用的是None。 首先构造一组数据&#xff1a; 使用None或者np.nan来表示缺失的值&#xff1a; 清理DataFrame时&#xf…

ubuntu apt命令 出现红色弹框 Daemons using outdated libraries

1. 弹框没截图&#xff0c;是因为ubuntu22.04一个新特性导致的&#xff0c;由 needrestart 命令触发&#xff0c;默认情况是交互性质的&#xff0c;也就是会中断在这里需要手动要处理提示。 2. 修改/etc/needrestart/needrestart.conf 文件&#xff0c;将 #$nrconf{restart} …