LeetCode-4题解 寻找两个正序数组的中位数

news/2024/11/17 17:49:25/

文章目录

  • LeetCode-4[题解] 寻找两个正序数组的中位数
    • 问题描述
    • 样例
    • 解析
      • 1 常规做法
      • 2 二分+K-th Number解法
    • 代码

LeetCode-4[题解] 寻找两个正序数组的中位数

问题描述

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106

样例

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

解析

1 常规做法

常规做法很简单,虽然不满足题意,但是一定是最先想到的:

  1. 先归并排序在直接拎出中位数——时间复杂度 O ( m + n ) O(m+n) O(m+n),空间复杂度 O ( m + n ) O(m+n) O(m+n)
  2. 通过这两个数组可以知道整个数组的最小值/最大值,删除半个数组就可以找到中位数——时间复杂度 O ( m + n ) O(m+n) O(m+n),空间复杂度 O ( 1 ) O(1) O(1)

不满足题意的方法只能说是提供思路

2 二分+K-th Number解法

在题目要求时间复杂度在O(log (m+n)) 后,我们可以先整理出几个思路:

  1. 一定不能做任何遍历的操作:排序、移动指针。
  2. 我们已知的条件有什么?两个数组分别的最小值/最大值、综合后数组的最大值/最小值。
  3. 时间要求在log范围内,我自然就会想到二分。

在整理出这三点后我就陷入了误区,我一直想要用二分探索找到中位数的区间,但怎么都想不出来。~~通过大佬的一点小小启发,~~其实通过引入K-th Number的思想就可以解决这个问题。

  • 思路:假如我们要在 m + n m+n m+n个数中找到中位数,等价于我们要找到第 K = ⌊ m + n + 1 2 ⌋ K = {\lfloor \frac{m+n+1}{2} \rfloor} K=2m+n+1小的数(如果是偶数只要再往后找一个就行)。
  • 倘若我们要找到第 K K K个数,我们应该将前面的 K − 1 K-1 K1个数全部去除。

为了保证时间复杂度合适,自然不能像常规第二种解法一样一个一个删除,而是使用二分的思想。但是这个二分有点难考虑到:通过比较两个数列中第 ⌊ K / 2 ⌋ {\lfloor K / 2 \rfloor} K/2个数的大小,一定能删掉较小数列中的前 ⌊ K / 2 ⌋ {\lfloor K / 2 \rfloor} K/2个数。为什么呢?下面我们分情况考虑一下:

  1. 若第K个数不在其中一个数列的前 ⌊ K / 2 ⌋ {\lfloor K / 2 \rfloor} K/2个中:

  2. 若在前 ⌊ K / 2 ⌋ {\lfloor K / 2 \rfloor} K/2个中:

  3. 当然我们还要考虑一下特殊情况,超出范围则退到最远可比较的位置:

根据上述三种情况,我们一定在删除过程中不会删掉第 K K K个数。并且循环结束后我们可以将情况分成几种:

  1. 只剩一个数列:这个时候当前 K K K可能大于 1 1 1(由于好算,我们在代码直接判出),找到数列删掉的位置后面第 K K K个就好。
  2. 剩两个数列:这个时候 K K K一定等于 1 1 1(否则循环一定继续),找到当前两个数列的前两个数就好了(要注意考虑两个数组剩余数的个数,防止数组越界)。

这样就好啦,总结一下:

  1. 首先通过前 ⌊ K / 2 ⌋ {\lfloor K / 2 \rfloor} K/2个数的理论,删除数组直到 K = 1 K=1 K=1或只剩一条数组。
  2. 然后分类讨论,若只有一条数组就找到目前第 K K K个数,两条数组就找到最小的两个数就好。

代码

总体来说不太优雅,因为最后讨论的情况比较多,别的大佬代码应该会好看许多

