算法---分治(归并排序)

在这里插入图片描述

T04BF

👋专栏: 算法|JAVA|MySQL|C语言

🫵 小比特 大梦想

此篇文章与大家分享分治算法关于归并排序的专题
对于归并排序在我个人主页专栏 <排序> 有详细的介绍
如果有不足的或者错误的请您指出!

1.归并排序

题目: 排序数组

1.1解析

关于归并排序的解析在我的个人主页<排序>专栏有详细的解释,理解归并排序对本专题的其他题目格外重要,一定要先理解归并排序的原理
本文直接画图说明:
在这里插入图片描述

1.2题解

public class Test4 {
        int[] tmp;//将tmp数组设置为类属性时间复杂度更低
    public int[] sortArray(int[] nums) {
        tmp = new int[nums.length];
        mergeSort(nums,0,nums.length-1);
        return nums;
    }
    private void mergeSort(int[] nums,int left,int right){
        if(left >= right){
            return;
        }
        int mid = left + (right-left)/2;
        mergeSort(nums,left,mid);
        mergeSort(nums,mid+1,right);

        int i = 0;
        int cur1 = left;
        int 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-left];
        }
    }
}

2.数组中的逆序对

题目:数组中的逆序对

2.1解析:

如果我们通过暴力解法,就是两个两个枚举,但是时间复杂度太高了,在本题会超时
我们转变一下思路:
在这里插入图片描述
如果我们把数组划分维如图所示的两段区间,假设左区间的逆序对的数量为a,右边的逆序对的数量为b,再加上枚举左边一个右边一个元素的所有逆序对的情况为c,那么总逆序对的情况就是a + b + c;而左边区间和右边区间又各自可以分成两段区间…
举个具体例子:
在这里插入图片描述
如图所示,我们用count来记录逆序对的个数,那么最后一层9 和 7,就是1组逆序对,count = 1; 9 7 和 5,可以组成 (9,5)和 (7,5),那么count = 3;
4和6不能组成逆序对;9 7 5 和 4 6,可以组成(9,4),(9,6),(7,4),(7,6),(5,4) ,那么count = 8
而上述每次分解成两段区间的过程不就是我们归并排序的分解过程嘛??
再者说,好像我们这样子分,到头来还是需要一一枚举,好像时间复杂度并没有很大的提示.但是我们实际上会发现一个细节:
我们在一一枚举的过程中并不关心左区间元素的顺序,也不关心右区间元素的顺序,我们每次只需要关心相对于左边的某个元素,在右边有多少个比这个元素小即可
那么我们就可以利用归并排序,将左右区间排序好
在这里插入图片描述
假设这是我们某段已经分别排好序的左右区间,排完序后我们找逆序对的时间复杂度就很低了
我们用变量cur2来遍历右区间,用变量cur1来遍历左区间
那么在定住cur2的时候,我们只需在左边从左到右找到第一个比2大的,就是3,那么,在左区间内3以后的所有数字,都可以和右区间的2组成逆序对,这样我们一下子就找出很多逆序对,不再需要一一枚举;接下来cur2++,但是cur1不需要回头重新从1开始遍历,因为我们的区间是递增的,而1 < 2,1就必然小于3,这就是排序给我们带来的很多好处
而当我们回顾一下归并排序的过程:我们也是用变量cur2来遍历右区间,用变量cur1来遍历左区间,当nums[cur1] <= nums[cur2]的时候,cur1++;反之cur2++,这不就恰好和我们上面分析的过程一模一样嘛???
因此我们只需要将上面的找逆序对的过程穿插在归并排序中间即可!不会给原归并排序带来时间复杂度的增加,因此时间复杂度还是O(N logN)!

理解完上面后,我们来拓展一个点
我们上面的数组是递增的,但是如果是递减呢??
在这里插入图片描述
那么此时我们应该是定住左边的元素,在右边找到第一个比这个元素小的,那么找到的这个元素之后的所有元素,都能和左边这个元素组成逆序对

2.2题解:

