【算法总结】归并排序专题(刷题有感)

思考

一定要注意归并排序的含义,思考归并的意义。
主要分为两个步骤:

  1. 拆分
    1. 每次对半分(mid = l +r >> 1)
    2. 输入:raw整块,输出:raw左块+ raw右块
  2. 合并
    1. 每次都要对raw左块raw右块按照某种规则进行合并
    2. 输入:raw左块+ raw右块,输出:raw整块

知道两个步骤之后,可以总结其他的特点:

  1. 拆分阶段和合并阶段是一一对应的,只不过拆分阶段raw的,合并阶段符合一定的性质(对于归并排序则满足有序性)。
  2. 拆分时,段内是无序的,合并时,每一段都是有序的(数值有序性)。合并是针对两个有序的段进行合并,所以会经常用到双指针算法。
  3. 如下图所示,在合并过程中,段内是数值有序,但是相对顺序被破坏了,而两个段之间的相对顺序是不变的6、7、8相对于1、2、3的顺序是不变的,6、7、8依然在1、2、3的左边。

几道题做下来,感觉归并排序类型题的难点在于

  1. 题意的转化:重点就要题意是否支持将原模型分成两半来考虑,即计算左段相对后段的某种性质。
  2. 合并阶段对结果的计算,比如说求逆序对,那么合并的时候如何求逆序对的个数,双重循环遍历?双指针?等等。。。

普通模板

int* merge(int l, int r) {if (l > r) return nullptr;int* tmp = new int[r - l + 1];if (l == r) {tmp[0] = a[l];return tmp;}int mid = l + ((r - l) >> 1);int llen = mid - l + 1, rlen = r - mid;int* la = merge(l, mid);int* ra = merge(mid + 1, r);int i = 0, j = 0, cnt = 0;for (; i < llen && j < rlen; ) {if (la[i] > ra[j]) {tmp[cnt ++] = ra[j ++];} else {tmp[cnt ++] = la[i ++];}}// 上边的循环结束之后,可能存在一个数组还未完全遍历。while(i < llen) tmp[cnt ++] = la[i ++];while(j < rlen) tmp[cnt ++] = ra[j ++];return tmp;
}

Acwing 787. 归并排序

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>using namespace std;const int N = 100000 + 100;
int a[N], tmp[N];void merge(int q[], int l, int r) {if (l >= r) return;int mid = l + ((r - l) >> 1);merge(q, l, mid);merge(q, mid + 1, r);int i = l, j = mid + 1, cnt = 0;for (; i <= mid && j <= r; ) {if (q[i] > q[j]) {tmp[cnt ++] = q[j ++];} else {tmp[cnt ++] = q[i ++];}}while(i <= mid) tmp[cnt ++] = q[i ++];while(j <= r) tmp[cnt ++] = q[j ++];for (int i = l, j = 0; i <= r; i ++, j ++)q[i] = tmp[j];
}int main()
{int n;cin >> n;for (int i = 0; i < n; i ++) cin >> a[i];merge(a, 0, n - 1);for (int i = 0; i < n; i ++) {printf("%d ", a[i]);}printf("\n");
}

Acwing 788. 逆序对的数量

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>using namespace std;typedef long long LL;const int N = 100100;int a[N];
int n;
LL ans;void print_arr(int* arr, int size) {for (int i = 0; i < size; i ++) {printf("%d ", arr[i]);}printf("\n");
}int* merge(int l, int r) {if (l > r) return nullptr;int* tmp = new int[r - l + 1];if (r == l) {tmp[0] = a[l];return tmp;}int mid = l + r >> 1;int* larr = merge(l, mid);int* rarr = merge(mid + 1, r);int llen = (mid - l) + 1, rlen = (r - mid - 1) + 1;// printf("l:\n");// print_arr(larr, llen);// printf("r:\n");// print_arr(rarr, rlen);int i = 0, j = 0, cnt = 0;for (; i < llen && j < rlen;){if (larr[i] > rarr[j]) {ans += (llen - 1 - i) + 1;tmp[cnt ++] = rarr[j ++];} else {tmp[cnt ++] = larr[i ++];}}while(i < llen) tmp[cnt ++] = larr[i ++];while(j < rlen) tmp[cnt ++] = rarr[j ++];// printf("merge\n");// print_arr(tmp, (r - l) + 1);// printf("ans : %d\n", ans);return tmp;
}int main()
{cin >> n;for (int i = 0; i < n; i ++) cin >> a[i];int* h = merge(0, n - 1);// for (int i = 0; i < n; i ++) {//     printf("%d\n", h[i]);// }printf("%lld\n", ans);return 0;
}

Leetcode 493. 翻转对