class Solution {
public:pair<int, int> my_min(int a, int b1, int b2) {if (a < b1) return {a, b1};else return {b1, min(a, b2)};}pair<int, int> my_min(int a1, int a2, int b1, int b2) {if (a1 < b1) return {a1, min(b1, min(a2, b2))};else return {b1, min(a1, min(a2, b2))};}double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {int len1 = nums1.size(), len2 = nums2.size(); // 两个数组的长度int k = (len1 + len2 + 1) / 2, odd = (len1 + len2) & 1; // 第k小数、奇偶性int l1 = 0, l2 = 0; // 数组左端点while (k > 1 && l1 < len1 && l2 < len2) {int len = k / 2; // 探测的长度// 一边超出限制if (l1 + len > len1 || l2 + len > len2) len = min(len1 - l1, len2 - l2);// 探测if (nums1[l1 + len - 1] < nums2[l2 + len - 1]) l1 += len;else l2 += len;k -= len;}if (l1 == len1) return odd ? nums2[l2+k-1] : 1.0 * (nums2[l2+k-1] + nums2[l2+k]) / 2;if (l2 == len2) return odd ? nums1[l1+k-1] : 1.0 * (nums1[l1+k-1] + nums1[l1+k]) / 2;if (odd) return min(nums1[l1], nums2[l2]);else if (len1 - l1 < 2 && len2 - l2 < 2) {return 1.0 * (nums1[l1] + nums2[l2]) / 2;} else if (len1 - l1 < 2) {pair<int, int> mn = my_min(nums1[l1], nums2[l2], nums2[l2+1]);return 1.0 * (mn.first + mn.second) / 2;} else if (len2 - l2 < 2) {pair<int, int> mn = my_min(nums2[l2], nums1[l1], nums1[l1+1]);return 1.0 * (mn.first + mn.second) / 2;} else {pair<int, int> mn = my_min(nums1[l1], nums1[l1+1], nums2[l2], nums2[l2+1]);return 1.0 * (mn.first + mn.second) / 2;}}
};

http://www.ppmy.cn/news/402960.html

相关文章

DVPP媒体数据处理视频编码问题案例

DVPP&#xff08;Digital Vision Pre-Processing&#xff09;是昇腾AI处理器内置的图像处理单元&#xff0c;通过AscendCL媒体数据处理接口提供强大的媒体处理硬加速能力&#xff0c;主要功能包括图像编解码、视频编解码、图像抠图缩放等。 本期就分享几个关于DVPP视频编码问题…

RedisSon高并发分布式锁实战

Redis高并发分布式锁实战 1.分布式场景下的synchronized失效的问题–用redis实现分布式锁 synchronized是通过monitor实现的jvm级别的锁&#xff0c;如果是分布式系统&#xff0c;跑在不同的虚拟机上的tomcat上&#xff0c;会导致synchronized无法锁住对象 ----------- 需要分…

【Android】Room数据库的使用

简介 Room 是在 SQLite 的基础上推出的 Android 库&#xff0c;它是 Google 官方对数据库操作的推荐方式。使用 Room 可以更方便、高效地操作 SQLite 数据库。 使用 添加依赖 在使用 Room 之前&#xff0c;需要在项目中添加 Room 相关的依赖。在 build.gradle 文件中添加以…

Java 小白 重写toString()方法将如下信息输出在控制台上,红色的苹果被称为“糖心富士”,每500克4.98元,买了2500克“糖心富士”,须支付多少钱

class Apple {public String toString(){return "红色的苹果被称为“糖心富士”&#xff0c;每500克4.98元&#xff0c;买了2500克“糖心富士”&#xff0c;须支付多少钱";}public static void main(String[] args){System.out.println(new Apple());} }

二:物理层

一:物理层基本概念 物理层解决如何在连接各种计算机的传输媒体上传输数据比特流&#xff0c;而不是指具体的传输媒体。 物理层主要任务:确定与传输媒体接口有关的一些特性。 二:数据通信模型 通信的目的是传送消息。 两种数据传输方式&#xff1b;

百度网盘的登陆

登陆页面的具体细节还没有完善。 二维码移动&#xff1a; bool isMoveLight;public const int MOVE_STEP 10;private void P1_MouseEnter(object sender, EventArgs e){timer1.Enabled true;isMoveLight true;}private void Timer1_Tick(object sender, EventArgs e){if ((i…

zzulioj 1146: 吃糖果

题目描述 HOHO&#xff0c;终于从Speakless手上赢走了所有的糖果&#xff0c;是Gardon吃糖果时有个特殊的癖好&#xff0c;就是不喜欢连续两次吃一样的糖果&#xff0c;喜欢先吃一颗A种类的糖果&#xff0c;下一次换一种口味&#xff0c;吃一颗B种类的糖果&#xff0c;这样&…

Kotlin学习 - 数据类与单例类

数据类 在Java代码中&#xff0c;数据类通常需要重写equals()、hashCode()、toString()这几个方法。虽然有快捷方式可以自动生成&#xff0c;但是还是要我们去点击生成下&#xff0c;并且一个简单的数据类就算没有其他复杂逻辑看着也挺繁琐的&#xff0c;代码如下&#xff1a;…