【练习】分治--归并排序

  • 🎥 个人主页:Dikz12
  • 🔥个人专栏:算法(Java)
  • 📕格言:吾愚多不敏,而愿加学
  • 欢迎大家👍点赞✍评论⭐收藏

目录

归并排序

代码实现 

交易逆序对的总数 

题目描述 

​编辑 题解

代码实现

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

题目描述 

​编辑 题解

 代码实现

 翻转对

题目描述 

​编辑 题解

代码实现 


归并排序

归并排序( MERGE-SORT )是建立在归并操作上的一种有效的排序算法 ,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子 序列段间有序。若 将两个有序表合并成一个有序表 ,称为二路归并。 时间复杂度:O(N*logn)

⼤体过程分为两步:
  • 分:将数组⼀分为⼆为两部分,⼀直分解到数组的⻓度为 1 ,使整个数组的排序过程被分为 「左半部分排序」 + 「右半部分排序」;
  • 治:将两个较短的「有序数组合并成⼀个⻓的有序数组」,⼀直合并到最初的⻓度。

代码实现 

class Solution {
    int[] tmp;
    public int[] sortArray(int[] nums) {
        tmp = new int[nums.length];
        mergeSort(nums,0,nums.length-1);
        return nums;
    }
    public void mergeSort(int[] nums,int left,int right) {
        if(left >= right) {
            return;
        }
        //1.根据中间点拆分数据.左、右两部分
        int mid = (left + right) / 2;
        mergeSort(nums,left,mid);
        mergeSort(nums,mid+1,right);
        //2.合并两个有序数组
        // int[] tmp = new int[right - left + 1];
        int cur1 = left,cur2 = mid + 1,i = 0;
        while(cur1 <= mid && cur2 <= right) {
            tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];
        }
        while(cur1 <= mid) {
            tmp[i++] = nums[cur1++];
        }
        while(cur2 <= right) {
            tmp[i++] = nums[cur2++];
        }
        // 3.还原到原数组
        for(int j = left; j <= right; j++) {
            nums[j] = tmp[j - left];
        }
    }
}

交易逆序对的总数 

题目描述 

 题解

因此,我们可以利⽤归并排序的过程,先求出左半数组中逆序对的数量,再求出右半数组中逆序对的 数量,最后求出⼀个选择左边,另⼀个选择右边情况下逆序对的数量,三者相加即可。 (利⽤数组的有序性,快速统计 出逆序对的数量,⽽不是将所有情况都枚举出来)
最核心的问题,如何在合并两个有序数组的过程中,统计出逆序对的数量?
  1. 找出该数之前,有多少个数比它大
  2. 找出该数之后,有多少个数比它小.

代码实现

    int[] tmp;
    public int reversePairs(int[] nums) {
        tmp = new int[nums.length];
        return mergerSort(nums,0,nums.length - 1);
    }
    public int mergerSort(int[] nums,int left,int right) {
        //1.递归出口
        if(left >= right) {
            return 0;
        }
        // 2.分成左右两部分
        int ret = 0;
        int mid = (left + right) >> 1;
        ret += mergerSort(nums,left,mid);
        ret += mergerSort(nums,mid + 1,right);
        //3.合并
        // int[] tmp = new int[right - left + 1];
        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 j = left; j <= right; j++) {
            nums[j] = tmp[j - left];
        }
        return ret;
    }

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

题目描述 

 题解

解法:归并排序. 

 代码实现

    int[] ret; // 结果数组
    int[] index; // 存放原始数据的下标
    int[] tmpNums; // 辅助数组
    int[] tmpIndex;

    public List<Integer> countSmaller(int[] nums) {
        int n = nums.length;
        ret = new int[n];
        index = new int[n];
        tmpIndex = new int[n];
        tmpNums = new int[n];
        // 初始化原始数据的下标
        for (int i = 0; i < n; i++) {
            index[i] = i;
        }
        // 归并排序
        mergerSort(nums, 0, n - 1);
        //
        List<Integer> list = new ArrayList<>();
        for (int x : ret) {
            list.add(x);
        }
        return list;
    }

    public void mergerSort(int[] nums, int left, int right) {
        // 1.递归出口
        if (left >= right) {
            return ;
        }
        // 2.拆分成左右两部分
        int mid = (left + right) >> 1;
        mergerSort(nums, left, mid);
        mergerSort(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];
        }
    }

 翻转对

题目描述 

 题解

