归并排序及其应用

news2025/8/3 2:24:11

归并排序算法基于分而治之的概念,具体来说就是遍历一棵树,归并的过程是一个后序执行的动作。 由于我们知道每个子部分在合并后都是有序的,我们可以利用这个特性来解决一些问题。
归并排序图示
上图可视化了merge sort algorithm的过程,我们很容易看出树的深度是log(N)。 基本上我们必须在合并中对序列进行排序,时间复杂度是 O(N)。 所以这个算法的时间复杂度总共是Nlog(N)
根据上图的思路,我们可以很容易的编写出下面这个程序。

class Solution
{
public:
    vector<int> sortArray(vector<int> &nums)
    {
        int len = nums.size();
        if (len < 2) return;
        
        int mid = len >> 1;
        vector<int> leftArray(nums.begin(), nums.begin() + mid);
        vector<int> rightArray(nums.begin() + mid, nums.end());
        
        sort(leftArray);
        sort(rightArray);
        mergeArray(nums, leftArray, rightArray);
        
        return nums;
    }
    
    void mergeArray(vector<int> &nums, vector<int> &leftArray, vector<int> &right)
    {
        int leftSize = leftArray.size(), rightSize = rightArray.size();
        int cur = 0, cur1 = 0, cur2 = 0;
        
        while (cur1 < leftSize && cur2 < rightSize)
        {
            if (leftArray[cur1] <= rightArray[cur2])
                nums[cur++] = leftArray[cur1++];
           	else
                nums[cur++] = rightArray[cur2++];
        }
        
        while (cur1 < leftSize)
            nums[cur++] = leftArray[cur1++];
        while (cur2 < rightSize)
            nums[cur++] = rightArray[cur2++];
    }
}

关于它的应用,我们总是试图找到一个问题是否可以应用合并后子部件有序的特性。 以下是应用“合并排序算法”的一些问题。
315. 计算右侧小于当前元素的个数
假设 i 指向左边的第一个元素,j 和 mid+1 指向右边的第一个元素。 当我们合并的时候,如果 temp[i] 小于 temp[j] ,我们可以知道有 j-mid-1 个元素小于 temp[i] ,因为数组是单调递增的。
合并示意
所以可以在合并的过程添加一些小小代码,其他的地方不变。

class Solution {
public:
    vector<pair<int, int>> temp;
    vector<int> count;
    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();
        vector<pair<int, int>> num_index;
        for (int i = 0; i < n; i++)
            num_index.push_back(pair<int, int>(nums[i], i));
        
        temp = vector<pair<int, int>>(n);
        count = vector<int>(n, 0);

        merge_sort(num_index, 0, n-1);
        return count;
    }
    void merge_sort(vector<pair<int, int>>& num_index, int l, int r){
        if (l >= r) return;
        int mid = l + (r - l) / 2;
        merge_sort(num_index, l, mid);
        merge_sort(num_index, mid+1, r);
        merge(num_index, l, mid, r);
    }
    void merge(vector<pair<int, int>>& num_index, int l, int mid, int r){
        int i = l, j = mid + 1;
        int k = l;
        while (i <= mid && j <= r){
            if (num_index[i].first <= num_index[j].first){
                count[num_index[i].second] += j - mid - 1;
                temp[k++] = num_index[i++];
            }
            else temp[k++] = num_index[j++];
        }
        while (i <= mid) {
            count[num_index[i].second] += j - mid - 1; 
            temp[k++] = num_index[i++];
        }
        while (j <= r) temp[k++] = num_index[j++];
        for (i = l; i <= r; i++)
            num_index[i] = temp[i];
    }
};

或者可以在后序位置操作一点点东西。
493. 翻转对
这个问题和上一个一样,只是有点不同。 我们假设下面有有序的左孩子和右孩子。 下一步是合并,但在此之前,我们可以计算左右之间的数字,betValue。 假设左边的数字是 leftValue,右边的数字是 rightValue。 可以递归计算最终结果。

