分治算法——快排 | 归并思想

news2024/11/28 10:39:19

文章目录

  • 一、快排思想
    • 1. leetcode75. 颜色分类
    • 2. leetcode912. 排序数组
    • 3. leetcode215. 数组中的第K个最大元素
    • 4. leetcode面试题17.14. 最小K个数
  • 二、归并思想
    • 1. leetcode912. 排序数组
    • 2. leetcodeLCR 170. 交易逆序对的总数
    • 3. 计算右侧小于当前元素的个数
    • 4. 翻转对


一、快排思想

当一个数组中的元素重复率特别高的时候,经典的快速排序算法是不适合的。它会导致时间复杂度由O(logN)上升为O(N^2),这里我们可以使用三项切分的方式来实现快速排序算法,所谓的三项切分,就是把等于基准值的元素放在中间,大于基准值的元素和小于基准值的分别放两边,这样数组分成了三分,比起普通的快速排序,当数据中的重复元素特别多时,效率将会大大提升。

单趟排序的过程:

在这里插入图片描述


1. leetcode75. 颜色分类

在这里插入图片描述
颜色分类

代码实现:

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

2. leetcode912. 排序数组

在这里插入图片描述
排序数组

代码实现:

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

    void qsort(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]);
        }

        // 递归左右子区间
        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;
    }
};

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

在这里插入图片描述
数组中的第K个最大元素

代码实现:

class Solution {
public:
    int getRandom(const vector<int>& nums, int left, int right){
        return nums[rand()%(right - left + 1) + left];
    }
    // 快速选择算法
    int qsort(vector<int>& nums, int l, int r, int k)
    {
        if(l == r) return nums[l];
        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]);
        }

        // 重点代码
        int c = r - right + 1, b = right - left - 1;
        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(0));
        return qsort(nums, 0, nums.size() - 1, k);
    }
};

4. leetcode面试题17.14. 最小K个数

在这里插入图片描述
最小K个数

代码实现:

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

    void qsort(vector<int>& arr, int l, int r, int k)
    {
        if(l >= r) return;
        int key = getRandom(arr, l, r);
        int i = l, left = l - 1, right = r + 1;
        while(i < right)
        {
            if(arr[i] < key) swap(arr[++left], arr[i++]);
            else if(arr[i] == key) i++;
            else swap(arr[--right], arr[i]);
        }

        // 核心代码
        int a = left - l + 1, b = right - left - 1;
        if(a > k) qsort(arr, l, left, k);
        else if(a + b >= k) return;
        else qsort(arr, right, r, k - a - b);
    }
    vector<int> smallestK(vector<int>& arr, int k) {
        srand(time(0));
        qsort(arr, 0, arr.size() - 1, k);
        return {arr.begin(), arr.begin() + k};
    }
};

二、归并思想

归并排序算法是采用 分治法(Divide and Conquer) 的一个非常典型的应用,且各层分治递归可以同时进行。

在这里插入图片描述

首先把一个未排序的序列从中间分割成2部分,再把2部分分成4部分,依次分割下去,直到分割成一个一个的数据,再把这些数据两两归并到一起,使之有序,不停的归并,最后成为一个排好序的序列。


1. leetcode912. 排序数组

在这里插入图片描述
排序数组

代码实现:

class Solution {
    vector<int> tmp;
public:
    void mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return;
        int mid = (left + right) >> 1;
        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]) tmp[i++] = nums[cur1++];
            else 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];
    }
    vector<int> sortArray(vector<int>& nums) {
        tmp.resize(nums.size());
        mergeSort(nums, 0, (int)nums.size() - 1);
        return nums;
    }
};

2. leetcodeLCR 170. 交易逆序对的总数

在这里插入图片描述
交易逆序对的总数

解题思路:

这里我们可以采用分治的思想来解决这道问题,与归并排序不同的是,这里我们使用降序的方式进行归并排序,将序列从中间分开,将逆序对分成三类:

在这里插入图片描述

因此我们解决问题的思路可以转换为一下方式:

  1. 递归算左边的;
  2. 递归算右边的;
  3. 算一个左一个右的;
  4. 把他们加到到一起。