class Solution {long ans = 0;public int reversePairs(int[] nums) {int n = nums.length;mergeSort(nums, 0, n - 1);return (int)ans;}void mergeSort(int[] nums, int l, int r) {if (l >= r) return;int[] tmp = new int[r - l + 1];int mid = l + ((r - l) >> 1);mergeSort(nums, l, mid);mergeSort(nums, mid + 1, r);int i = l, j = mid + 1, cnt = 0;int base = 0;for (; i <= mid; i ++) {while (j <= r && (long)nums[i] > 2L * nums[j]) {j ++;}ans += (j - (mid + 1));}i = l;j = mid + 1;for (; i <= mid && j <= r; ) {if (nums[i] > nums[j]) tmp[cnt ++] = nums[j ++];else tmp[cnt ++] = nums[i ++];}while(i <= mid) tmp[cnt ++] = nums[i ++];while(j <= r) tmp[cnt ++] = nums[j ++];for (int k = 0; k < cnt; k ++)nums[l + k] = tmp[k];}
}

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

  1. 这个题比较恶心的就是要维护元素原来的位置
class Node {int x;int id;Node(int x, int id) {this.x = x;this.id = id;}
}class Solution {List<Integer> ans = null;public List<Integer> countSmaller(int[] nums) {int n = nums.length;ans = new ArrayList<>(Collections.nCopies(n, 0));Node[] nodes = new Node[n];for (int i = 0; i < n; i ++) {nodes[i] = new Node(nums[i], i);}merge(nodes, 0, n - 1);return ans;}void merge(Node[] nodes, int l, int r) {if (l >= r) return;Node[] tmp = new Node[r - l + 1];int mid = l + ((r - l) >> 1);merge(nodes, l, mid);merge(nodes, mid + 1, r);int i = l, j = mid + 1, cnt = 0;int base = 0;for (; i <= mid;) {if (j == r + 1 || nodes[i].x <= nodes[j].x) {ans.set(nodes[i].id, ans.get(nodes[i].id) + base);tmp[cnt ++] = nodes[i ++];} else {tmp[cnt ++] = nodes[j ++];base ++;}}while (j <= r) tmp[cnt ++] = nodes[j ++];for (int k = 0; k < cnt; k ++)nodes[l + k] = tmp[k];}
}

Leetcode 327. 区间和的个数(前缀和)

  1. 这个题首先要想到利用前缀和将原来的数组进行转换。
  2. 要求的是区间和属于[lower, upper]区间的个数,转化为数学符号之后就是这样: l o w e r < = s u m [ i ] − s u m [ j ] < = u p p e r lower <= sum[i] - sum[j] <= upper lower<=sum[i]sum[j]<=upper
    1. 对于这样的不等式,可以分两步来考虑:
      1. 将连续不等式拆分成单个不等式, s u m [ i ] − s u m [ j ] < = u p p e r sum[i] - sum[j] <= upper sum[i]sum[j]<=upper
      2. 将变量i固定,求另外一个变量的值
    2. 之后对于刚才的连续不等式就可以计算出符合条件的区间[m, n]
  3. 计算符合条件的区间的时机:在合并阶段,i的范围是[mid + 1, r]
class Solution {int lower = 0, upper = 0, ans = 0;public int countRangeSum(int[] nums, int lower, int upper) {this.upper = upper;this.lower = lower;int n = nums.length;long[] pre = new long[n + 1];for (int i = 1; i <= n; i ++)pre[i] = pre[i - 1] + nums[i - 1];merge(pre, 0, n);return ans;}void merge(long[] nums, int l, int r) {if (l >= r) return;long[] tmp = new long[r - l + 1];int mid = l + ((r - l) >> 1);merge(nums, l, mid);merge(nums, mid + 1, r);// 核心代码for (int i = mid + 1, j = l, k = l; i <= r; i ++) {while (j <= mid && nums[i] - nums[j] > upper) j ++;while (k <= mid && nums[i] - nums[k] >= lower) k ++;ans += k - j;}int cnt = 0;for (int i = l, j = mid + 1; i <= mid || j <= r; ) {if (i == mid + 1) tmp[cnt ++] = nums[j ++];else if (j == r + 1) tmp[cnt ++] = nums[i ++];else {if (nums[i] > nums[j])tmp[cnt ++] = nums[j ++];elsetmp[cnt ++] = nums[i ++];}}for (int i = 0; i < cnt; i ++)nums[i + l] = tmp[i];}
}

Acwing 65. 数组中的逆序对