class Solution {
    private  int count = 0;
    private  int[] tmp;
    public  int reversePairs(int[] record) {
        tmp = new int[record.length];
        mergeSort(record,0,record.length-1);
        return count;
    }
    private  void mergeSort(int[] record,int left,int right){
        if(left >= right){
            return;
        }
        int mid = left + (right-left)/2;
        mergeSort(record,left,mid);
        mergeSort(record,mid+1,right);

        int cur1 = left;
        int cur2 = mid+1;
        int k = 0;
        while(cur1 <= mid && cur2 <= right){
            if(record[cur1] > record[cur2]){
                tmp[k++] = record[cur2++];
                count += mid - cur1 + 1;
            }else{
                tmp[k++] = record[cur1++];
            }
        }
        while(cur1 <= mid){
            tmp[k++] = record[cur1++];
        }
        while(cur2 <= right){
            tmp[k++] = record[cur2++];
        }

        for(int i = left; i <= right; i++){
            record[i] = tmp[i-left];
        }
    }
}

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

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

3.1解析

实际上和我们上一道题的原理是一模一样的,但是这道题要我们返回的是数组就给我们带来了一定的难度
即题目要求我们返回的是数组,数组中存储的是给定数组对应元素的右侧小于当前元素的个数,而由于我们的原数组在归并排序中是一直在变的
解决方法就是哈希思想,但是我们采用哈希表,因为数组中可能会存在重复元素;
我们可以构建一个index数组,用来映射给定数组的下标,.当给定数组变了的时候,我们的index也要跟着变

3.2 题解