在这里插入图片描述

代码实现:

class Solution {
public:
    vector<int> tmp;
    int MergeSort(vector<int>& record, int left, int right)
    {
        if(left >= right) return 0;
        int mid = (left + right) >> 1;
        int res = MergeSort(record, left, mid) + MergeSort(record, mid + 1, right);
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if(record[cur1] > record[cur2]){
                res += (right - cur2 + 1);
                tmp[i++] = record[cur1++];
            }else{
                tmp[i++] = record[cur2++];
            }
        }
        while(cur1 <= mid) tmp[i++] = record[cur1++];
        while(cur2 <= right) tmp[i++] = record[cur2++];

        for(int i = left; i <= right; i++) record[i] = tmp[i - left];
        return res;
    }
    int reversePairs(vector<int>& record) {
        tmp.resize(record.size());
        return MergeSort(record, 0, record.size() - 1);
    }
};

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

在这里插入图片描述
计算右侧小于当前元素的个数

解题思路:

这道题和上一道题目很像,但是不同的是本题需要将数组中的每一个元素的逆序对的个数组成一个新的数组来返回,也就是需要将数组中的每一个元素与其下标建立一一映射关系,但是数组中的元素难免会有重复的,这样的话就不好建立一一对应的映射关系,

当然,思路我们还是上一道题目的思路,不过这里我们增加了数组中的元素和唯一下标的一一对应关系。

在这里插入图片描述

代码实现:

class Solution {
public:
    vector<int> ret;
    vector<int> index;
    int tmpN[500010], tmpI[500010];

    void MergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return;
        int mid = (left + right) / 2;

        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])
            {
                ret[index[cur1]] += right - cur2 + 1;
                tmpN[i] = nums[cur1];
                tmpI[i++] = index[cur1++];
            }
            else
            {
                tmpN[i] = nums[cur2];
                tmpI[i++] = index[cur2++];
            }
        }
        while(cur1 <= mid){ tmpN[i] = nums[cur1], tmpI[i++] = index[cur1++]; }
        while(cur2 <= right){ tmpN[i] = nums[cur2], tmpI[i++] = index[cur2++]; }

        for(int j = left; j <= right; j++)
        {
            nums[j] = tmpN[j - left];
            index[j] = tmpI[j - left];
        }
    }
    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();
        index.resize(n), ret.resize(n);

        for(int i = 0; i < n; i++) index[i] = i;

        MergeSort(nums, 0, n - 1);
        return ret;
    }
};

4. 翻转对

在这里插入图片描述
翻转对

解题思路:

这道题目同样是求逆序对,不过该逆序对需要前一个数大于后一个数的两倍,才能构成满足要求的一个逆序对。

这里我们同样采用归并排序的思想来解决,前面的题目我们都可以使用边排序边求解的方式来进行,但是这道题目前一个数需要大于后一个数的两倍,所以我们不能使用边排序边求解的思路来解决。我们可以先寻找答案然后再排序。下面重点来说一下寻找答案的过程:

这里再已经排好序的两段区间内寻找答案我们可以使用同向双指针的方式来解决。

在这里插入图片描述

代码实现:

class Solution {
public:
    vector<int> tmp;
    int MergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return 0;
        int mid = (left + right) >> 1;
        int ret = MergeSort(nums, left, mid) + MergeSort(nums, mid + 1, right);
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid)
        {
            while(cur2 <= right && nums[cur1]/2.0 <= nums[cur2]) cur2++;
            if(cur2 > right) break;
            ret += right - cur2 + 1;
            cur1++;
        }

        cur1 = left, cur2 = mid + 1;
        while(cur1 <= mid && cur2 <= right)
        {
            if(nums[cur1] > nums[cur2])
                tmp[i++] = nums[cur1++];
            else
                tmp[i++] = nums[cur2++];
        }
        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 - left];

        return ret;
    }
    int reversePairs(vector<int>& nums) {
        tmp.resize(nums.size());
        return MergeSort(nums, 0, nums.size() - 1);
    }
};

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

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

