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

思考

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

  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…

#[量化投资-学习笔记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 硬件信…

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; 硬件置一后必须用软件…

【广州华锐互动】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…

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

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

Flutter有状态组件StatefulWidget生命周期

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

【开源】基于Vue.js的校园二手交易系统的设计和实现

目录 一、摘要1.1 项目介绍1.2 项目详细录屏 二、功能模块2.1 数据中心模块2.2 二手商品档案管理模块2.3 商品预约管理模块2.4 商品预定管理模块2.5 商品留言板管理模块2.6 商品资讯管理模块 三、实体类设计3.1 用户表3.2 二手商品表3.3 商品预约表3.4 商品预定表3.5 留言表3.6…

如何正确使用GPT工具

引言 在快速发展的数字时代&#xff0c;人工智能&#xff08;AI&#xff09;已成为科研领域的一个不可或缺的工具。特别是像ChatGPT这样的AI聊天机器人&#xff0c;它通过高效的语言模型和深度学习算法&#xff0c;为科研工作者提供了前所未有的辅助。从文献搜索到数据分析&…

sqlmap requires ‘python-pymysql‘ third-party library

使用sqlmap进行udf提权报错&#xff1a; [14:06:04] [CRITICAL] sqlmap requires python-pymysql third-party library in order to directly connect to the DBMS MySQL. You can download it from https://github.com/PyMySQL/PyMySQL. Alternative is to use a package pyt…

asp.net core mvc之 过滤器

过滤器允许我们在Action执行之前和执行之后去执行一下业务代码 一、过滤器的作用域 1、全局过滤器&#xff0c; 在Startup.cs文件中注册 2、控制器过滤器&#xff0c; 在控制器类上面使用标注 3、action过滤器 二、全局过滤器使用 1、在 core 目录&#xff0c;添加 TestA…

MySQL(18):MySQL8.0的其它新特性

MySQL从5.7版本直接跳跃发布了8.0版本。 MySQL8.0 新增特征 1.更简便的NoSQL支持。 NoSQL泛指非关系型数据库和数据存储。随着互联网平台的规模飞速发展&#xff0c;传统的关系型数据库已经越来越不能满足需求。从5.6版本开始&#xff0c;MySQL就开始支持简单的NoSQL存储功能…

Servlet 常见的API

文章目录 写在前面Smart Tomcat 插件Servlet 中常见的API1. HttpServletinit 方法destroy 方法service 方法Servlet 的生命周期 使用 postman 构造请求使用 ajax 构造请求2. HttpServletRequest3. 前端给后端传参1). GET, query string2). POST, form3). json 4. HttpServletRe…

11_聚类算法

文章目录 1 聚类1.1 什么是聚类1.2 相似度/距离公式1.3 聚类的思想 2 K-means算法2.1 K-means算法步骤2.2 K-means算法思考2.3 K-means算法优缺点 3 解决K-Means算法对初始簇心比较敏感的问题3.1 二分K-Means算法3.2 K-Means算法3.3 K-Means||算法3.4 Canopy算法3.4.1 Canopy算…