class Solution
{
public:
    vector<int> tmp;
    int mergeSort(vector<int> &nums, int left, int right)
    {
        if (left >= right)
            return 0;
        int mid = left + ((right - left) >> 1);

        int retLeft = mergeSort(nums, left, mid);
        int retRight = mergeSort(nums, mid + 1, right);

        int cur1 = left, cur2 = mid + 1;
        int ret = 0;

        while (cur1 <= mid)
        {
            while (cur2 <= right && nums[cur1] / 2.0 > nums[cur2])
                cur2++;
            ret += cur2 - mid - 1;
            cur1++;
        }

        merge(nums, left, mid, right);

        return ret + retLeft + retRight;
    }

    void merge(vector<int> &nums, int left, int mid, int right)
    {
        int cur1 = left, cur2 = mid + 1, cur = left;

        while (cur1 <= mid && cur2 <= right)
        {
            if (nums[cur1] <= nums[cur2])
                tmp[cur++] = nums[cur1++];
            else
                tmp[cur++] = nums[cur2++];
        }
        while (cur1 <= mid)
            tmp[cur++] = nums[cur1++];
        while (cur2 <= right)
            tmp[cur++] = nums[cur2++];

        for (int i = left; i <= right; i++)
            nums[i] = tmp[i];
    }

    int reversePairs(vector<int> &nums)
    {
        int len = nums.size();
        tmp = vector<int>(len, 0);
        return mergeSort(nums, 0, len - 1);
    }
};

那么,如何获得betValue呢? 只需在后序空间添加一些代码。 我们可以得到右边第一个大于 nums[i] / 2.0 的元素。
327. 区间和的个数
是一样的,但是这里需要用到前缀和,理解为什么可以使用merge sort来解决这个问题。

class Solution
{
public:
    vector<long> tmp;
    int countRangeSum(vector<int> &nums, int lower, int upper)
    {
        int len = nums.size();
        vector<long> preSum({0});
        for (int i = 0; i < len; i++)
            preSum.emplace_back(preSum[i] + nums[i]);
        tmp = vector<long>(preSum.size(), 0);
        return mergeSort(preSum, 0, preSum.size() - 1, lower, upper);
    }

    int mergeSort(vector<long> &nums, int left, int right, int lower, int upper)
    {
        if (left >= right)
            return 0;
        int mid = left + ((right - left) >> 1);

        int retLeft = mergeSort(nums, left, mid, lower, upper);
        int retRight = mergeSort(nums, mid + 1, right, lower, upper);

        int cur1 = mid + 1, cur2 = mid + 1;
        int ret = 0;
        for (int i = left; i <= mid; i++)
        {
            while (cur1 <= right && nums[cur1] - nums[i] < lower)
                cur1++;
            while (cur2 <= right && nums[cur2] - nums[i] <= upper)
                cur2++;
            ret += cur2 - cur1;
        }

        merge(nums, left, mid, right);
        return ret + retLeft + retRight;
    }

    void merge(vector<long> &nums, int left, int mid, int right)
    {
        int cur1 = left, cur2 = mid + 1, cur = left;

        while (cur1 <= mid && cur2 <= right)
        {
            if (nums[cur1] <= nums[cur2])
                tmp[cur++] = nums[cur1++];
            else
                tmp[cur++] = nums[cur2++];
        }
        while (cur1 <= mid)
            tmp[cur++] = nums[cur1++];
        while (cur2 <= right)
            tmp[cur++] = nums[cur2++];

        for (int i = left; i <= right; i++)
            nums[i] = tmp[i];
    }
};

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

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

相关文章

2023年:我成了半个外包

边线业务与主线角色被困外包&#xff1b; 012022年&#xff0c;最后一个工作日&#xff0c;裁员的小刀再次挥下&#xff1b; 商务区楼下又多了几个落寞的身影&#xff0c;办公室内又多了几头暴躁的灵魂&#xff1b; 随着裁员的结束&#xff0c;部门的人员结构简化到了极致&am…