翻转对和逆序对的定义⼤同⼩异,逆序对是前⾯的数要⼤于后⾯的数。⽽翻转对是前⾯的⼀个数要 ⼤于 后⾯某个数的两倍 。因此,我们依旧可以⽤归并排序的思想来解决这个问题。
但是,归并排序,要求的是左边元素大于右边元素;而这道题的条件是,左边元素大于右边元素的两倍,所以, 我们需要在归并之前完成翻转对的统计。

代码实现 

    int[] tmp;
    public int reversePairs(int[] nums) {
        int n = nums.length;
        tmp = new int[n];
        return mergerSort(nums,0,n - 1);
    }
    public int mergerSort(int[] nums,int left,int right) {
        //1.递归结束条件
        if(left >= right) {
            return 0;
        }
        //2.拆分数组分为左右两部分
        int ret = 0;
        int mid = (left + right) >> 1;
        ret += mergerSort(nums,left,mid);
        ret += mergerSort(nums,mid + 1,right);
        //3.计算翻转对(降序)
        int cur1 = left,cur2 = mid + 1,i = 0;
        while(cur1 <= mid) {
            // while(cur2 <= right && nums[cur2] < nums[cur1] / 2.0 ) {
            //     ret += right - cur2 + 1;
            //     cur1++;
            // }
            // if(cur2 > right) {
            //     break;
            // }
            // cur2++;
            while(cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) {
                cur2++;
            }
            if(cur2 > right) {
                break;
            }
            ret += right - cur2 + 1;
            cur1++;
        }
        //4.合并两个有序数组
        cur1 = left;
        cur2 = mid + 1;
        while(cur1 <= mid && cur2 <= right) {
            if(nums[cur1] <= nums[cur2]) {
                tmp[i++] = nums[cur2++];
            }else{
                tmp[i++] = 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];
        }
        return ret;
    }

 

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

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

相关文章

前端Vue组件化实践:打造灵活可维护的地址管理组件

随着前端技术的不断演进&#xff0c;复杂度和开发难度也随之上升。传统的一体化开发模式使得每次小小的修改或功能增加都可能牵一发而动全身&#xff0c;严重影响了开发效率和维护成本。组件化开发作为一种解决方案&#xff0c;通过模块化、独立化的开发方式&#xff0c;实现了…

云计算【第一阶段(29)】远程访问及控制

一、ssh远程管理 1.1、ssh (secureshell)协议 是一种安全通道协议对通信数据进行了加密处理&#xff0c;用于远程管理功能SSH 协议对通信双方的数据传输进行了加密处理&#xff0c;其中包括用户登录时输入的用户口令&#xff0c;建立在应用层和传输层基础上的安全协议。SSH客…

SQL 多变关联使用子查询去重

不去重状态 select a.*,b.recon_amt from free_settlement_first aleft join free_settlement_second b on a.settlement_first_id b.settlement_first_id 有2条数据出现了重复 使用子查询去重 select a.*,b.recon_amt from free_settlement_first aleft join free_settlem…

谈谈软件交互设计

谈谈软件交互设计 交互设计的由来 交互设计(Interaction Design)这一概念,最初是由IDEO创始人之一Bill.Moggridge(莫格里奇)1984年在一次会议上提出。他设计了世界上第一台笔记本电脑Compass,并写作出版了在交互设计领域影响深远的《Designing Interactions》一书,被称…

Azcopy Sync同步Azure文件共享

Azcopy Sync同步Azure文件共享 一、工作原理二、安装 AzCopy在 Windows 上在 Linux 上 三、资源准备1. 创建源和目标 Azure 存储账户2. 创建源和目标文件共享3. 确定路径4. 生成源和目的存储账户的共享访问签名&#xff08;SAS&#xff09;令牌配置权限示例生成的 URL 四、Azco…

AI算法14-套索回归算法Lasso Regression | LR

套索回归算法概述 套索回归算法简介 在统计学和机器学习中&#xff0c;套索回归是一种同时进行特征选择和正则化&#xff08;数学&#xff09;的回归分析方法&#xff0c;旨在增强统计模型的预测准确性和可解释性&#xff0c; 正则化是一种回归的形式&#xff0c;它将系数估…

课程的概述

课程概述 课程类型 课程理论流派 制约课程开发的因素 课程设计的概念及两种模式 课程内容 课程评价 新课程改革理念

前一段时间比较火的刷网课平台源码,带数据库和教程

前一段时间比较火的刷网课平台源码&#xff0c;带数据库和教程。 好在疫情已经结束了&#xff0c;希望今后世上再无网课。 这个代码免费提供给大家学习开发用吧&#xff0c;作为一个php的入门学习案例用用还可以。 使用办法 网站根目录解压 打开nginx.htaccess文件&#x…

社交App iOS审核中的4.3问题:深入分析与解决策略