相关文章

GBJ3510-ASEMI电源控制柜专用GBJ3510

编辑&#xff1a;ll GBJ3510-ASEMI电源控制柜专用GBJ3510 型号&#xff1a;GBJ3510 品牌&#xff1a;ASEMI 芯片个数&#xff1a;4 封装&#xff1a;GBJ-4 恢复时间&#xff1a;&#xff1e;50ns 工作温度&#xff1a;-55C~150C 浪涌电流&#xff1a;350A 正向电流&am…

iWall:支持自定义的Mac动态壁纸软件

iWall Mac是一款动态壁纸软件&#xff0c;它可以使用任何格式的漂亮视频&#xff08;无须转换&#xff09;、图片、动画、Flash、gif、swf、程序、网页、网站做为您的动态壁纸、动态桌面&#xff0c;并且可以进行交互。 这款软件功能多、使用简单、体积小巧、不占用资源、运行…

Java每日笔试题错题分析(5)

Java每日笔试题错题分析&#xff08;5&#xff09; 一、错题知识点前瞻第1题第2题第3题第4题第5题第6题第7题 二、错题展示及其解析第1题第2题第3题第4题第5题第6题第7题 一、错题知识点前瞻 第1题 数组的初始化 数组的初始化有两种&#xff0c;分为静态初始化和动态初始化 静…

ai_drive67_基于不确定性的多视图决策融合

论文链接&#xff1a;https://openreview.net/forum?idOOsR8BzCnl5 https://arxiv.org/abs/2102.02051 代码链接&#xff1a;https://github.com/hanmenghan/TMC Zongbo Han, Changqing Zhang, Huazhu Fu, Joey Tianyi Zhou, Trusted Multi-View Classification, Internatio…

动态内存管理改造简易通讯录

动态通讯录 本章内容基于上章内容实现&#xff0c;具体情况若有不清楚&#xff0c;请先查看上一篇文章。 动态通讯录实现了&#xff0c;动态开辟&#xff0c;如果存放满了&#xff0c;再开辟空间进行存储&#xff0c;相对静态更方便一些。 动态通讯录需要改造的地方 我们基于…

Python--比较运算符

比较运算符 特别注意&#xff1a;当我们使用比较运算符对两个变量进行比较时&#xff0c;其返回一个布尔类型的值。 案例&#xff1a;两个数大小的比较 num1 10 num2 20 print(num1 > num2) # False print(num1 < num2) # True print(num1 > num2) # False print…

使用免费云服务器体验

免费的才是最贵的 谈谈使用【三*丰*云*免*费*服务器】的超级后悔体验 你以为开通了就永久免费了&#xff1f;怎么可能&#xff01;&#xff01;&#xff01; 使用方法 第一步&#xff1a;注册&#xff0c;实名认证 实名认证收费0.7 此时可以使用24小时&#xff0c;到期自动…

AI影像修复及图像超分辨率

AI图像修复软件主要包含人脸修复、图像超分等功能。人脸修复功能主要对图像上的人脸进行识别和修复&#xff0c;从模糊、缺损、噪声图像中恢复高质量人脸图像。图像超分功能主要对图像进行超分辨率重建&#xff0c;将低分辨率图像处理为高分辨率图像。 链接&#xff1a;https:…

Linux:基础命令

Linux&#xff1a;基础命令 0. Linux的目录结构1. Linux命令基础格式2. ls命令2.1 隐藏文件、文件夹 3. 相对和绝对路径3.1 特殊路径符 4. mkdir命令4.1 mkdir -p 选项 5. touch 创建文件6. cat命令 查看文件内容 0. Linux的目录结构 /&#xff0c;根目录是最顶级的目录了Linux…

【java学习—七】关键字super(32)

文章目录 1. 功能2. 代码中理解3. super调用父类构造器3.1. 结论一证明3.2. 结论二证明 4. this和super的区别 1. 功能 在 Java 类中使用 super 来调用父类中的指定操作&#xff1a; &#xff08;1&#xff09;super 可用于访问父类中定义的属性 &#xff08;2&#xff09;sup…

