中位数:一组有序数字里面最中间的那个数 (奇数个数)或者最中间两个数的平均数(偶数个数)
三种方法:
1、首先想到的是把两个数组按升序merge起来,直接取最中间的那个数(或者中间两个数的平均数),时间复杂度O(m+n)
2、第一种方法的缺点是要把所有元素都进行排序才能直接取最中位数,但是我们只需要取中位数,其实当排序排到中间那个数的时候后面的元素就不需要排了,我们可以边排序边count元素个数,如果count值等于中位数的位置则停止排序,当前元素即为中位数。此时时间复杂度要根据两个数组的元素分布情况来看了,如果该中位数在数组的很后边,那么时间复杂度也接近于O(m+n)
3、更好的方案是Top-K思想,把中位数看作是第k小的数(升序排列)。
首先假设数组A和B的元素个数都大于k/2,我们比较A[k/2-1]和B[k/2-1]两个元素,这两个元素分别表示A的第k/2小的元素和B的第k/2小的元素。这两个元素比较共有三种情况:
(1)如果A[k/2-1]<B[k/2-1],这表示A[0]到A[k/2-1]的元素都在A和B合并之后的前k小的元素中,也就是说A[k/2-1]不可能大于两数组合并之后的第k小值,那么就可以直接不考虑这部分元素了。
(2)当A[k/2-1]>B[k/2-1]时也类似,这表示B[0]到B[k/2-1]的元素都在A和B合并之后的前k小的元素中,那么就可以直接不考虑这部分元素了。
(3)当A[k/2-1]=B[k/2-1]时,我们已经找到了第k小的数,就是A[k/2-1]或B[k/2-1]。由于在A和B中分别有k/2-1个元素小于它,所以它是第k小的数。
如果其中一个数组(假设是数组A)的元素个数小于k/2,那么我们要比较A[m]和B[k-m],方法同上。
因为要不断删除前k/2个数,直到寻找到第k个数,这里用递归实现寻找第k小的数。
还需要考虑几种情况:
(1)如果A或者B为空,则直接返回B[k-1]或者A[k-1];
(2)如果k为1,我们只需要返回A[0]和B[0]中的较小值;
(3)如果A[k/2-1]=B[k/2-1],返回其中一个;
时间复杂度为O(log(m+n))
class Solution {
public:
double findKth(vector<int>::iterator arr1, int size1, vector<int>::iterator arr2, int size2, int k){
if (size1 > size2)
return findKth(arr2, size2, arr1, size1, k);
if (size1 == 0)
return arr2[k - 1];
if (k == 1)
return min(arr1[0], arr2[0]);
int pa = min(k / 2, size1);
int pb = k - pa;
if (arr1[pa - 1] < arr2[pb - 1])
return findKth(arr1 + pa, size1 - pa, arr2, size2, k - pa);
else if (arr1[pa - 1] > arr2[pb - 1])
return findKth(arr1, size1, arr2 + pb, size2 - pb, k - pb);
else
return arr1[pa - 1];
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int len1 = nums1.size();
int len2 = nums2.size();
int sum = len1 + len2;
if (sum % 2 != 0)
return findKth(nums1.begin(), len1, nums2.begin(), len2, sum / 2 + 1);
else
return (findKth(nums1.begin(), len1, nums2.begin(), len2, sum / 2)
+ findKth(nums1.begin(), len1, nums2.begin(), len2, sum / 2 + 1)) / 2;
}
};
补充一点C++vector的迭代器iterator的知识:
iterator可以很方便遍历vector的元素,可以从指定的某个位置开始遍历(iter+2).
区分两种const iterator:
1、const迭代器,是常量,是不能改变的迭代器,其性质是由const决定的。比如说我们这样定义一个const迭代器。
vector<int> vv(10,9);
const vector<int> :: iterator iter = vv.begin();
当程序中出现如下的语句时是错误的
++iter;
其原因是iter是一个常量,因此是不能改变的。换句话说,iter只能指向vv的地一个元素,不能指向其他的元素。
但是这样的语句是正确的:
*iter = 10;
因为虽然iter不能指向其他的元素,但是其指向的第一个元素的值是可以改变的。
2、对于const_iterator来说的话,正好相反。比如我们这样定义
vector<int> vv(10,9);
vector<int> :: const_iterator iter;
即定义了一个const_iterator迭代器。这个迭代器是可以自己增加的,但是其所指向的元素是不可以被改变的。比如说
for(iter = vv.begin(); iter != vv.end(); ++iter){
cout << *iter << endl;
}
这样是正确的,即iter本身的值是可以改变的。但是
for(iter = vv.begin(); iter != vv.end(); ++ iter){
*iter = 0;
}
这样是不对的,因为const_iterator迭代器是不能改变其所指向的元素的值的。