社交App审核中的4.3问题&#xff1a;深入分析与解决策略 在iOS应用开发和审核过程中&#xff0c;开发者经常会遇到苹果审核4.3问题。这一问题往往涉及应用的设计和内容重复性&#xff0c;导致应用被拒绝上架。为了帮助开发者更好地理解和解决这一问题&#xff0c;本文将对4.3问…

FPGA设计之跨时钟域(CDC)设计篇(1)----亚稳态到底是什么?

1、什么是亚稳态? 在数字电路中,如果数据传输时不满足触发器FF的建立时间要求Tsu和保持时间要求Th,就可能产生亚稳态(Metastability),此时触发器的输出端(Q端)在有效时钟沿之后比较长的一段时间都会处于不确定的状态(在0和1之间振荡),而不是等于数据输入端(D端)的…

集训 Day 3 总结 虚树 + dfs tree + 基环树

虚树 虚树&#xff0c;顾名思义是 只关注原树上的某些 关键点&#xff0c;在保留原树祖孙关系的前提下建出的一棵边数、点数大大减少的树 适用于优化某些在整棵树上进行 d p dp dp、 d f s dfs dfs 的问题 通常是题目中出现多次询问&#xff0c;每次给出树上的一些关键点&a…

taro小程序terser-webpack-plugin插件不生效(vue2版本)

背景 最近在做公司内部的小程序脚手架&#xff0c;为了兼容老项目和旧项目&#xff0c;做了vue2taro,vue3taro两个模板&#xff0c;发现terser-webpack-plugin在vue2和vue3中的使用方式并不相同&#xff0c;同样的配置在vue3webpack5中生效&#xff0c;但是在vue2webpack4中就…

【C++】哈希(散列)表

目录 一、哈希表的基本概念1.哈希的概念2.哈希冲突2.1 哈希函数2.2 哈希冲突的解决办法2.2.1 闭散列2.2.2 开散列 二、哈希表的实现1.闭散列的实现1.1 闭散列的结构1.2 闭散列的插入1.3 闭散列的删除1.4 闭散列的查找 2.开散列的实现2.1 key值不能取模的情况2.2 开散列的结构2.…

编译x-Wrt 全过程

参考自;​​​​​​c编译教程 | All about X-Wrt 需要详细了解的小伙伴还请参看原文 ^-^ 概念&#xff1a; x-wrt&#xff08;基于openwrt深度定制的发行版本&#xff09; 编译系统: ubuntu22.04 注意&#xff1a; 特别注意的是&#xff0c;整个编译过程&#xff0c;都是用 …

线程池笔记

笔记梳理 前言.PHONYC标准库头文件C/C通用或C特有头文件mkdirc_str()snprintfvsnprintfumaskopen函数可变参数列表va_startva_endfunctionalstatic_castpthread_cond_init_threads.emplace_backstd::bindstd::placeholdersThreadPool(const ThreadPool<T> &tp) dele…

springboot系列教程(三):全局异常映射(含源码)

一、异常分类 这里的异常分类从系统处理异常的角度看&#xff0c;主要分类两类&#xff1a;业务异常和系统异常。 1、业务异常 业务异常主要是一些可预见性异常&#xff0c;处理业务异常&#xff0c;用来提示用户的操作&#xff0c;提高系统的可操作性。常见的业务异常提示&…

学会电子期刊制作的必备工具

​随着数字化时代的到来&#xff0c;电子期刊作为一种新型的传播媒介&#xff0c;已经越来越受到大众的青睐。它以环保、便捷、互动性强等特点&#xff0c;逐渐成为传统纸质期刊的重要补充。那么&#xff0c;如何制作一款精美的电子期刊呢&#xff1f;本文将为你介绍学会电子期…

电子电气架构 --- 关于DoIP的一些闲思 上

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

什么? CSS 将支持 if() 函数了?

CSS Working Group 简称 CSSWG, 在近期的会议中决定将 if() 添加到 CSS Values Module Level 5 中。 详情可见&#xff1a;css-meeting-bot 、[css-values] if() function 当我看到这个消息的时候&#xff0c;心中直呼这很逆天了&#xff0c;我们知道像 less 这些 css 这些预…

给 「大模型初学者」 的 LLaMA 3 核心技术剖析

编者按&#xff1a; 本文旨在带领读者深入了解 LLaMA 3 的核心技术 —— 使用 RMSNorm 进行预归一化、SwiGLU 激活函数、旋转编码&#xff08;RoPE&#xff09;和字节对编码&#xff08;BPE&#xff09;算法。RMSNorm 技术让模型能够识别文本中的重点&#xff0c;SwiGLU 激活函…