Fiddler在ios内的app中抓取https的解决方法

1、安装&设置Fiddler 查看链接---->Fiddler对PC浏览器&安卓App抓包的使用和配置 2、配置完后重启fiddler 3、ios安装证书 3.1、在fiddler右上角这里悬浮鼠标&#xff0c;查看自己电脑IP 或者通过&#xff1a; window键R&#xff0c;输入cmd&#xff0c;在命令行…

java Spring JdbcTemplate 准备工作

查看本文之前 您需要先看我 java Spring的IOC和AOP操作作为基础 如果并不掌握 可以找一下我之前的文章 都有讲到过 接下来的 我们将通过 JdbcTemplate 来做一些数据库操作 那我们就需要对 JdbcTemplate 有个基本的概念 首先 JdbcTemplate 是 Spring对 JDBC的一个很好的封装 通…

哪款蓝牙耳机延迟最低?打游戏零延迟的蓝牙耳机

我知道很多朋友会很在意声音延迟问题&#xff0c;虽然现如今蓝牙5.2甚至5.3已经可以几乎做到零延迟&#xff0c;但毕竟无线传输到现在依然存在或多或少延迟的情况出现&#xff0c;尤其是在这种蓝牙设备密集的地方&#xff0c;信号的干扰是不可避免的&#xff0c;由于技术的迭代…

【数据结构】空间复杂度

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;初阶数据结构 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对…

免去打包烦恼,自动构建你的GitHub Pages|玩转GitHub Pages三部曲(二)

本文讲述了如何利用 GitHub Actions 来自动构建 GitHub Pages 项目&#xff0c;免去繁琐的手动构建再提交过程&#xff0c;让你专注于写作。大家的点赞和互动是我更文的动力 /(ㄒoㄒ)/ 所以我决定发起一项活动&#xff0c;到三月三十一日统计&#xff0c;留言次数和赞赏次数最多…

CSDN新星计划/原力计划来喽,对此你有何期待

文章目录&#x1f31f; 写在前面&#x1f31f; 新星计划&#x1f31f; 独自开&#x1f31f; 原力计划&#x1f31f; 横穿全年的计划&#x1f31f; 写在最后&#x1f31f; 写在前面 哈喽&#xff0c;大家好&#xff0c;我是几何心凉&#xff0c;这是一份全新的专栏&#xff0c;…

辛普森悖论

感谢原作者&#xff01;原文传送门 以下是摘录&#xff1a; 1、什么是辛普森悖论&#xff1f; 辛普森悖论是 1951 年由 E.H. 辛普森提出的&#xff0c;简单来讲就是在分组比较中都占优势的一方&#xff0c;有的时候在总评中反而是失势的一方。指局部的结论和整体的结论完全相…

HashMap~

HashMap&#xff1a; HashMap是面试中经常被问到的一个内容&#xff0c;以下两个经常被问到的问题&#xff0c; Question1&#xff1a;底层数据结构&#xff0c;1.7和1.8有何不同&#xff1f; 答&#xff1a;1.7数组&#xff0b;链表&#xff0c;1.8数组&#xff0b;(链表|红…

k8s使用外部ca证书

PKI证书和要求Kubernetes 需要 PKI 证书才能进行基于 TLS 的身份验证。如果你是使用 kubeadm 安装的 Kubernetes&#xff0c; 则会自动生成集群所需的证书。你还可以生成自己的证书。 例如&#xff0c;不将私钥存储在 API 服务器上&#xff0c;可以让私钥更加安全。此页面说明了…

C++ 模板

1. 泛型编程实现一个通用的交换函数&#xff0c;使用函数重载虽然可以实现&#xff0c;但是有以 下几个不好的地方&#xff1a;1. 重载的函数仅仅是类型不同&#xff0c;代码复用率比较低&#xff0c;只要有新类型出现时&#xff0c;就需要用户自己增加对应的函数2. 代码的可维…

