#include <iostream> using namespace std; #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) const int N = 1e8+10; double findMedianSortedArrays(int nums1[], int nums2[],int n,int m) { /************Begin**********/ /************End**********/ } int main() { int m,n,nums1[N],nums2[N]; cin>>n>>m; for(int i = 0; i < n; i ++)cin>>nums1[i]; for(int i = 0; i < m; i ++)cin>>nums2[i]; double ret = findMedianSortedArrays(nums1, nums2,n,m); cout<<ret<<endl; return 0; } 本关任务:给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数 。算法的时间复杂度应该为 O(log (m+n)) 。 示例 1: 输入:nums1 = [1,3], nums2 = [2] 输出:2.00000 解释:合并数组 = [1,2,3] ,中位数 2
时间: 2025-04-10 12:46:23 浏览: 22
<think>嗯,我现在得解决这个问题:在两个有序数组中找到中位数,时间复杂度必须是O(log(m+n))。首先,我得理解题目要求。中位数就是合并后的数组中间位置的数,如果总长度是奇数,就是中间那个数;如果是偶数,就是中间两个数的平均值。那怎么高效地找到这个中位数呢?
直接合并两个数组然后找中位数的话,时间复杂度是O(m+n),但题目要求的是O(log(m+n)),所以这种方法不行。那必须得用更高效的方法,比如二分查找之类的。
我记得有一个方法叫做寻找第k小的元素。这里的k是中位数的位置。比如,总共有m+n个数,如果总长度是奇数,k就是(m+n+1)/2;如果是偶数,就是两个中间位置的数,比如k1=(m+n)/2和k2=k1+1,然后取平均。那问题就转化为如何在不合并数组的情况下找到第k小的元素。
那如何高效地找到第k小的元素呢?因为两个数组都是有序的,可以每次排除掉一部分元素。例如,比较两个数组的第k/2个元素,较小的那个数组的前k/2个元素都不可能是第k小的元素,所以可以排除掉,然后在剩下的部分中继续查找。这应该是一个二分的过程,每次将k减半,直到k为1或者某个数组被排除完。
具体来说,假设现在要找第k小的元素,分别在nums1和nums2中找第k/2个元素。如果nums1[k/2-1]小于nums2[k/2-1],那么nums1的前k/2个元素都不可能是第k小的元素,可以排除掉,然后将问题转化为在剩下的nums1和nums2中找到第k - k/2小的元素。这个过程递归进行,直到找到结果。
不过,具体实现的时候需要注意边界条件。例如,当某个数组的长度不足k/2时,如何处理?这时候可能需要取该数组的最后一个元素,或者另一个数组的元素直接作为候选。另外,当k减到1的时候,只需要比较两个数组当前的首元素,取较小的那个。
那现在,如何将这个思路转化为代码呢?
首先,我们需要处理两个数组的边界情况。比如,其中一个数组可能已经被完全排除,这时候只需要在另一个数组中直接找第k小的元素即可。反之,如果两个数组都还有元素,就继续比较。
举个例子,假设nums1的有效起始位置是i,nums2是j,现在要找第k小的元素。比较nums1[i + k/2 -1]和nums2[j + k/2 -1],如果nums1的元素较小,那么排除掉i到i + k/2 -1的元素,i移动到i + k/2,k减去k/2。如果某个数组剩下的长度不足k/2,比如nums1剩下的元素不足k/2个,那么比较nums2的j + k/2 -1的位置是否越界,这时候应该取nums1的最后一个元素和nums2的相应位置比较。
但具体代码怎么处理这些情况呢?
另外,这里需要处理奇数和偶数的情况。比如,当m+n是奇数时,中位数是第(m+n+1)/2小的元素;如果是偶数,则是第(m+n)/2和第(m+n)/2+1小的元素的平均值。
因此,函数的大致步骤可能是这样的:
1. 计算总长度,如果是奇数,调用findKth函数找到中间的那个数;如果是偶数,找到中间两个数的平均值。
2. findKth函数的实现,每次排除k/2的元素,直到k=1或者其中一个数组被排除完。
接下来,我需要考虑如何实现findKth函数。假设函数参数是nums1的起始位置i,nums2的起始位置j,以及要找的第k小的元素。但原题中的函数参数是两个数组和它们的长度n和m,所以在函数内部可能需要递归或者循环处理。
不过,为了达到O(log(m+n))的时间复杂度,必须确保每次递归调用都能将k减少一半,这样总共有log(k)次操作,每次操作的时间是常数,所以总时间是对数级别的。
在实现findKth的时候,需要注意数组越界的问题。例如,当nums1剩下的元素数量比k/2少的时候,我们只能取剩下的所有元素。这个时候,另一个数组中的元素可能更大,所以此时如果nums1中的剩余元素都小于nums2的当前比较元素,那么就可以排除nums1中的这些元素,然后继续处理剩下的部分。
例如,假设nums1的长度是m,nums2的长度是n。要找第k小的元素。i和j是当前数组的起始位置。如果nums1剩下的元素数目是len1 = m - i,nums2剩下的是len2 = n - j。假设k=5,那么k/2=2。这时候比较nums1[i+1]和nums2[j+1]。但如果其中一个数组剩下的元素不足2个,比如len1=1,这时候我们只能比较nums1[i]和nums2[j+1]。如果nums1[i]较小,那么可以排除nums1剩下的所有元素(也就是1个元素),然后在剩下的两个数组中找第k-1=4小的元素。
所以,在代码中,每次我们需要计算两个数组可以取出的元素数量,取其中的较小值,即step = min(k/2, len1, len2)。或者,可能应该取每个数组中可能取的步长,比如step1 = min(k/2, len1),step2 = min(k/2, len2)。然后比较这两个步长对应的元素,排除较小的那一部分。
或者,正确的做法是,每个数组取k/2个元素进行比较。如果其中一个数组剩下的元素不足k/2,那么只能取剩下的所有元素。这时候,如果该数组的这部分元素的最大值(即最后一个元素)小于另一个数组的对应位置的元素,那么可以排除这部分元素,然后k减去这部分元素的数量。
例如,假设k=5,我们想取每个数组中的前2个元素进行比较。如果数组1剩下的元素不足2个,比如只有1个,那么step1=1。这时候比较数组1的这个元素(即i+step1-1)和数组2的j+step2-1(step2=2)的元素。如果数组1的元素小,那么可以排除数组1的这1个元素,k减1,然后在剩下的数组中找第4小的元素。
所以,在每次比较时,step1 = min(k/2, len1), step2 = min(k/2, len2)。然后比较nums1[i + step1 - 1]和nums2[j + step2 - 1]。如果nums1的元素小,则排除step1个元素,i += step1,k -= step1。反之,排除step2个元素,j += step2,k -= step2。
这样处理的话,每次都能减少k的值,直到k=1时,比较两个数组剩下的第一个元素,取较小的即可。
那现在,如何将这个过程用循环或递归的方式实现呢?
可能用循环更高效,避免递归的栈开销。所以可以写一个循环,每次根据step1和step2的情况,排除一部分元素,直到k=1或者其中一个数组被排除完。
接下来,编写findKth函数的大致步骤:
函数参数:nums1数组,起始位置i,数组长度n;nums2数组,起始位置j,数组长度m;要找的第k小的元素。
但原题中的函数参数是int nums1[], int nums2[], int n, int m。所以可能需要在这个函数的内部实现时,用循环处理。
或者,可以写一个辅助函数,参数包括当前两个数组的起始位置和剩余长度,然后递归处理。但可能更高效的是用循环。
具体来说,在findMedianSortedArrays函数中,首先计算总长度total = n + m。如果是奇数,中位数是findKth(total / 2 + 1),如果是偶数,则是(findKth(total/2) + findKth(total/2 +1))/2.0。
然后,findKth函数的实现逻辑:
初始化i=0,j=0,剩下的数组长度分别为n和m。k是要找的第k小。
循环直到其中一个数组被排除完或者k=1:
比较剩下的数组长度,如果其中一个长度是0,那么直接返回另一个数组的第k-1个元素。
否则,取step1 = min(k/2, n - i),step2 = min(k/2, m - j)。或者,应该取step的值为k/2,但不超过每个数组的剩余长度。例如,对于nums1来说,可以取step1 = min(k/2, len1),其中len1 = n - i。同理step2 = min(k/2, len2)。
比较nums1[i + step1 -1]和nums2[j + step2 -1]。如果nums1的元素小,那么排除step1个元素,i += step1,k -= step1。否则,排除step2个元素,j += step2,k -= step2。
当k=1的时候,比较两个数组当前剩下的第一个元素,取较小的那个。
但是,需要注意数组是否已经超出范围的情况。例如,如果i >=n,那么只能从nums2中取,反之亦然。
那现在,编写这个循环的逻辑:
int findKth(int nums1[], int n, int nums2[], int m, int k) {
int i = 0, j = 0;
while (true) {
if (i == n) {
return nums2[j + k -1];
}
if (j == m) {
return nums1[i + k -1];
}
if (k ==1) {
return min(nums1[i], nums2[j]);
}
int step1 = min(k/2, n -i);
int step2 = min(k/2, m -j);
int val1 = nums1[i + step1 -1];
int val2 = nums2[j + step2 -1];
if (val1 <= val2) {
i += step1;
k -= step1;
} else {
j += step2;
k -= step2;
}
}
}
这样,这个函数可以找到第k小的元素。不过,这里假设数组都是0-based的索引。那么,在合并后的数组中,第k小的元素的索引是k-1。
然后,在findMedianSortedArrays函数中,根据总长度的奇偶性,调用这个函数两次(如果是偶数的话)。
但是原题中的函数接口是double findMedianSortedArrays(int nums1[], int nums2[], int n, int m)。所以需要将这个过程整合进去。
不过,原题中的函数体部分需要补全,也就是在/************Begin**********/和/************End**********/之间填写代码。
因此,整个函数的实现步骤:
1. 计算总长度total = n + m.
2. 如果total是奇数,调用getKth找到第(total+1)/2小的元素,返回double。
3. 如果total是偶数,调用getKth找到第total/2和第total/2 +1小的元素,返回它们的平均值。
而getKth函数的实现方式如上所述。
但如何将getKth函数整合到现有的函数中呢?或者是否可以在findMedianSortedArrays内部实现?
是的,可以在findMedianSortedArrays函数中实现。例如:
double findMedianSortedArrays(int nums1[], int nums2[],int n,int m) {
int total = n + m;
if (total % 2 == 1) {
return getKth(nums1, n, nums2, m, total / 2 + 1);
} else {
int a = getKth(nums1, n, nums2, m, total / 2);
int b = getKth(nums1, n, nums2, m, total / 2 +1);
return (a + b) / 2.0;
}
}
然后,编写getKth函数。不过,由于C++函数不能嵌套,所以需要在外部定义这个函数。或者,可以将getKth作为辅助函数,在findMedianSortedArrays内部处理。或者,直接在findMedianSortedArrays内部处理。
或者,可以将getKth的逻辑直接写在循环中,无需单独的函数。例如,在findMedianSortedArrays函数中处理。
不过,为了代码结构清晰,可能需要一个辅助函数。但原题中的函数定义可能不允许添加其他函数。所以可能需要将整个逻辑整合到现有函数中。
或者,将getKth的逻辑直接实现在findMedianSortedArrays函数内部,通过循环处理。
那现在,如何实现这个getKth的逻辑呢?
比如,在findMedianSortedArrays函数内部:
double findMedianSortedArrays(...) {
int total = n + m;
int k = (total +1)/2;
// 如果是奇数,返回第k小的数,否则返回第k和k+1的平均
int i=0, j=0;
// 这里需要处理两种情况,所以可能需要两次遍历?
// 或者,可以写一个循环,找到第k小的元素,同时处理两种情况?
// 或许更直接的方法是,对于偶数的情况,分别找两次,然后取平均。
if (total %2 ==1) {
int median = getKthElement(nums1, n, nums2, m, k);
return median;
} else {
int median1 = getKthElement(nums1, n, nums2, m, total/2);
int median2 = getKthElement(nums1, n, nums2, m, total/2 +1);
return (median1 + median2)/2.0;
}
}
所以,这里的getKthElement函数需要被实现。那如何在C++中实现这个函数?
或者,可以写一个辅助函数,或者在findMedianSortedArrays内部使用lambda函数,但可能原题中的环境不允许。
所以,可能得将整个逻辑写成一个循环。
现在,重点实现getKthElement函数。例如:
int getKthElement(int nums1[], int n, int nums2[], int m, int k) {
int i=0, j=0;
while(true) {
if (i ==n) {
return nums2[j +k-1];
}
if (j ==m) {
return nums1[i +k-1];
}
if (k ==1) {
return min(nums1[i], nums2[j]);
}
int step1 = min(k/2, n -i);
int step2 = min(k/2, m -j);
int val1 = nums1[i + step1 -1];
int val2 = nums2[j + step2 -1];
if (val1 <= val2) {
i += step1;
k -= step1;
} else {
j += step2;
k -= step2;
}
}
}
这样,这个函数可以找到第k小的元素。然后,在findMedianSortedArrays函数中调用即可。
但是,原题中的函数参数是int nums1[], int nums2[], int n, int m,而getKthElement的参数是同样的,所以可以直接调用。
现在,回到原题的代码框架。用户提供的代码中,需要补全findMedianSortedArrays函数。
因此,在给定的代码框架中,需要补全该函数,并实现上述逻辑。
现在,代码的步骤:
1. 计算总长度total =n +m.
2. 如果是奇数,求第k=(total+1)/2小的元素;否则,求第k=total/2和k+1的两个元素的平均值。
3. 编写getKthElement函数,或者将其逻辑直接嵌入到findMedianSortedArrays中。
但原题中的函数需要返回double,而getKthElement返回int。由于数组中的元素是整数,中位数可能是小数,例如当两个中间数的和为奇数时,例如2和3的平均是2.5。
所以,在计算偶数情况时,需要将两个整数相加后除以2.0,转换为double。
现在,考虑可能的错误情况,例如数组越界,或者k的取值是否正确。
例如,当n=0或者m=0的情况。例如,nums1是空数组,那么中位数就是nums2的中位数。这种情况下,getKthElement的处理是否正常?
比如,当i==n时,直接返回nums2[j +k -1]。当nums1为空时,i=0,n=0,所以在第一次循环就会进入i==n的情况,返回nums2的k-1位置。这是正确的。
现在,测试示例1:
输入:nums1 = [1,3], nums2 = [2], n=2, m=1。总长度3,奇数。k=2。中位数是第二个元素。
在getKthElement中:
初始i=0,j=0,k=2.
进入循环:
i !=n,j!=m,k>1.
step1 = min(1, 2-0)=1.
step2 = min(1,1-0)=1.
val1 = nums1[0+1-1] = nums1[0]=1.
val2 = nums2[0+1-1]=nums2[0]=2.
比较1<=2,所以i +=1 →i=1,k=2-1=1.
再次循环:
i=1 <2,j=0<1.
k=1.
返回min(nums1[1], nums2[0]) →min(3,2)=2。正确。
所以函数返回2,转换为double,结果为2.0。符合示例。
另一个测试案例,比如nums1和nums2各有两个元素,总长度4,求中位数是第二和第三个元素的平均值。
例如,nums1=[1,2], nums2=[3,4].总长度4,中位数是(2+3)/2=2.5.
调用getKthElement两次:k=2和k=3.
对于k=2:
初始i=0,j=0,k=2.
step1=1, step2=1.
val1=1, val2=3 →1<=3,i+=1,k=1.
k=1时,比较nums1[1]=2和nums2[0]=3 →返回2.
对于k=3:
i=0,j=0,k=3.
step1=1, step2=1.
val1=1 <3 →i+=1, k=2.
现在i=1,j=0,k=2.
step1= min(1, 2-1=1), step2= min(1,4-0=1).
val1=2 <3 →i+=1, k=1.
此时i=2(等于n=2),所以返回nums2[j +k-1] = nums2[0+1-1] = nums2[0]=3.
所以平均值是(2+3)/2.0=2.5,正确。
那这说明逻辑是正确的。
现在,将这些逻辑转化为C++代码。
首先,在给定的函数框架中:
double findMedianSortedArrays(int nums1[], int nums2[],int n,int m) {
/************Begin**********/
// 这里填写代码
/************End**********/
}
需要实现上述逻辑。
因此,代码的大致结构如下:
在函数内部,首先计算总长度total,然后根据奇偶性调用getKthElement两次或者一次。
但需要实现getKthElement函数。然而,C++不允许在函数内部定义另一个函数,所以必须将getKthElement作为辅助函数,或者在同一个函数内部处理。
但原题中的代码框架中没有声明这个辅助函数,所以必须将逻辑内联到findMedianSortedArrays函数中。
因此,可以将getKthElement的逻辑实现为一个循环,直接放在findMedianSortedArrays函数内部。
例如:
double findMedianSortedArrays(int nums1[], int nums2[],int n,int m) {
int total = n + m;
if (total % 2 == 1) {
int k = (total + 1) / 2;
return findKth(nums1, n, nums2, m, k);
} else {
int k1 = total / 2;
int k2 = k1 + 1;
int median1 = findKth(nums1, n, nums2, m, k1);
int median2 = findKth(nums1, n, nums2, m, k2);
return (median1 + median2) / 2.0;
}
}
而findKth函数的实现:
int findKth(int nums1[], int n, int nums2[], int m, int k) {
int i = 0, j = 0;
while (true) {
if (i == n) {
return nums2[j + k - 1];
}
if (j == m) {
return nums1[i + k - 1];
}
if (k == 1) {
return min(nums1[i], nums2[j]);
}
int step1 = min(k / 2, n - i);
int step2 = min(k / 2, m - j);
int val1 = nums1[i + step1 - 1];
int val2 = nums2[j + step2 - 1];
if (val1 <= val2) {
i += step1;
k -= step1;
} else {
j += step2;
k -= step2;
}
}
}
但是,原题中的代码框架不允许添加额外的函数,所以必须将findKth的逻辑内联到findMedianSortedArrays函数中。或者,是否可以将findKth作为辅助函数放在外部?
原题中的代码框架中,已经存在函数findMedianSortedArrays的定义,但并没有声明其他函数。所以,如果将findKth函数放在该函数之后,可能无法被调用。或者,可以将其放在该函数的前面。
但是,根据C++的规则,函数必须先声明后使用。因此,在现有的代码框架中,findMedianSortedArrays函数是需要在main函数中被调用的,所以必须在main之前声明。但在这个框架中,main函数在后面,所以可以将findKth函数放在findMedianSortedArrays的上方。
或者,可以将其作为静态函数,或者在findMedianSortedArrays之前声明。
因此,在用户提供的代码框架中,需要在findMedianSortedArrays函数之前定义findKth函数。
所以,最终的代码可能如下:
#include <iostream>
using namespace std;
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
const int N = 1e8+10;
// 辅助函数
int findKth(int nums1[], int n, int nums2[], int m, int k) {
int i = 0, j = 0;
while (true) {
if (i == n) {
return nums2[j + k - 1];
}
if (j == m) {
return nums1[i + k - 1];
}
if (k == 1) {
return min(nums1[i], nums2[j]);
}
int step1 = min(k / 2, n - i);
int step2 = min(k / 2, m - j);
int val1 = nums1[i + step1 - 1];
int val2 = nums2[j + step2 - 1];
if (val1 <= val2) {
i += step1;
k -= step1;
} else {
j += step2;
k -= step2;
}
}
}
double findMedianSortedArrays(int nums1[], int nums2[],int n,int m) {
int total = n + m;
if (total % 2 == 1) {
return findKth(nums1, n, nums2, m, (total + 1)/2);
} else {
int a = findKth(nums1, n, nums2, m, total / 2);
int b = findKth(nums1, n, nums2, m, total / 2 + 1);
return (a + b) / 2.0;
}
}
int main()
{
// ... 原有的main函数
}
这样,在findMedianSortedArrays函数之前定义了findKth函数,就能正确调用了。但是原题的代码框架中的函数定义部分是否允许这样?
在用户提供的代码中,findMedianSortedArrays的函数体需要补全,所以需要将代码写在Begin和End之间。但是,C++不允许在函数体内定义另一个函数。所以,上述方法可能无法在给定的代码框架中实现,因为用户提供的代码框架可能不允许在函数外添加其他函数。
因此,这种情况下,必须将findKth的逻辑直接内联到findMedianSortedArrays函数中,而无法使用辅助函数。因此,必须将两个逻辑合并。
所以,如何处理?
可能需要在findMedianSortedArrays函数中,处理两种情况,并在同一个循环中处理两次寻找的情况?
或者,可以重写代码,将findKth的逻辑直接写在findMedianSortedArrays函数内部,针对两种情况分别处理。
这可能比较复杂,但可能可行。
例如,在findMedianSortedArrays中,根据奇偶性,进行两次循环操作?
或者,可以编写一个通用的函数,返回第k小的元素,而无需辅助函数。
例如,将findKth的逻辑直接写在findMedianSortedArrays函数内部。
例如:
double findMedianSortedArrays(int nums1[], int nums2[], int n, int m) {
int total = n + m;
int k1 = (total + 1) / 2;
int k2 = (total + 2) / 2; // 当total为奇数时,k1和k2相等,否则相差1
// 寻找第k1和第k2小的元素,然后求平均
int median1 = 0, median2 = 0;
// 寻找第k1小的元素
int i=0, j=0, k=k1;
while (true) {
if (i == n) {
median1 = nums2[j + k -1];
break;
}
if (j == m) {
median1 = nums1[i + k -1];
break;
}
if (k ==1) {
median1 = min(nums1[i], nums2[j]);
break;
}
int step1 = min(k/2, n -i);
int step2 = min(k/2, m -j);
int val1 = nums1[i + step1 -1];
int val2 = nums2[j + step2 -1];
if (val1 <= val2) {
i += step1;
k -= step1;
} else {
j += step2;
k -= step2;
}
}
// 如果是偶数,还需要找到k2小的元素
if (total %2 ==0) {
// 重新初始化i,j,k
i=0; j=0; k=k2;
while (true) {
if (i ==n) {
median2 = nums2[j +k -1];
break;
}
if (j ==m) {
median2 = nums1[i +k -1];
break;
}
if (k ==1) {
median2 = min(nums1[i], nums2[j]);
break;
}
int step1 = min(k/2, n -i);
int step2 = min(k/2, m -j);
int val1 = nums1[i + step1 -1];
int val2 = nums2[j + step2 -1];
if (val1 <= val2) {
i += step1;
k -= step1;
} else {
j += step2;
k -= step2;
}
}
return (median1 + median2)/2.0;
} else {
return median1;
}
}
这样,将两次寻找第k小的元素的逻辑都写在同一个函数中。但是,这样会导致代码重复,因为两次循环的逻辑是相同的。不过,这样可能在没有辅助函数的情况下解决问题。
但是,这可能会有问题,因为在第一次循环中i和j的值已经被修改,所以第二次循环需要重新初始化i=0,j=0。这可能没问题,因为每次寻找都是独立进行的。例如,第一次找第k1小的元素,第二次找第k2小的元素,每次都是从头开始的。
这是否正确?例如,原来的findKth函数每次调用都是独立的,i和j的初始值都是0。因此,在两次调用中,两次寻找的过程是相互独立的。因此,在两次循环中,必须重新将i和j初始化为0,k初始化为k2。
所以,上述代码是正确的。例如,在第一次循环之后,i和j的值可能被修改,但在第二次循环时,重新将i=0,j=0,k=k2,所以第二次循环是正确的。
这样,即使两次循环都独立执行,也能正确找到第k2小的元素。
例如,在示例1中,第一次循环找到第2小的元素,而第二次可能不需要执行,因为total是奇数。所以返回median1。
在偶数的情况下,例如总长度4,两次循环分别寻找第2和第3小的元素,每次循环都是独立从头开始。
例如,对于nums1=[1,3], nums2=[2,4].总长度4,中位数是(2+3)/2=2.5.
第一次循环找k=2:
i=0,j=0.
step1=1, step2=1.
val1=1, val2=2 →i becomes 1, k=1.
k=1,比较nums1[1]=3和nums2[0]=2 →取2,median1=2.
然后,第二次循环找k=3:
i=0,j=0.
step1=1, step2=1.
val1=1 <2 →i=1, k=2.
然后,step1=1 (k=2/2=1), step2=1.
val1=3,val2=2 →比较3>2,所以j=1, k=1.
k=1,比较nums1[1]=3和nums2[1]=4 →取3,median2=3.
返回(2+3)/2=2.5。正确。
这说明这种方法是可行的。
因此,这种写法是正确的,尽管代码有些重复。
现在,将这段代码写入原题的函数框架中。
因此,最终的findMedianSortedArrays函数的实现如下:
double findMedianSortedArrays(int nums1[], int nums2[],int n,int m) {
int total = n + m;
int k1 = (total + 1) / 2;
int k2 = (total + 2) / 2;
int median1, median2;
// Find k1-th element
int i = 0, j = 0, k = k1;
while (true) {
if (i == n) {
median1 = nums2[j + k -1];
break;
}
if (j == m) {
median1 = nums1[i + k -1];
break;
}
if (k == 1) {
median1 = min(nums1[i], nums2[j]);
break;
}
int step1 = min(k/2, n - i);
int step2 = min(k/2, m - j);
int val1 = nums1[i + step1 -1];
int val2 = nums2[j + step2 -1];
if (val1 <= val2) {
i += step1;
k -= step1;
} else {
j += step2;
k -= step2;
}
}
if (total % 2 == 0) {
// Find k2-th element
i = 0;
j = 0;
k = k2;
while (true) {
if (i == n) {
median2 = nums2[j + k -1];
break;
}
if (j == m) {
median2 = nums1[i + k -1];
break;
}
if (k == 1) {
median2 = min(nums1[i], nums2[j]);
break;
}
int step1 = min(k/2, n - i);
int step2 = min(k/2, m - j);
int val1 = nums1[i + step1 -1];
int val2 = nums2[j + step2 -1];
if (val1 <= val2) {
i += step1;
k -= step1;
} else {
j += step2;
k -= step2;
}
}
return (median1 + median2) / 2.0;
} else {
return median1;
}
}
这样,代码虽然冗长,但可以正确运行。并且,这样的代码可以直接插入到用户提供的函数框架中。
现在,检查是否符合C++语法,例如变量声明是否正确,是否使用了正确的min函数等。原题中的代码已经定义了宏min和max,因此可以用min(a,b)来进行比较。例如,在代码中,min(nums1[i], nums2[j])被正确使用。
原题中的宏定义是:
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
因此,在代码中使用min是正确的。
现在,测试这段代码是否能正确处理各种情况。
例如,当有一个数组为空的情况:
nums1为空,nums2=[1,2,3],n=0, m=3. total=3,奇数。k1=2.
在循环中,i=0等于n=0,所以返回nums2[j +k-1] = nums2[0 + 2-1] = nums2[1]=2 →正确。
如果nums1和nums2都非空,且存在重复元素的情况,比如nums1=[1,1], nums2=[1,1],n=2,m=2. total=4. k1=2, k2=3.
第一个循环找k1=2:
i=0,j=0,k=2.
step1=1, step2=1.
val1=1, val2=1 →相等,可以任选一个。假设选nums1的step1=1.
i=1, k=1.
比较nums1[1]和nums2[0], 1和1 →取1. median1=1.
第二个循环找k2=3:
i=0,j=0,k=3.
step1=1 (3/2=1), step2=1.
val1=1, val2=1 →i becomes 1, k=2.
现在,step1= min(1, 2-1)=1, step2=1.
val1=1, val2=1 →i becomes 2, k=1. 此时i ==n=2,所以返回nums2[j +k-1] = nums2[0 +1-1]=nums2[0]=1. median2=1.
返回(1+1)/2=1.0,正确。
这说明代码在这种情况下也能正确运行。
综上所述,这个代码应该可以正确解决问题,并且时间复杂度为O(log(m+n)),因为每次循环都将k减少一半,总共有O(logk)次循环,而k最多是(m+n)/2,所以总的时间复杂度是O(log(m+n))。</think>```cpp
#include <iostream>
using namespace std;
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
const int N = 1e8+10;
double findMedianSortedArrays(int nums1[], int nums2[],int n,int m) {
/************Begin**********/
int total = n + m;
int k1 = (total + 1) / 2;
int k2 = (total + 2) / 2;
int median1, median2;
// 查找第k1小的元素
int i = 0, j = 0, k = k1;
while (true) {
if (i == n) {
median1 = nums2[j + k - 1];
break;
}
if (j == m) {
median1 = nums1[i + k - 1];
break;
}
if (k == 1) {
median1 = min(nums1[i], nums2[j]);
break;
}
int step1 = min(k/2, n - i);
int step2 = min(k/2, m - j);
int val1 = nums1[i + step1 - 1];
int val2 = nums2[j + step2 - 1];
if (val1 <= val2) {
i += step1;
k -= step1;
} else {
j += step2;
k -= step2;
}
}
// 处理偶数情况
if (total % 2 == 0) {
// 查找第k2小的元素
i = 0;
j = 0;
k = k2;
while (true) {
if (i == n) {
median2 = nums2[j + k - 1];
break;
}
if (j == m) {
median2 = nums1[i + k - 1];
break;
}
if (k == 1) {
median2 = min(nums1[i], nums2[j]);
break;
}
int step1 = min(k/2, n - i);
int step2 = min(k/2, m - j);
int val1 = nums1[i + step1 - 1];
int val2 = nums2[j + step2 - 1];
if (val1 <= val2) {
i += step1;
k -= step1;
} else {
j += step2;
k -= step2;
}
}
return (median1 + median2) / 2.0;
} else {
return median1;
}
/************End**********/
}
int main()
{
int m,n,nums1[N],nums2[N];
cin>>n>>m;
for(int i = 0; i < n; i ++)cin>>nums1[i];
for(int i = 0; i < m; i ++)cin>>nums2[i];
double ret = findMedianSortedArrays(nums1, nums2,n,m);
cout<<ret<<endl;
return 0;
}
```
### 分步解释:
1. **计算总长度**:首先计算两个数组的总长度 `total = n + m`,用于判断奇偶性。
2. **确定中位数位置**:
- 奇数情况:中位数位置为 `(total + 1) / 2`。
- 偶数情况:中位数位置为 `total/2` 和 `total/2 + 1`。
3. **查找第k小元素**:
- 使用双指针 `i` 和 `j` 分别遍历两个数组,每次排除 `k/2` 个不可能的元素。
- 通过比较两个数组的中间值,缩小搜索范围,逐步逼近第k小的元素。
4. **处理边界条件**:
- 当某个数组被完全排除时,直接从另一个数组获取剩余元素。
- 当 `k=1` 时,直接比较当前指针位置的元素。
5. **计算最终结果**:
- 奇数次直接返回第k小的元素。
- 偶数次返回两个中间值的平均。
该方法通过二分查找策略将时间复杂度优化至 $O(\log(m+n))$,满足题目要求。
阅读全文
相关推荐


