  1. 题意就在题面上,所以直接套模板。
class Solution {int ans = 0;public int inversePairs(int[] nums) {int n = nums.length;mergeSort(nums, 0, n - 1);return ans;}void mergeSort(int[] nums, int l, int r) {if (l >= r) return;int[] tmp = new int[r - l + 1];int mid = l + ((r - l) >> 1);mergeSort(nums, l, mid);mergeSort(nums, mid + 1, r);int i = l, j = mid + 1, cnt = 0;for (; i <= mid && j <= r; ) {if (nums[i] > nums[j]) {ans += (mid - i) + 1;tmp[cnt ++] = nums[j ++];} else {tmp[cnt ++] = nums[i ++];}}while (i <= mid) tmp[cnt ++] = nums[i ++];while (j <= r) tmp[cnt ++] = nums[j ++];for (int k = 0; k < cnt; k ++)nums[l + k] = tmp[k];}}

Acwing 107. 超快速排序

  1. 根据题意可以分析出本题是要求逆序的数量, 那就直接套模板。
import java.util.Scanner;class Main {static long ans = 0;public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = 0;while((n = sc.nextInt()) != 0) {ans = 0;int[] nums = new int[n];    for (int i = 0; i < n; i ++) {nums[i] = sc.nextInt();}mergeSort(nums, 0, n - 1);System.out.println(ans);}}static void mergeSort(int[] nums, int l, int r) {if (l >= r) return;int[] tmp = new int[r - l + 1];int mid = l + ((r - l) >> 1);mergeSort(nums, l, mid);mergeSort(nums, mid + 1, r);int i = l, j = mid + 1, cnt = 0;for (; i <= mid && j <= r; ) {if (nums[i] > nums[j]) {ans += (mid - i) + 1;tmp[cnt ++] = nums[j ++];} else {tmp[cnt ++] = nums[i ++];}}while (i <= mid) tmp[cnt ++] = nums[i ++];while (j <= r) tmp[cnt ++] = nums[j ++];for (int k = 0; k < cnt; k ++)nums[l + k] = tmp[k];}}

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

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

相关文章

《变形监测与数据处理》笔记/期末复习资料(择期补充更新)

变形&#xff1a; 变形是物体在外来因素作用下产生的形状、大小及位置的变化&#xff08;随时间域和空间域的变化&#xff09;&#xff0c;它是自然界普遍存在的现象。 变形体&#xff1a; 一般包括工程建筑物、构筑物、大型机械设备以及其他自然和人工对象等。 变形体和变形…

手把手教你搭建属于自己的快递小程序

在数字化时代&#xff0c;小程序已经成为各行各业连接用户、提供服务、创造价值的重要工具。其中&#xff0c;快递寄件小程序因其实用性和广泛的需求&#xff0c;成为很多企业和开发者关注的焦点。本文将详细介绍如何快速创建快递寄件小程序&#xff0c;以及如何利用它实现盈利…

振南技术干货集:比萨斜塔要倒了,倾斜传感器快来!(1)

注解目录 1、倾斜传感器的那些基础干货 1.1 典型应用场景 &#xff08;危楼、边坡、古建筑都是对倾斜敏感的。&#xff09; 1.2 倾斜传感器的原理 1.2.1 滚珠式倾斜开关 1.2.2 加速度式倾斜传感器 1)直接输出倾角 2)加速度计算倾角 3)倾角精度的提高 &#xff08;如果…

MyBatis-Plus 系列

目录&#xff1a; 一、 Spring Boot 整合 MyBatis Plus 二、MyBatisPlus 多数据源配置 三、MybatisPlus —注解汇总 四、MyBatis Plus—CRUD 接口 五、MyBatis-Plus 条件构造器 六、MyBatis-Plus 代码生成器 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09…

requests 在 Python 3.2 中使用 OAuth 导入失败的问题与解决方案

问题背景 在Python 3.2中&#xff0c;尝试使用Request的OAuth支持时&#xff0c;遇到了OAuth导入失败的问题。以下代码&#xff1a;import requests from requests.auth import OAuth1url https://api.twitter.com/1/account/settings.jsonqueryoauth OAuth1(client_key, cli…

#[量化投资-学习笔记018]Python+TDengine从零开始搭建量化分析平台-正态分布与收益率

正态分布(Normal Distribution)又叫高斯分布、常态分布。通常用来描述随机变量的概率分布。 自然界的数据分布通常是符合正态分布规律的&#xff0c;比如说人的身高、体重。但是非自然界数据就不一定了。尤其是经过人为加工过的数据。 金融领域大量使用正态分布来计算收益率和…

《白帽子讲web安全》笔记

第八章 文件上传漏洞 文件上传漏洞是指用户上传了一个可执行的脚本文件&#xff0c;并通过此脚本文件获得了执行服务器端命令的能力 文件上传后导致的常见安全问题一般有&#xff1a; ❍ 上传文件是Web脚本语言&#xff0c;服务器的Web容器解释并执行了用户上传的脚本&#xf…

