浅谈归并排序:合并 K 个升序链表的归并解法

在面试中遇到了这道题:如何实现多个升序链表的合并。这是 LeetCode 上的一道原题,题目具体如下:

用归并实现合并 K 个升序链表

LeetCode 23. 合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

这题可以用归并的思想来实现,我们两两链表合并,到最后合成所有的链表。代码如下:

public ListNode mergeKLists(ListNode[] lists) {return merge(lists, 0, lists.length - 1);
}public ListNode merge(ListNode[] lists, int left, int right) {if(left == right) {return lists[left];}if (left > right) {return null;}int mid = (left + right) >> 1;return mergeTwoLists(merge(lists, left, mid), merge(lists, mid + 1, right));
}public ListNode mergeTwoLists(ListNode l1, ListNode l2) {if(l1 == null) {return l2;}if(l2 == null) {return l1;}ListNode head = new ListNode(-1);ListNode cur = head;while(l1 != null && l2 != null) {if(l1.val < l2.val) {cur.next = l1;l1 = l1.next;}else {cur.next = l2;l2 = l2.next;}cur = cur.next;}cur.next = l1 == null ? l2:l1;return head.next;
}

现在我们来回顾一下归并排序的知识

一、归并排序

1. 归并排序的定义

  • 基本思路:借助外部空间,合并两个有序数组/序列,得到更长的数组
  • 算法思想:分而治之

比如对于数组[8,4,5,7,1,3,6,2]的排序:整体的过程是这样:先“分”成小问题,再进行“治”操作

2.归并排序算法代码实现

先来看看归并排序实现一个数组[8,4,5,7,1,3,6,2]的排序,难以理解的是合并相邻有序子序列这块,我们来看 [4,5,7,8] 和[1,2,3,6]这两个已经有序的子序列的合并:图片转自这篇博客图解排序算法(四)之归并排序