深度剖析数据在内存中的存储(下)(适合初学者)

上篇讲解了整形在内存中的存储方式&#xff0c;这篇文章就来继续讲解浮点数在内存中的存储方式。 上篇地址&#xff1a; (5条消息) 深度剖析数据在内存中的存储&#xff08;上&#xff09;_陈大大陈的博客-CSDN博客 目录&#xff1a; 3.浮点型在内存中的存储 3.1.浮点数的…

【Windows Server 2019】发布服务器 | 远程桌面服务的安装与配置 Ⅱ——配置RemoteAPP和访问

目录4. 配置RemoteAPP4.2 设置要发布的APP4.1 如何找到访问链接5. 访问发布的RemoteAPP关联博文4. 配置RemoteAPP 4.2 设置要发布的APP &#xff08;1&#xff09;返回【Server Manager】&#xff0c;在左侧的菜单栏中找到【Remote Desktop Services】。 &#xff08;2&#…

jupyter notebook小技巧

1、.ipynb 文件转word文档 将 jupyter notebook&#xff08;.ipynb 文件&#xff09;转换为 word 文件&#xff08;.docx&#xff09;的最简单方法是使用 pandoc。 首先安装pip install pandoc&#xff0c; 安装后&#xff0c;在将 Jupyter notebook文件目录cmd 然后输入打开…

ChatGPT 的盈利潜力:我使用语言模型赚取第一笔钱的个人旅程

使用 Fiverr、Python ChatGPT 和数据科学赚钱的指南。众所周知&#xff0c;ChatGPT 是 12 月发生的互联网突破性事件&#xff0c;几乎每个人都跳过了使用 AI 赚钱的潮流。在本文中&#xff0c;我将分享我是如何使用 ChatGPT 赚到第一笔钱的。本文包括以下主题&#xff1a;回到基…

深入理解Golang 中的Context包

context.Context是Go语言中独特的设计&#xff0c;在其他编程语言中我们很少见到类似的概念。context.Context深度支持Golang的高并发。 1.Goroutine 和channel。 在理解context包之前&#xff0c;应该首先熟悉Goroutine和Channel&#xff0c;能加深对context的理解。 1.1 Goro…

苹果设计可变色Apple Watch表带,智能穿戴玩法多

苹果最新技术专利显示&#xff0c;苹果正在为 Apple Watch 设计一款可变色的表带&#xff0c;可以根据佩戴者所穿着的服装、所在的环境等自动改变颜色。据介绍&#xff0c;这款表带里的灯丝具有电致变色功能&#xff0c;可以通过施加不同的电压&#xff0c;来实现显示多种颜色或…

C++之类与对象(上)

目录 一、类的定义 二.类的访问限定及封装 1.访问限定 2.封装 三.类的作用域和实例化 2.类的实例化 四.类的对象大小的计算 1.类成员存储方式 2.结构体内存对齐规则 五.类成员函数的this指针 1.this指针的引出 2.this指针的特性 3.C语言和C实现Stack的对比 一、类的定义 class …

Linux的kdump分析

文章目录一 系统环境二 下载和安装kerner-debuginfo三 启动crash四 crash常用命令一 系统环境 进行kdump分析的主机是CentOS-7.9系统。 [rootcanway ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootcanway ~]# uname -r 3.10.0-1160.el7.x86_64检查…

智慧新零售网络解决方案,助力新零售企业数智化转型

随着数字化时代的不断发展&#xff0c;新零售连锁业务模式“线上线下”融合发展&#xff0c;数据、设备在逐渐增加&#xff0c;门店数量也会随着企业规模的扩大而增加&#xff0c;但由于传统网络架构不稳定、延时、容量小影响服务质量&#xff08;QoS&#xff09;、分支设备数量…