算法笔记(五)——分治

news2024/12/27 19:27:23

文章目录

  • 算法笔记(五)——分治
  • 快排
    • 颜色分类
    • 排序数组
    • 数组中的第K个最大元素
    • 库存管理 III
  • 归并
    • 排序数组
    • 交易逆序对的总数
    • 计算右侧小于当前元素的个数
    • 翻转对

算法笔记(五)——分治

分治算法字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序)…

分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。

步骤

  • 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
  • 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
  • 合并:将各个子问题的解合并为原问题的解

经典的分治算法有二分搜索,归并排序,快速排序,。

快排

颜色分类

题目:颜色分类

在这里插入图片描述
思路

  • 初始化三个指针:
  • i遍历数组;
  • left左侧均为0
  • right右侧均为2
  • 遍历过程中遇到0swap(nums[++left],nums[i++])
  • 遇到1i++,不进行交换
  • 遇到2swap(nums[--right], nums[i])
  • 循环条件i < right

C++代码

class Solution 
{
public:
    void sortColors(vector<int>& nums) 
    {
        for(int i = 0, left = -1, right = nums.size(); i < right; )
        {
            if(nums[i] == 0) 
                swap(nums[++left], nums[i++]);
            else if(nums[i] == 1)
                i++;
            else
                swap(nums[--right], nums[i]);
        }
    }
};

排序数组

题目:排序数组

在这里插入图片描述
思路

  • 我们将数组划分为三块,再来实现快排,将数组划分为三个部分:小于、等于、大于基准值;
  • <key,=key,>key

三路划分:减少重复元素的递归处理(相同元素过多的话,可以减小递归深度)、避免不必要的交换(将相同元素聚集在一起,避免了不必要的交换操作)

C++代码

class Solution 
{
public:
    int getKey(vector<int>& nums, int left, int right)
    {
        return nums[rand() % (right - left + 1) + left];
    }

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

        int key = getKey(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]);
        }

        
        qsort(nums, l, left);
        qsort(nums, right, r);
    }

    vector<int> sortArray(vector<int>& nums) 
    {
        srand(time(NULL));
        qsort(nums, 0, nums.size() - 1);

        return nums;
    }
};

数组中的第K个最大元素

题目:数组中的第K个最大元素

在这里插入图片描述

思路

常规解法,利用堆排,但时间复杂度不为O(N)

快速选择算法(快排)O(N)

  • 三路划分,将数组划分为三块;
  • 大于key的元素个数为c,等于key的元素个数为b,小于key元素个数为a
  • c >= k,则第k大元素在右侧,继续在右侧递归寻找第k大元素;
  • b + c >= k,则直接返回基准元素,即为第k大元素;
  • 若上述均不满足,则第k大元素在左侧,继续在左侧递归寻找第k大元素,此时k = k - b - c

C++代码

class Solution 
{
public:
    // 数组中获得随机值 
    int getKey(vector<int>& nums, int l, int r) 
    {
        return nums[rand() % (r - l + 1) + l];
    }

    int qsort(vector<int>& nums, int l, int r, int k)
    {
        if(l == r) return nums[l];
        // 随机选择基准元素
        int key = getKey(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]);
        }

        int b = right - 1 - (left + 1) + 1; // 等于key的数量
        int c = r - right + 1; // 大于key的数量
        if(c >= k) return qsort(nums, right, r, k);
        else if((b + c) >= k) return key;
        else return qsort(nums, l, left, k - b - c);
    }

    int findKthLargest(vector<int>& nums, int k)             
    {
        srand(time(NULL));
        return qsort(nums, 0, nums.size() - 1, k);
    }
};

库存管理 III

题目:库存管理 III

在这里插入图片描述
思路

和上题想法一致,使用快速选择的算法,使时间复杂度达到O(n)

C++代码