public int[] sortArray(int[] nums) {int[] temp = new int[nums.length];merge(nums, 0, nums.length - 1, temp);return nums;
}public void merge(int[] nums, int left, int right, int[] temp) {if(left < right) {int mid = (left + right) >> 1;merge(nums, left, mid, temp);merge(nums, mid + 1, right, temp);mergeSort(nums, left, mid, right, temp);}
}public void mergeSort(int[] nums, int left, int mid, int right, int[] temp) {int i = left;int j = mid + 1;for(int k = left; k <= right; k++) {temp[k] = nums[k];}for(int k = left; k <= right; k++) {//当 i 指针走完时,将 j 指针部分复制到数组中if(i == mid +1) {nums[k] = temp[j];j++;//若 j 指针走完,将 i 指针部分复制到最后数组中}else if(j == right + 1) {nums[k] = temp[i];i++;//这里的 = 是保持排序算法的稳定性,即排序后相等的数据原有顺序不变}else if(temp[i] <= temp[j]) {nums[k] = temp[i];i++;}else {nums[k] = temp[j];j++;}}
}

二、归并排序的一些经典题

1.LeetCode 88. 合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

这道题可以用归并排序的思想来完成,这里就是用的“治”操作:

public void merge(int[] nums1, int m, int[] nums2, int n) {int[] temp = new int[m+n];for(int t = 0; t < m; t++) {temp[t] = nums1[t];}for(int t = m, g = 0; t < m + n & g < n; t++,g++) {temp[t] = nums2[g];}int i = 0;int j = m;for(int k = 0; k < m + n; k++) {if(i == m) {nums1[k] = temp[j];j++;} else if(j == m + n) {nums1[k] = temp[i];i++;} else if(temp[i] <= temp[j]) {nums1[k] = temp[i];i++;} else {nums1[k] = temp[j];j++;}}
}

2.剑指 Offer 51. 数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例 1:

输入: [7,5,6,4]
输出: 5

这题实际上还是利用数组中的元素两两配对比较,也就是分治处理,不过这次是比较大小后的计数。

public int reversePairs(int[] nums) {int len = nums.length;if(len < 2) {return 0;}int[] temp = new int[len];return mergePairs(nums, 0, len - 1, temp);
}public int mergePairs(int[] nums, int left, int right, int[] temp) {//如果是无参void 则可写成 return; 或者不写if(left >= right) {return 0;}int mid = (left + right) >> 1;int leftCount = mergePairs(nums, left, mid, temp);int rightCount = mergePairs(nums, mid + 1, right, temp);int reverseCount = merge(nums, left, mid, right, temp);//最后记得返回三者之和return leftCount + rightCount + reverseCount;
}public int merge(int[] nums, int left, int mid, int right, int[] temp) {int i = left;int j = mid + 1;int count = 0;for(int t = left; t <= right; t++) {temp[t] = nums[t];}for(int k = left; k <= right; k++) {if(i == mid + 1) {nums[k] = temp[j++];} else if(j == right + 1 || temp[i] <= temp[j]) {nums[k] = temp[i++];} else {nums[k] = temp[j++];count += mid - i + 1;}}return count;
}

这里要说一下逆序数的求法:前提是两个序列有序

如果有两个有序序列:

Seq1:3 4 5

Seq2:2 6 8 9

对于序列seq1中的某个数a[i],序列seq2中的某个数a[j]:

  • 如果a[i]<a[j],没有逆序数
  • 如果a[i]>a[j],那么逆序数为seq1 中a[i]后边元素的个数(包括a[i]),即len1 -i+1。

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

给你一个整数数组 nums ,按要求返回一个新数组 counts 。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。

示例 1:

输入:nums = [5,2,6,1]
输出:[2,1,1,0] 
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素

这题和第二题类似,但是这里要解决定位的问题,因为我们的元素节点在归并排序的时候是会移动的,所以需要设置一个索引数组来给这些元素定位。但是求逆序数用的是第二种方法:在前有序数组出列时,计算后有序数组中已经出列的元素个数。

public List<Integer> countSmaller(int[] nums) {int len = nums.length;List<Integer> res = new ArrayList<>();if(len < 2) {res.add(0);return res;}int[] temp = new int[len];int[] indexes = new int[len];int[] result = new int[len];for(int i = 0; i < len; i++) {indexes[i] = i;}merge(nums, 0, len - 1, temp, indexes, result);for(int i = 0; i < len; i++) {res.add(result[i]);}return res;
}public void merge(int[] nums, int left, int right, int[] temp, int[] indexes, int[] result) {if(left >= right) {return;}int mid = (left + right) >> 1;merge(nums,left, mid, temp,indexes,result);merge(nums,mid+1,right,temp,indexes,result);mergeSort(nums,left,right,mid,temp,indexes,result);}
public void mergeSort(int[] nums, int left, int right, int mid, int[] temp, int[] indexes, int[] result) {int i = left;int j = mid + 1;for(int t = left; t <= right; t++) {temp[t] = indexes[t];}for(int k = left; k <= right; k++) {if(i == mid + 1) {indexes[k] = temp[j++];} else if(j == right + 1) {indexes[k] = temp[i++];result[indexes[k]] += right - mid;} else if(nums[temp[i]] <= nums[temp[j]]) {indexes[k] = temp[i++];result[indexes[k]] += j - mid - 1;} else {indexes[k] = temp[j++];}}
}

归并排序的思想很重要,在解决负责问题的分治思想有利于将大问题分解。从而更快的解决问题。

参考资料

https://www.cnblogs.com/chengxiao/p/6194356.html

https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/solution/gui-bing-pai-xu-suo-yin-shu-zu-python-dai-ma-java-/

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

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

相关文章

【学习总结】地面路谱分析

本文仅用于记录自己的学习总结&#xff0c;包括个人理解。不保证内容严格正确。 0. 参考资料 [1] 国标GB/T 703-2005/ISO 8608:1995。[2] Bilibili 路面不平度统计特性[3] 网络参考文档. 网盘&#xff1a;https://pan.baidu.com/s/1ameuQwdOquCrk2V1PIS-Xw?pwdut9m 1. 路面…

Vue框架底层

一、前端框架的由来 1、服务端渲染 sequenceDiagram 浏览器->>服务器: https://www.bilibili.com/ Note right of 服务器: 组装页面(服务端渲染) 服务器->>-浏览器: 完整页面2、前后端分离 sequenceDiagram 浏览器->>服务器: https://www.bilibili.com/ 服务…

Tensorflow2.0笔记 - 基本数据类型,数据类型转换

【TensorFlow2.0】(1) tensor数据类型&#xff0c;类型转换_tensorflow tensor转int-CSDN博客文章浏览阅读1.5w次&#xff0c;点赞8次&#xff0c;收藏28次。各位同学好&#xff0c;今天和大家分享一下TensorFlow2.0中的tensor数据类型&#xff0c;以及各种类型之间的相互转换方…

Spring见解4 基于注解的AOP配置

5.基于注解的AOP配置 5.1.创建工程 5.1.1.pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation…

余震强度预测能力升级,Nature 刊文认证基于神经网络的模型性能优于传统模型

作者&#xff1a;李宝珠 编辑&#xff1a;李玮栋、xixi&#xff0c;三羊 地震的发生涉及诸多变量&#xff0c;「预测」存在挑战&#xff0c;但余震发生次数及强度的预测已取得重大进展。 2023 年 12 月 18 日 23 时 59 分&#xff0c;甘肃省临夏州积石山县发生 6.2 级地震&…

企鹅目标检测数据集VOC格式400张

企鹅&#xff0c;一种可爱而独特的鸟类&#xff0c;以其圆滚滚的身体、黑白相间的羽毛和独特的行走方式而备受人们喜爱。 企鹅是鸟纲、企鹅科的动物&#xff0c;它们生活在南半球&#xff0c;特别是南极地区。企鹅的体型短而肥胖&#xff0c;有着流线型的身体和黑白相间的羽毛…

excel中相同类型的数据归到一起显示

1.选中所有数据 2.开始菜单-排序和筛选-自定义排序 3.选择分类关键字 此处&#xff0c;以属性为例 4.效果 归类后的数据&#xff1a;

Windows打印后打印机没有反应

Windows点打印后打印机没有任何反应 有时候点了打印&#xff0c;打印机却没有任何反应&#xff0c;别人却可以正常打印文件&#xff0c;问题可能出在自己电脑上。 可以试试以下几个方法&#xff0c;不好使不要骂我。 可能是本地打印服务问题 可以尝试在服务中将打印机服务重…

消息队列-RockMQ-批量收发实践

批量收发实战 发送消息是需要网络连接的如果我们单条发送吞吐量可能没有批量发送好。剖来那个发送可以减少网络IO开销&#xff0c;但是也不能一批次发送太多的数据&#xff0c;需要根据每条消息的大小和网络带宽来确定量的数目。 比如网络带宽为可以支持一次性发送8M的数据包&…

复试 || 就业day05(2024.01.08)项目一

文章目录 前言代码模拟梯度下降构建函数与导函数函数的可视化求这个方程的最小值&#xff08;直接求导&#xff09;求方程最小值&#xff08;不令方程导为0&#xff09;【梯度下降】eta0.1eta 0.2eta 50eta 0.01画出eta0.1时的梯度下降x的变化过程 总结 前言 &#x1f4ab;你…

Unity中URP下使用屏幕坐标采样深度图

文章目录 前言一、Unity使用了ComputeScreenPos函数得到屏幕坐标1、 我们来看一下这个函数干了什么2、我们看一下该函数实现该结果的意义 二、在Shader中使用&#xff08;法一&#xff09;1、在Varying结构体中2、在顶点着色器中3、在片元着色器中 三、在Shader中使用&#xff…

玩转Mysql 三(权限管理)

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。 一、用户管理 1、登录MySQL服务器 语法示例&#xff1a; mysql –h hostname|hostIP –P port –u username –p DatabaseName –e "SQL语句" 详细命令…

MYSQL篇--索引高频面试题

mysql索引 1什么是索引&#xff1f; 索引说白了就是一种数据结构&#xff0c;可以协助快速查询数据&#xff0c;以及更新数据库表中的数据&#xff0c;更通俗的来说索引其实就是目录&#xff0c;通过对数据建立索引形成目录&#xff0c;便于去查询数据&#xff0c;而mysql索引…

DES算法(Python实现)

一、具体描述 基于计算机高级语言&#xff08;如C语言&#xff09;实现DES算法 二、名词术语与相关知识 DES算法 DES&#xff08;Data Encryption Standard&#xff09;是一种对称加密算法&#xff0c;被广泛应用于数据加密领域。它使用64位密钥和64位明文&#xff0c;通过…

【⭐AI工具⭐】AI工具导航推荐

目录 零 工具导航&#x1f449;【[AI工具集导航](https://ai-bot.cn/)】&#x1f448;&#x1f449;【[iForAI](https://iforai.com/)】&#x1f448;&#x1f449;【[AInav](https://www.ainav.cn/)】&#x1f448;&#x1f449;【[Navi AI 导航](https://www.naviai.cn/)】&a…

Spring 见解 6 Spring事务控制

Spring事务控制 事务介绍 什么是事务&#xff1f; 当你需要一次执行多条SQL语句时&#xff0c;可以使用事务。通俗一点说&#xff0c;如果这几条SQL语句全部执行成功&#xff0c;则才对数据库进行一次更新&#xff0c;如果有一条SQL语句执行失败&#xff0c;则这几条SQL语句…

leetcode“位运算”——只出现一次的数字

只出现一次的数字i&#xff1a; https://leetcode.cn/problems/single-number/ 给你一个非空整数数组 nums&#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现一次的元素。 class Solution { public:int singleNumber(vector<i…

外汇天眼:放弃对波动的偏爱才能追逐趋势!

无论在熊市还是牛市中&#xff0c;市场上亏损者仍然和别的状态下一样多。 在趋势不明的情况下&#xff0c;我们盼望趋势的来临; 然而趋势真正形成之时&#xff0c;我们却仍然一无所获。 趋势表面上看对我们很重要&#xff0c;然而具体交易时却又难以利用&#xff0c;在具体交易…

优优聚美团外卖代运营,提升销量的秘密武器

随着互联网的飞速发展&#xff0c;外卖平台逐渐成为人们生活中的重要组成部分。作为国内领先的外卖平台&#xff0c;美团外卖吸引了众多商家入驻。然而&#xff0c;如何在竞争激烈的市场中脱颖而出&#xff0c;成为许多商家面临的难题。此时&#xff0c;美团外卖代运营应运而生…

基于动物迁徙算法优化的Elman神经网络数据预测 - 附代码

基于动物迁徙算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于动物迁徙算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于动物迁徙优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&…