class Solution {
    int[] ret;
    int[] tmp;
    int[] tmpIndex;
    int[] index;
    public List<Integer> countSmaller(int[] nums) {
        ret = new int[nums.length];
        tmp = new int[nums.length];
        tmpIndex = new int[nums.length];
        index = new int[nums.length];
        for(int i = 0; i < index.length; i++){
            index[i] = i;
        }
        mergeSort(nums,0,nums.length-1);
        List<Integer> list = new ArrayList<>();
        for(int x : ret){
            list.add(x);
        }
        return list;
    }
    private void mergeSort(int[] nums,int left,int right){
        if(left >= right){
            return;
        }
        int mid = left + (right - left)/2;
        mergeSort(nums,left,mid);
        mergeSort(nums,mid+1,right);

        int cur1 = left;
        int cur2 = mid + 1;
        int k = 0;
        while(cur1 <= mid && cur2 <= right){
            if(nums[cur2] >= nums[cur1]){
                tmp[k] = nums[cur2];
                tmpIndex[k++] = index[cur2++];

            }else{
                ret[index[cur1]] += right - cur2 + 1;
                tmp[k] = nums[cur1];
                tmpIndex[k++] = index[cur1++];
            }
        }
        while (cur1 <= mid){
            tmp[k] = nums[cur1];
            tmpIndex[k++] = index[cur1++];
        }
        while(cur2 <= right){
            tmp[k] = nums[cur2];
            tmpIndex[k++] = index[cur2++];
        }

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

4.翻转对

题目:翻转对

4.1解析

大体思路还是和我们前两题的思路是一样的,但是有一点特别的就是,.我们前两道题,无论是找逆序对还是找比当前元素小的个数,实际上都是单纯的两个数比大小,恰好和归并排序的过程吻合
但是我们这道题要比较的是两倍的关系,因此我们需要"合并"两次,第一次合并实际上并不是真正的合并,而是判断进行比较;第二次才是真正的合并

4.2题解

class Solution {
    int[] tmp;
    int count = 0;
    public int reversePairs(int[] nums) {
        tmp = new int[nums.length];
        merseSort(nums,0,nums.length-1);
        return count;
    }

    private void merseSort(int[] nums,int left,int right){
        if(left >= right){
            return;
        }
        int mid = left + (right - left) / 2;
        merseSort(nums,left,mid);
        merseSort(nums,mid+1,right);
        int cur1 = left;
        int cur2 = mid + 1;

        while(cur1 <= mid && cur2 <= right){
            while(cur2 <= right && nums[cur1] / 2.0 <= nums[cur2]){
                cur2++;
            }
            count += right - cur2 + 1;
            cur1++;
        }
        cur1 = left;
        cur2 = mid+1;
        int k = 0;
        while(cur1 <= mid && cur2 <= right){
            tmp[k++] = nums[cur1] > nums[cur2] ? nums[cur1++] : nums[cur2++];
        }
        while(cur1 <= mid){
            tmp[k++] = nums[cur1++];
        }
        while(cur2 <= right){
            tmp[k++] = nums[cur2++];
        }
        for(int i = left; i <= right; i++){
            nums[i] = tmp[i-left];
        }
    }
}

感谢您的访问!!期待您的关注!!!

在这里插入图片描述

T04BF

🫵 小比特 大梦想

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32使用HAL库获取GPS模块HT1818Z3G5L信息(方法1)

1、写在最前 先了解一下GPRMC的格式 格 式&#xff1a; GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,A*50 说 明&#xff1a; 字段 0&#xff1a;$GPRMC&#xff0c;语句ID&#xff0c;表明该语句为Recommended Minimum Specific GPS/TRANSIT Data&…

Open CASCADE学习|在给定的TopoDS_Shape中查找与特定顶点 V 对应的TopoDS_Edge编号

enum TopAbs_ShapeEnum{TopAbs_COMPOUND,TopAbs_COMPSOLID,TopAbs_SOLID,TopAbs_SHELL,TopAbs_FACE,TopAbs_WIRE,TopAbs_EDGE,TopAbs_VERTEX,TopAbs_SHAPE}; 这段代码定义了一个名为 TopAbs_ShapeEnum 的枚举类型&#xff0c;它包含了表示不同几何形状类型的常量。这些常量通常…

通过学习mayfly-go,我学会了前端如何优雅设计字典值

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen shigen在假期的最后一天早晨起来&#xff0c;翻看了一下博客&#xff0c;一个ma…

spring注解驱动系列--声明式事务

一、环境搭建 一、导入依赖 <!-- 数据源、数据库驱动、spring-jdbc模块--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.12.RE…

Dockerfile详解构建镜像

Dockerfile构建企业级镜像 在服务器上可以通过源码或rpm方式部署Nginx服务&#xff0c;但不利于大规模的部署。为提高效率&#xff0c;可以通过Dockerfile的方式将Nginx服务封装到镜像中&#xff0c;然后Docker基于镜像快速启动容器&#xff0c;实现服务的快速部署。 Dockerf…

统一网关 Gateway(黑马程序员)

网关的技术实现 在 SpringCloud 中网关的实现包括两种&#xff1a; gatewayzuul Zuul 是基于 Servlet 的实现&#xff0c;属于阻塞式编程。而 SpringCloudGateway 则是基于 Spring5 中提供的 WebFlux &#xff0c; 属于响应式编程的实现&#xff0c;具备更好的性能。 网关的作…

火柴排队(c++实现)

题目 涵涵有两盒火柴&#xff0c;每盒装有 n 根火柴&#xff0c;每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列&#xff0c;同一列火柴的高度互不相同&#xff0c;两列火柴之间的距离定义为&#xff1a; 其中 ai 表示第一列火柴中第 i 个火柴的高度&#xf…

【OneAPI】贴纸生成API

OneAPI新接口发布&#xff1a;贴纸生成 生成一个10241024像素的贴纸。 API地址&#xff1a;POST https://oneapi.coderbox.cn/openapi/api/stickers 请求参数&#xff08;body&#xff09; 参数名类型必填含义说明prompt提示词是提示词示例&#xff1a;一只可爱的小狗 响应…

网络网络层之(1)IPv4地址

网络网络层之(1)IPv4地址 Author: Once Day Date: 2024年4月1日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day的…

嵌入式系统初学者指南

什么是嵌入式系统&#xff1f; 嵌入式系统是一种独立的、基于微处理器的计算机系统。您可以将其视为大型系统的一部分的微型计算机。如今&#xff0c;从洗碗机到波音 747&#xff0c;几乎所有“电子”产品内部都有嵌入式系统。但是&#xff0c;嵌入式系统与笔记本电脑或手机不…

mysql dublewrite 双写缓存机制

mysql dublewrite 双写缓存机制&#xff0c;像不像主板双bois系统&#xff0c; 在MySQL的InnoDB存储引擎中&#xff0c;当进行数据写操作时&#xff0c;会先将数据写入到内存中的缓冲池&#xff08;Buffer Pool&#xff09;&#xff0c;然后异步刷新到磁盘上的数据文件。为了提…

基于巴法云物联网云平台构建可视化控制网页(以控制LED为例)

0 前言 如今大大小小的物联网云平台非常多&#xff0c;但大部分要收取费用&#xff0c;免费的物联网云平台功能则有很多限制使用起来非常不方便。以百度云物联网云平台为例&#xff0c;它的物可视不支持发布主题&#xff0c;等于可视化界面只能作为数据监控而不具备双向通信的…

打造个人高效图床系统:威联通NAS+兰空+PicGo全方位整合教程

1.图床选择 最近因为家里人有使用图床的需求&#xff0c;又担心第三方图床跑路导致数据丢失&#xff0c;恰好家里有个威联通NAS&#xff0c;还有公网IP和域名&#xff0c;既然如此&#xff0c;那就动手自建一个图床吧&#xff0c;毕竟开源的图床应用还是有很多的。 一上来就在…

掌握数据相关性新利器:基于R、Python的Copula变量相关性分析及AI大模型应用探索

在工程、水文和金融等各学科的研究中&#xff0c;总是会遇到很多变量&#xff0c;研究这些相互纠缠的变量间的相关关系是各学科的研究的重点。虽然皮尔逊相关、秩相关等相关系数提供了变量间相关关系的粗略结果&#xff0c;但这些系数都存在着无法克服的困难。例如&#xff0c;…

写JDBC遇到的问题

执行会出现以下错误信息 java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ? and loginPwd ? at line 1 at com.mysql.cj.jdbc.exceptions…

spark3.x新特性

Adaptive Query Execution自适应查询(SparkSQL) 由于缺乏或者不准确的数据统计信息&#xff08;元数据&#xff09;和对成本的错误估算&#xff08;执行计划调度&#xff09;导致生成的初始执行计划不理想 在Spark3.x版本提供Adaptive Query Execution自适应查询技术 通过在”…

数据结构顺序表的初始化,头插,尾插,头删,尾删,指定位置删除,指定位置插入,查找,销毁(详解)

目录 前言顺序表的介绍静态顺序表动态顺序表一.顺序表的初始化二.销毁扩容顺序表打印顺序表三.头插四.尾插五.头删六.尾删七.指定位置之前&#xff08;包括指定位置&#xff09;的插入八.指定位置数据的删除九.查找全部的代码实现总结 前言 数据结构是什么&#xff1f; 数据结…

碘浊度法与红外相机联用测定食品中维生素C

&#x1f31e;欢迎来到看论文的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f4c6;首发时间&#xff1a;&#x1f339;2024年4月6日&…

1.微服务

一、微服务是什么 微服务是一种架构风格&#xff0c;即&#xff0c;一个应用应该是一组小型服务&#xff0c;每个服务器只负责一种服务&#xff0c;服务之间可以通过 HTTP 的方式进行互通。每一个功能元素最终都是一个可独立替换和独立升级的软件单元。 可以说&#xff0c;微…

网络编程套接字应用分享【Linux C/C++ 】【UDP应用 | TCP应用 | TCP线程池小项目】

目录 前提知识 1. 理解源ip&#xff0c;目的ip和Macip 2. 端口号 3. 初识TCP&#xff0c;UDP协议 4. 网络字节序 5. socket 编程 sockaddr类型 一&#xff0c;基于udp协议编程 1. socket——创建套接字 2. bind——将套接字强绑定 3. recvfrom——接受数据 4. s…