『LeetCode』4. 寻找两个正序数组的中位数 Median of Two Sorted Arrays

题目描述

给定两个大小分别为mn的正序(从小到大)数组nums1nums2
请你找出并返回这两个正序数组的 中位数
算法的时间复杂度应该为O(log(m+n))

示例 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

题目链接https://leetcode.cn/problems/median-of-two-sorted-arrays/

『1』合并两个正序数组

解题思路

  先将两个正序数组合并、再排序找到中位数是最容易想到的一个思路,但显然这是暴力解法,没有利用题目中数组有序这一条件,时间复杂度过高,达到了O((m+n)log(m+n))
  对该解法寻求进一步的优化,可以发现在合并数组时借鉴归并排序的关键步骤能够将时间复杂度降低到O(m+n),合并后再根据数组长度是奇数还是偶数返回中位数即可。

实现代码

class Solution {
// Using the Key Thinking of Merge Sort
// M is the length of nums1, N is the length of nums2
// Time Complexity: O(M+N)
// Space Complexity: O(M+N)
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length, n = nums2.length;
int[] nums = new int[m + n]; // 用于存储合并后的数组
// 数组nums1为空,则直接返回数组nums2的中位数
if (m == 0) {
if (n % 2 == 0) return (nums2[n / 2 - 1] + nums2[n / 2]) / 2.0;
else return nums2[n / 2];
}
// 数组nums2为空,则直接返回数组nums1的中位数
if (n == 0) {
if (m % 2 == 0) return (nums1[m / 2 - 1] + nums1[m / 2]) / 2.0;
else return nums1[m / 2];
}
int count = 0;
int i = 0, j = 0;
while (i < m && j < n) {
if (nums1[i] <= nums2[j]) nums[count++] = nums1[i++];
else nums[count++] = nums2[j++];
}
while (i < m) nums[count++] = nums1[i++];
while (j < n) nums[count++] = nums2[j++];
// 根据数组长度是奇数还是偶数返回中位数
if (count % 2 == 0) return (nums[count / 2 - 1] + nums[count / 2]) / 2.0;
else return nums[count / 2];
}
}

『2』第k小的数

解题思路

  用二分查找可以把时间复杂度降至log级别,由于每进行一次循环就减少k/2个元素,所以时间复杂度是O(log(k)),而k(m+n)/2,因此最终的时间复杂度是O(log(m+n))
具体题解:解法三
参考视频:【LeetCode 每日一题】4. 寻找两个正序数组的中位数 | 手写图解版思路 + 代码讲解

实现代码

class Solution {
// Using the Thinking of Binary Search
// M is the length of nums1, N is the length of nums2
// Time Complexity: O(log(M+N))
// Space Complexity: O(1)
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length, n = nums2.length;
int count = m + n; // 两个数组的总长度
if (count % 2 == 0) {
int left = find(nums1, 0, nums2, 0, count / 2);
int right = find(nums1, 0, nums2, 0, count / 2 + 1);
return (left + right) / 2.0;
} else {
return find(nums1, 0, nums2, 0, count / 2 + 1);
}
}
int find(int[] nums1, int i, int[] nums2, int j, int k) {
// 保证 nums1 的元素更少,方便处理
if (nums1.length - i > nums2.length - j) return find(nums2, j, nums1, i, k);
// 若 nums1 为空,直接取 nums2 的第 k 个元素
if (nums1.length == i) return nums2[j + k - 1];
// k = 1 时,取两数组首元素的最小值
if (k == 1) return Math.min(nums1[i], nums2[j]);
// 分别对应两数组 k/2 的下一个位置的下标
int idx1 = Math.min(nums1.length, i + k / 2);
int idx2 = j + k - k / 2; // 用减法可以避免整除的问题
if (nums1[idx1 - 1] < nums2[idx2 - 1]) {
return find(nums1, idx1, nums2, j, k - (idx1 - i));
} else {
return find(nums1, i, nums2, idx2, k - (idx2 - j));
}
}
}

『3』划分数组法

解题思路:(图片源自参考视频
解题思路

如何判断i

具体题解:解法四
参考视频:LeetCode004-两个有序数组的中位数-最优算法代码讲解

实现代码

class Solution {
// Using the Thinking of Binary Search
// M is the length of nums1, N is the length of nums2
// Time Complexity: O(log(min(M,N))
// Space Complexity: O(1)
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length, n = nums2.length;
if (m > n) return findMedianSortedArrays(nums2, nums1);
int iMin = 0, iMax = m;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;
int j = (m + n + 1) / 2 - i;
// 开始判断 i 的位置,注意边界条件
if (j != 0 && i != m && nums2[j - 1] > nums1[i])
// i 需要增大
iMin = i + 1;
else if (i != 0 && j != n && nums1[i - 1] > nums2[j])
// i 需要减小
iMax = i - 1;
else { // 已确定 i 的位置,接下来需要将边界条件列出来单独考虑
int maxLeft = 0;
if (i == 0) maxLeft = nums2[j - 1];
else if (j == 0) maxLeft = nums1[i - 1];
else maxLeft = Math.max(nums1[i - 1], nums2[j - 1]);
// 总长度若为奇数,不用再考虑右半部分
if ((m + n) % 2 == 1) return maxLeft;
int minRight = 0;
if (i == m) minRight = nums2[j];
else if (j == n) minRight = nums1[i];
else minRight = Math.min(nums2[j], nums1[i]);
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
}
posted @   北岛孤影  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示