【Apache Doris】审计日志插件 | 快速体验

【Apache Doris】审计日志插件 | 快速体验 一、 环境信息1.1 硬件信息1.2 软件信息 二、 审计日志插件介绍三、 快速 体验3.1 AuditLoader 配置3.1.1 下载 Audit Loader 插件3.1.2 解压安装包3.1.3 修改 plugin.conf 3.2 创建库表3.3 初始化3.4 验证 一、 环境信息 1.1 硬件信…

鸿蒙4.0正式版升级机型

官网支持升级机型入口&#xff1a;HarmonyOS 4支持机型 | 华为官网 (huawei.com) 正式版 手机 HUAWEI P60 HUAWEI P60 Pro HUAWEI P60 Art HUAWEI Mate X3 HUAWEI Mate X3 典藏版 HUAWEI Mate 50 HUAWEI Mate 50 Pro HUAWEI Mate 50 RS 保时捷设计 HUAWEI Mate 50E …

vscode文件夹折叠问题

今天发现一个vscode的文件夹显示的问题&#xff0c;首先是这样的&#xff0c;就是我的文件夹里又一个子文件夹&#xff0c;子文件夹里有一些文件&#xff0c;但是我发现无法折叠起这个子文件夹&#xff0c;总是显示全部的文件&#xff0c;这让我备份很难&#xff0c;具体参考 h…

C51--PC通过串口(中断)点亮LED

B4中的&#xff1a;REN允许 / 禁止串行接收控制位 REN 1为允许串行接收状态。 接收数据必须开启。所以SCON&#xff1a;0101 0000 &#xff1b;即0x50 如何知道数据已经接收 RI位&#xff1a;当收到数据后 RI 1&#xff08;由硬件置一&#xff09; 硬件置一后必须用软件…

Docker - MySQL Database is uninitialized and password option is not specified

问题描述 docker run --namemaster -p 3306:3306 -d mysql 2022-11-11 08:03:0500:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.31-1.el8 started. 2022-11-11 08:03:0500:00 [Note] [Entrypoint]: Switching to dedicated user mysql 2022-11-11 08:03…

程序员崩溃瞬间

身为程序员哪一个瞬间让你最奔溃&#xff1f; 有一次我面临一个挑战&#xff0c;由于后续开发的需要&#xff0c;本来不需要同步块运行的部分突然需要进行同步块处理。为了避免重新设计同步块的耗时&#xff0c;我考虑使用一个资源占用标志代替。然而&#xff0c;事情并没有按…

【广州华锐互动】VR居家防火逃生模拟演练增强训练的真实性

VR软件开发公司广州华锐互动在消防培训领域已开发了多款VR产品&#xff0c;今天为大家介绍VR居家防火逃生模拟演练系统&#xff0c;这是一种基于虚拟现实技术的消防教育训练设备&#xff0c;通过模拟真实的火灾场景&#xff0c;让使用者身临其境地体验火灾逃生过程&#xff0c;…

搭建成功simulink-stm32硬件在环开发环境

本次实验所使用的软件版本和硬件平台参数如下&#xff1a; Matlab版本: 2021b STM32硬件平台&#xff1a;YF_STM32_Alpha 1R4(参考自STM32 Nucleo F103RB官方开发板) YF_STM32_Alpha开发板 STM32 Nucleo F103RB 开发板 2.1 STM32硬件支持包下载 读者朋友平时使用的是和谐版M…

【ceph】ceph集群中使用多路径(Multipath)方法

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

在vue项目里面使用index.ts进行统一导出

目录 一、概述 二、具体实践 2.1创建目录 2.2index.ts文件内容展示 2.2在需要的vue文件里面import 2.3vue全代码 三、实际效果 一、概述 一般我们在做项目的时候会发现vue文件里面没有export default 转而替代的是使用同目录下index.ts进行统一导出 好处&#xff1a;能…

Linux的root用户

拥有最大权限的用户名为root su和exit命令 su命令就是用于账户切换的系统命令Switch user 语法&#xff1a;su - [用户名] -符号可选&#xff0c;表示是否在切换用户后加载环境变量&#xff0c;建议带上 参数&#xff1a;用户名&#xff0c;表示要切换的用户&#xff0c;用…

Flutter有状态组件StatefulWidget生命周期

StatefulWidget是Flutter中的一个有状态的组件&#xff0c;它的生命周期相对复杂一些。下面是StatefulWidget的生命周期方法及其调用顺序&#xff1a; 1. createState(): 当StatefulWidget被插入到Widget树中时&#xff0c;会调用createState()方法来创建与之关联的State对象。…