```class Solution 
{
public:
    void qsort(vector<int>& nums, int l, int r, int cnt)
    {
        if(l >= r) return ;

        int key = nums[rand() % (r - l + 1) + l];
        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]);
        }

        int a = left - l + 1;
        int b = right - 1 - (left + 1) + 1;
        if(a >= cnt) qsort(nums, l, left, cnt);
        else if((a + b) >= cnt) return;
        else qsort(nums, right, r, cnt - a - b);
        
    }
    vector<int> inventoryManagement(vector<int>& stock, int cnt) 
    {
        srand(time(NULL));
        qsort(stock, 0, stock.size() - 1, cnt);

        return {stock.begin(), stock.begin() + cnt};
    }
};

归并

排序数组

题目:排序数组

在这里插入图片描述C++代码

class Solution 
{
    // 归并
    vector<int> tmp;
public:
    void mergeSort(vector<int>& nums, int l, int r)
    {
        if(l >= r) return ;

        // 计算中间位置
        int mid = (l + r) >> 1;

        // 对左右两部分进行归并排序
        mergeSort(nums, l, mid);
        mergeSort(nums, mid + 1, r);

        // 归并合并两个有序部分
        int i = l, j = mid + 1, k = 0;
        while(i <= mid && j <= r)
            tmp[k++] = (nums[i] <= nums[j]) ? nums[i++] : nums[j++];

        while(i <= mid) tmp[k++] = nums[i++];
        while(j <= r) tmp[k++] = nums[j++];

        // 拷贝回原数组
        for(int i = l; i <= r; i++)
        {
            nums[i] = tmp[i - l];
        }
    } 
    vector<int> sortArray(vector<int>& nums) 
    {
        tmp.resize(nums.size());
        mergeSort(nums, 0, (int)nums.size() - 1);

        return nums;
    }
};

交易逆序对的总数

题目:交易逆序对的总数

在这里插入图片描述
思路
当我们将两个已排序的子数组合并成一个有序数组时,如果左侧子数组中的某个元素大于右侧子数组中的某个元素,那么左侧子数组中该元素之后的所有元素(包括该元素本身)都将与右侧子数组中的该元素形成逆序对。因此,我们可以通过计算这样的元素对数来统计逆序对的总数

C++代码

class Solution 
{
    int tmp[50010];
public:
    int reversePairs(vector<int>& record) 
    {
        return mergeSort(record, 0, record.size() - 1);
    }
    int mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return 0; 

        int ret = 0;
        // 中间,将数组分为两部分
        int mid = left + right >> 1;
        // [left, mid], [mid + 1, right]

        // 左边个数 + 排序 + 右边个数 + 排序
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);

        // 一左一右个数
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if(nums[cur1] <= nums[cur2])
            {
                tmp[i++] = nums[cur1++];
            }
            else
            {
                ret += mid - cur1 + 1;  // 统计逆序对个数
                tmp[i++] = nums[cur2++];                
            }
        }

        // 处理剩余元素
        while (cur1 <= mid) tmp[i++] = nums[cur1++];
        while (cur2 <= right) tmp[i++] = nums[cur2++];

        // 拷贝回原数组
        for (int i = left; i <= right; ++i)
            nums[i] = tmp[i - left];

        return ret;

    }
};

计算右侧小于当前元素的个数

题目:计算右侧小于当前元素的个数

在这里插入图片描述
思路

这⼀道题的解法与求数组中的逆序对的解法是类似的,记录每⼀个元素的右边有多少个元素⽐⾃⼰⼩

归并排序的过程中,元素的下标是会跟着变化的,因此我们需要⼀个辅助数组,来将数组元素和对应的下标绑定在⼀起归并,也就是再归并元素的时候,顺势将下标也转移到对应的位置上

C++代码

class Solution 
{
    vector<int> ret;
    vector<int> index; // 记录当前元素的元素下标
    int tmpNums[500010];
    int tmpIndex[500010];
public:
    vector<int> countSmaller(vector<int>& nums) 
    {
        int n = nums.size();
        ret.resize(n);  
        index.resize(n);

        // 初始化tmpIndex
        for(int i = 0; i < n; i++)  index[i] = i; 

        mergeSort(nums, 0, n - 1);

        return ret;
    }

    void mergeSort(vector<int>& nums, int left, int right)
    {   
        if(left >= right) return ;

        // 根据中间元素划分区间
        int mid = (left + right) >> 1;
        // [left, mid]、[mid + 1, right]

        // 处理左右两部分
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);

        // 处理一左一右,降序数组
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if(nums[cur1] <= nums[cur2]) 
            {
                tmpNums[i] = nums[cur2];
                tmpIndex[i++] = index[cur2++];          
            }
            else 
            {
                ret[index[cur1]] += right - cur2 + 1;
                tmpNums[i] = nums[cur1];
                tmpIndex[i++] = index[cur1++];  
            }
        }    

        // 处理剩余数组
        while(cur1 <= mid)
        {
            tmpNums[i] = nums[cur1];
            tmpIndex[i++]=index[cur1++];
        }
        while(cur2 <= right)
        {
            tmpNums[i] = nums[cur2];
            tmpIndex[i++]=index[cur2++];
        }

        // 还原
        for(int j = left; j <= right; j++)
        {
            nums[j] = tmpNums[j - left];
            index[j] = tmpIndex[j - left];
        }
    }
};

翻转对

题目:翻转对

在这里插入图片描述
思路

翻转对和逆序对的定义⼤同⼩异,逆序对是前⾯的数要⼤于后⾯的数。⽽翻转对是前⾯的⼀个数要⼤于后⾯某个数的两倍。因此,我们依旧可以⽤归并排序的思想来解决这个问题

C++代码

class Solution 
{
    int tmp[50010];
public:
    int reversePairs(vector<int>& nums) 
    {
        return mergeSort(nums, 0, nums.size() - 1);
    }
    int mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return 0;

        int ret = 0;
        int mid = (left + right) >> 1;

        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);

        int cur1 = left, cur2 = mid + 1, i = left;
        while(cur1 <= mid) // 降序
        {
            while(cur2 <= right &&  nums[cur2] >= nums[cur1] / 2.0)
                cur2++;
            if(cur2 > right)
                break;
        
            ret += right - cur2 + 1;
            cur1++;
        }

        cur1 = left, cur2 = mid + 1;
        while(cur1 <= mid && cur2 <= right)
            tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur2++] : nums[cur1++];
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];

        for(int j = left; j <= right; j++)
            nums[j] = tmp[j];
            
        return ret;
    }
};

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

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

相关文章

mindsearch run 本地服务

bing_browser.py ~/.conda/envs/mindsearch/lib/python3.10/site-packages/lagent/actions# vim bing_browser.py 修改提示词文件 MindSearch/mindsearch/agent/mindsearch_prompt.py # flake8: noqasearcher_system_prompt_cn """## 人物简介 你是一个可以…

易贝恩副总经理朱洪泽受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 北京易贝恩项目管理科技有限公司副总经理朱洪泽女士受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“从手动到智能&#xff1a;项目管理系统助力项目经理提升执行效率”。大会将…

python中的copy方法

记录一下python中的浅拷贝copy和深拷贝deepcopy 例题如下&#xff1a; import copyls [1, 2, [3, 4], 5, 6]ls1 ls.copy()ls2 lsls3 copy.deepcopy(ls)ls[2][1] 0ls.pop(1)ls.append([7, 8])print(ls1) #--------慙1慖print(ls2) #--------慙2慖print(ls3) #------…

windows10或11家庭版实现远程桌面连接控制

远程协助是一种Windows工具&#xff0c;允许控制者使用鼠标和键盘远程控制接受者的计算机&#xff0c;从某种程度上讲&#xff0c;这也是Win10家庭版无法远程桌面的一个有效替代方案。 步骤1. 在使用Windows远程协助之前&#xff0c;您需要先更改某些设置&#xff0c;右键单击…

yolov11模型在bdd100k数据集上的应用【代码+数据集+python环境+训练/应用GUI系统】

yolov8/9/10/11模型在bdd100k数据集上的应用【代码数据集python环境训练/应用GUI系统】 yolov8/9/10/11模型在bdd100k数据集上的应用【代码数据集python环境训练/应用GUI系统】 yolov8/9/10/11模型在bdd100k数据集上的应用【代码数据集python环境训练/应用GUI系统】 bdd100k数据…

Vue3项目开发——新闻发布管理系统(九)(完结篇)

文章目录 十一、用户信息管理1、用户基本资料管理1.1 页面设计1.2 封装接口,更新信息2、更换头像2.1 静态结构2.2 选择图片预览2.3 上传头像3、重置密码3.1 页面设计3.2 封装接口,更新密码十二、项目打包十三、系统全部源码下载十一、用户信息管理 用户信息管理包括功能:基…

软件设计师——信息安全

&#x1f4d4;个人主页&#x1f4da;&#xff1a;秋邱-CSDN博客☀️专属专栏✨&#xff1a;软考——软件设计师&#x1f3c5;往期回顾&#x1f3c6;&#xff1a;软件设计师——计算机网络&#x1f31f;其他专栏&#x1f31f;&#xff1a;C语言_秋邱 ​ 一、加密技术与认证技术…

Redis:初识Redis

Redis&#xff1a;初识Redis Redis 介绍分布式架构Redis特性安装Redis Redis 介绍 在官网中&#xff0c;是如下介绍Redis的&#xff1a; in-memory data store used by millions of developers as a cache, vector database, document database, streaming engine, and messag…

【解锁心灵枷锁】每天焦虑烦躁压抑?这7招助你重拾宁静与阳光!

在这个快节奏、高压力的时代&#xff0c;每个人心中或许都藏着一份不易察觉的焦虑与烦躁&#xff0c;它像一层无形的阴霾&#xff0c;悄悄笼罩着我们的生活&#xff0c;让人感到压抑与无力。但请相信&#xff0c;无论现状多么艰难&#xff0c;总有方法能驱散这些负面情绪&#…

大数据与人工智能:基础与应用的多维思考

大数据与人工智能&#xff1a;基础与应用的多维思考 前言一、时代定位与发展方向二、人工智能的本质与学科融合三、大数据和人工智能的构成要素与大众需求四、计算机系统结构与基础软件的重要性五、研究途径与领域知识的作用六、发展的态度与责任 前言 当下&#xff0c;大数据…

SpringBoot系列 启动流程

文章目录 SpringApplicationSpringApplication#run 启动流程BootstrapContextSpringApplicationRunListenersprepareEnvironmentconfigureEnvironmentconfigurePropertySourcesconfigureProfiles 上下文初始化prepareContextrefreshContextprepareRefreshobtainFreshBeanFactor…

Kotlin 处理字符串和正则表达式(二十一)

导读大纲 1.1 处理字符串和正则表达式1.1.1 分割字符串1.1.2 正则表达式和三引号字符串1.1.3 多行三引号字符串IntelliJ IDEA 和 Android Studio 中三重引号字符串内部的语法高亮显示 1.1 处理字符串和正则表达式 Kotlin 字符串与 Java 字符串完全相同 可以将 Kotlin 代码中创建…

【算法】哈希映射(C/C++)

目录 算法引入&#xff1a; 算法介绍&#xff1a; 优点&#xff1a; 缺点&#xff1a; 哈希映射实现&#xff1a; map unordered_map 题目链接&#xff1a;“蓝桥杯”练习系统 解析&#xff1a; 代码实现&#xff1a; 哈希映射算法是一种通过哈希函数将键映射到数组索…

JAVA基础语法 Day11

一、Set集合 Set特点&#xff1a;无序&#xff08;添加数据的顺序和获取出的数据顺序不一致&#xff09;&#xff0c;不重复&#xff0c;无索引 public class demo1 {public static void main(String[] args) {//1.创建一个集合//HashSet特点&#xff1a;无序&#xff0c;不重…

Redis: 集群架构,优缺点和数据分区方式和算法

集群 集群指的就是一组计算机作为一个整体向用户提供一组网络资源 我就举一个简单的例子&#xff0c;比如百度&#xff0c;在北京和你在上海访问的百度是同一个服务器吗&#xff1f;答案肯定是不是的&#xff0c;每一个应用可以部署在不同的地方&#xff0c;但是我们提供的服务…

C0010.Qt5.15.2下载及安装方法

1. 下载及安装 Qt 添加链接描述下载地址&#xff1a;http://download.qt.io/ 选择 archive 目录 安装Qt **注意&#xff1a;**本人使用的是Qt5.15.2版本&#xff0c;可以按如下方法找到该版本&#xff1b;

华为OD机试 - 信号发射和接收 - 矩阵(Python/JS/C/C++ 2024 E卷 200分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

【AIGC】AI时代的数据安全:使用ChatGPT时的自查要点

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;法律法规背景中华人民共和国保守秘密法中华人民共和国网络安全法中华人民共和国个人信息保护法遵守法律法规的重要性 &#x1f4af;ChatGPT的数据使用特点ChatGPT数据安全…

华为OD机试 - 分班问题(Python/JS/C/C++ 2024 E卷 200分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

无人机电力巡检:点亮电力巡检新视野!

一、无人机电力巡查的优势 提高巡检效率&#xff1a;无人机可以搭载高清摄像头、红外热像仪等先进设备&#xff0c;实时拍摄和传输图像&#xff0c;帮助巡检人员快速发现潜在问题&#xff0c;如电线破损、绝缘子污损、设备过热等&#xff0c;从而大大缩短了巡检周期。 降低人…