LEETCODE的第四题——寻找两个正序数组的中位数(二分法)
本文主要用于记录刷力扣的题解,想通过深度学习力扣的每一道题目的原理来提高自己。
下面内容为自己学习时的记录,如有错误,欢迎指正🍭
一、题目
注意上面题目中有指定算法的时间复杂度为O(log(m+n))
二、思路分析与方法解析
2.1思路分析
错误思路:
自己先开始想到的是将两个已知的数组合并为一个数组,然后根据合并后的数组长度分奇数偶数两种情况直接输出中位数的值。❌
这就不得不注意题目的要求了,就是之前提到的时间复杂度的问题。
反思一下自己,我觉得还是自己算法练习太少了,没有把摆脱这种低级的思路/(ㄒoㄒ)/~
正确的的思路
寻找两个正序数组的中位数,要求时间复杂度为 O(log(m + n)),有多种解决方法。以下是两种常见的方法:
2.1.1 方法1:二分查找法
这个方法通过在较短的数组上进行二分查找来实现 O(log(min(m, n))) 的时间复杂度。具体步骤如下:
-
确保 nums1 是较短的数组
。如果不是,则交换 nums1 和 nums2,确保 nums1 是较短的数组。 -
在 nums1 上进行
二分查找
,将其分成两部分,左侧部分记为 left1,右侧部分记为 right1。 -
根据左侧部分和右侧部分的长度,
计算
在 nums2 上对应的位置 left2 和 right2。 -
检查
left1、right1、left2、right2 的值,以确定中位数。
具体的实现需要处理边界情况,例如当一个数组为空或者左侧部分的最大值小于右侧部分的最小值时,需要进行适当的调整。这个方法能够保证时间复杂度为 O(log(min(m, n)))。
2.1.2 方法2:划分数组法
这个方法通过将两个数组分成左右两部分,确保左侧部分的元素小于右侧部分的元素,然后根据左侧部分和右侧部分的性质计算中位数。具体步骤如下:
-
使用二分查找法
找到一个位置 i,将两个数组分成左右两部分,分别是 nums1[0:i] 和 nums2[0:j],其中 j = (m + n + 1) / 2 - i。 -
确保左侧部分的最大值小于等于右侧部分的最小值
。 -
根据左侧部分的
最大值
max_of_left 和右侧部分的最小值
min_of_right,计算中位数
。 -
如果总元素个数为
奇数
,中位数就是 max_of_left;如果总元素个数为偶数
,中位数就是 (max_of_left + min_of_right) / 2.0。
这个方法同样能够保证时间复杂度为 O(log(min(m, n)))。
这两种方法都可以达到题目要求的时间复杂度,选择其中一种来实现即可。方法2通常更容易理解和实现。
2.2 方法解析
划分数组法和二分法都是用于解决寻找两个有序数组的中位数问题,但它们的思路和实现方式有一些不同之处:
划分数组法(Median of Two Sorted Arrays):
-
划分为左右两部分:这个方法的核心思想是将两个有序数组分别划分成左右两部分,确保左侧部分的元素小于右侧部分的元素。这个划分是基于一个索引 i,将数组1分为 nums1[0:i] 和数组2分为 nums2[0:j],其中 j = (m + n + 1) / 2 - i。
-
检查左右部分的性质:在划分后,需要确保左侧部分的最大值(max_of_left)小于等于右侧部分的最小值(min_of_right)。这是为了满足中位数的定义。
-
计算中位数:根据左右部分的性质,计算中位数。如果总元素个数为奇数,中位数就是 max_of_left;如果总元素个数为偶数,中位数就是 (max_of_left + min_of_right) / 2.0。
-
时间复杂度:划分数组法的时间复杂度是 O(log(min(m, n))),其中 m 和 n 分别是两个数组的长度。
二分法:
-
二分查找:这个方法主要是在较短的数组上进行二分查找,将其分成左侧部分(left1 和 left2)和右侧部分(right1 和 right2)。这个方法不涉及将数组划分成左右两部分,而是通过二分查找来定位中位数的位置。
-
时间复杂度:二分法的时间复杂度也是 O(log(min(m, n))),因为它主要是在较短的数组上进行二分查找,从而减小了查找的时间复杂度。
总的来说,两种方法都能够达到题目要求的时间复杂度,并且都能有效地寻找两个有序数组的中位数。选择哪种方法取决于个人偏好和实际实现情况。划分数组法通常更容易理解和实现,但二分法也是一个有效的选择,特别是在处理边界情况时可能更简单。
三、代码实现
3.1 二分法实现
c语言
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
if (nums1Size > nums2Size) {
return findMedianSortedArrays(nums2, nums2Size, nums1, nums1Size);
}
int i, j, left = 0, right = nums1Size;
int halfLen = (nums1Size + nums2Size + 1) / 2;
double median = 0.0;
while (left <= right) {
i = (left + right) / 2;
j = halfLen - i;
if (i < nums1Size && nums2[j - 1] > nums1[i]) {
left = i + 1;
} else if (i > 0 && nums1[i - 1] > nums2[j]) {
right = i - 1;
} else {
int maxLeft, minRight;
if (i == 0) {
maxLeft = nums2[j - 1];
} else if (j == 0) {
maxLeft = nums1[i - 1];
} else {
maxLeft = fmax(nums1[i - 1], nums2[j - 1]);
}
if ((nums1Size + nums2Size) % 2 == 1) {
return maxLeft;
}
if (i == nums1Size) {
minRight = nums2[j];
} else if (j == nums2Size) {
minRight = nums1[i];
} else {
minRight = fmin(nums1[i], nums2[j]);
}
return (maxLeft + minRight) / 2.0;
}
}
return median;
}
3.2 划分数组法
C
version 1 内存占用比较大
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
if (nums1Size > nums2Size) {
return findMedianSortedArrays(nums2, nums2Size, nums1, nums1Size);
}
int left = 0, right = nums1Size;
int halfLen = (nums1Size + nums2Size + 1) / 2;
while (left < right) {
int partition1 = left + (right - left) / 2;
int partition2 = halfLen - partition1;
if (nums1[partition1] < nums2[partition2 - 1]) {
left = partition1 + 1;
} else {
right = partition1;
}
}
int partition1 = left;
int partition2 = halfLen - left;
int maxLeft1 = (partition1 == 0) ? INT_MIN : nums1[partition1 - 1];
int minRight1 = (partition1 == nums1Size) ? INT_MAX : nums1[partition1];
int maxLeft2 = (partition2 == 0) ? INT_MIN : nums2[partition2 - 1];
int minRight2 = (partition2 == nums2Size) ? INT_MAX : nums2[partition2];
if ((nums1Size + nums2Size) % 2 == 0) {
return (double)(fmax(maxLeft1, maxLeft2) + fmin(minRight1, minRight2)) / 2.0;
} else {
return (double)fmax(maxLeft1, maxLeft2);
}
}
version 2 查看了内存占用少的大佬的代码(没整明白QAQ)但是还是贴出来
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
int margin_left = 0, margin_right = nums1Size;
int tmp1_left, tmp2_left, tmp1_right, tmp2_right;
if(nums1Size==0){
if(nums2Size&1)
return nums2[nums2Size/2];
else
return (nums2[nums2Size/2-1] + nums2[nums2Size/2])/2.0;
}
if(nums2Size==0){
if(nums1Size&1)
return nums1[nums1Size/2];
else
return (nums1[nums1Size/2-1] + nums1[nums1Size/2])/2.0;
}
while(margin_left <= margin_right){
int x = margin_left + (margin_right - margin_left) / 2;
int y = (nums1Size + nums2Size) / 2 - x;
if(y > nums2Size){
margin_left = x;
continue;
}
if(y < 0){
margin_right = x;
continue;
}
if(x) tmp1_left = nums1[x-1]; else tmp1_left = NULL;
if(x != nums1Size) tmp1_right = nums1[x]; else tmp1_right = NULL;
if(y) tmp2_left = nums2[y-1]; else tmp2_left = NULL;
if(y != nums2Size) tmp2_right = nums2[y]; else tmp2_right = NULL;
if(tmp1_left!=NULL && tmp2_right!=NULL)
if(tmp1_left > tmp2_right){
margin_right = x;
continue;
}
if(tmp2_left!=NULL && tmp1_right!=NULL)
if(tmp2_left > tmp1_right){
margin_left = x+1;
continue;
}
break;
}
if((nums1Size + nums2Size) & 1){
return tmp1_right == NULL? tmp2_right : (tmp2_right == NULL? tmp1_right: (tmp1_right > tmp2_right ? tmp2_right: tmp1_right));
}
else{
int left, right;
left = tmp1_left == NULL? tmp2_left:(tmp2_left==NULL?tmp1_left:(tmp1_left>tmp2_left?tmp1_left:tmp2_left));
right = tmp1_right == NULL? tmp2_right : (tmp2_right == NULL? tmp1_right: (tmp1_right > tmp2_right ? tmp2_right: tmp1_right));
return (left + right) / 2.0;
}
}
python
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
if len(nums1) > len(nums2):
nums1, nums2 = nums2, nums1
nums1Size, nums2Size = len(nums1), len(nums2)
left, right = 0, nums1Size
halfLen = (nums1Size + nums2Size + 1) // 2
while left < right:
partition1 = left + (right - left) // 2
partition2 = halfLen - partition1
if nums1[partition1] < nums2[partition2 - 1]:
left = partition1 + 1
else:
right = partition1
partition1 = left
partition2 = halfLen - left
maxLeft1 = float("-inf") if partition1 == 0 else nums1[partition1 - 1]
minRight1 = float("inf") if partition1 == nums1Size else nums1[partition1]
maxLeft2 = float("-inf") if partition2 == 0 else nums2[partition2 - 1]
minRight2 = float("inf") if partition2 == nums2Size else nums2[partition2]
if (nums1Size + nums2Size) % 2 == 0:
return (max(maxLeft1, maxLeft2) + min(minRight1, minRight2)) / 2.0
else:
return max(maxLeft1, maxLeft2)
一般方法(虽然在时间复杂度上不符合,但是运行速度非常快)
int min(int x, int y)
{
if (x < y){
return x;
} else {
return y;
}
}
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size)
{
int i,j,k;
i = 0;
j = 0;
k = 0;
int * num = malloc(sizeof(int) * (nums1Size + nums2Size));
while (i < nums1Size && j < nums2Size) {
//取两个数组中较小的数放入新数组中
if(nums1[i] < nums2[j]){
num[k++] = nums1[i++];
} else {
num[k++] = nums2[j++];
}
}
//两个数组那个还有剩的
if(i >= nums1Size){
for(; j < nums2Size; j++){
num[k++] = nums2[j];
}
} else {
for(; i < nums1Size; i++){
num[k++] = nums1[i];
}
}
if((nums1Size + nums2Size) % 2 == 0){
return (num[(nums1Size + nums2Size) / 2] + num[(nums1Size + nums2Size) / 2 - 1]) / 2.0;
} else {
return num[(nums1Size + nums2Size) / 2];
}
}
写在最后
本文只为个人学习记录★,°:.☆( ̄▽ ̄)/$:.°★ 。