unity的脚本执行顺序问题

当一个物体同时挂载有多个脚本时&#xff0c;谁会先执行呢&#xff1f; 猜想&#xff1a;Test2在Test1的上面应该会先执行吧&#xff01; 结果&#xff1a;Test1先执行 如果你想要某一个脚本先执行&#xff0c;可以使用Awake方法 执行顺序 是先把所以脚本的Awake执行完&a…

【Transformer系列】深入浅出理解ViT(Vision Transformer)网络模型

一、参考资料 极智AI | 详解 ViT 算法实现 MobileViT模型简介 ECCV 2022丨力压苹果MobileViT&#xff0c;这个轻量级视觉模型新架构火了 ECCV 2022丨轻量级模型架构火了&#xff0c;力压苹果MobileViT&#xff08;附代码和论文下载&#xff09; 再读VIT&#xff0c;还有多少细…

学习笔记---超基础+详细+新手的顺序表~~

目录 1.顺序表的前言 1.1 顺序表--->通讯录&#x1f4c7; 1.2 数据结构的相关概念&#x1f3c7; 1.2.1 什么是数据结构 1.2.1 为什么需要数据结构 2. 顺序表概念及分类 2.1 顺序表的概念&#x1f419; 2.2 顺序表的分类&#x1f42b; 2.2.1 顺序表和数组的区别 2.…

c++ 学习之多态

来看代码 我们来看看早绑定的代码 #define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std;class Animal { public:void speak(){cout << "动物在说话 " << endl;} }; class Cat : public Animal { public:void speak() {cout…

android studio检测不到真机

我的情况是&#xff1a; 以前能检测到&#xff0c;有一天我使用无线调试&#xff0c;发现调试有问题&#xff0c;想改为USB调试&#xff0c;但是半天没反应&#xff0c;我就点了手机上的撤销USB调试授权&#xff0c;然后就G了。 解决办法&#xff1a; 我这个情况比较简单&…

LD链接脚本

1.LD链接脚本的简介 LD链接脚本的概念 LD链接器脚本在完整程序编译流程中的链接过程使用。LD链接器脚本定义了程序各个程序段的存储分布&#xff0c;描述链接器如何将这些目标文件.o文件链接成一个输出可执行文件LD链接器脚本与CPU的种类、MCU的内部存储器分布有关。 LD链接…

华为---企业WLAN组网基本配置示例---AC+AP组网

ACAP组网所需的物理条件 1、无线AP---收发无线信号&#xff1b; 2、无线控制器(AC)---用来控制管理多个AP&#xff1b; 3、PoE交换机---能给AP实现网络连接和供电的交换机&#xff1b; 4、授权&#xff1a;默认AC管理的AP数量有限&#xff0c;买授权才能管控更多AP。 WLAN创建…

思维模型 峰终定律

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。 1 峰-终定律的应用 1.1 迪士尼游乐园 迪士尼乐园采用了多种策略来创造令人难忘的体验&#xff0c;从而遵循峰终定律的原则。具体如下&#xff1a; 迪士尼乐园的入口设计和服务体验&…

E047-论坛漏洞分析及利用-针对Wordpress论坛进行信息收集与漏洞扫描的探索

任务实施: E047-论坛漏洞分析及利用-针对Wordpress论坛进行信息收集与漏洞扫描的探索 任务环境说明&#xff1a; 服务器场景&#xff1a;p9_kali-6&#xff08;用户名&#xff1a;root&#xff1b;密码&#xff1a;toor&#xff09; 服务器场景操作系统&#xff1a;Kali Li…

零代码编程:用ChatGPT批量下载谷歌podcast上的播客音频

谷歌podcast有很多播客音频&#xff0c;如何批量下载到电脑呢&#xff1f; 以这个播客为例&#xff1a; https://podcasts.google.com/feed/aHR0cHM6Ly9oYWRhcnNoZW1lc2guY29tL2ZlZWQvcG9kY2FzdC8?saX&ved0CAkQlvsGahcKEwi4uauWsvKBAxUAAAAAHQAAAAAQAg 查看网页源代码&a…