两个有序数组中找中位数或者第K大的元素
RT,在两个有序数组中找中位数或者第K大的元素.
假设两个数组为A, B长度分别为m,n.分别是递增顺序。
可以采用的算法有很多:
首先想到的是类似MergeSort的方式,合并的同时找第K大元素,这个基本没难度,复杂度O(m + n)。
不过此算法并不是最优,还有Log级别复杂度的算法,此算法其实很简单,远没有很多网站的代码那么玄乎,以下一一道来:
首先明白几个前提:
1.如果是求中位数,(m + n)是奇数还是偶数对结果是很有影响的,具体的如果(m + n)是奇数,中位数唯一,如果是偶数就有两个中位数,怎么取舍就看要求了。
2.如果找到的第k大数(中位数类似)是 X ,如果X排在A中的 第Ax位置,X排序在B中的Bx位置,那么(Ax + Bx - 1) == k 是恒成立的。
明白了2中的前提后,我们就可以得到一个算法,在A数组中枚举X,加入在A中是第Ax个,那么可以反推B中第 (k + 1 - Ax)个以及相邻元素和X的大小关系就可以得到一个Log级别复杂度的算法:
简单点我们可以这么想:
1)先假设第k大数在A中,我们首先从A中第(m/(m + n)) * (k - 1)个元素开始检查其是否是第k个元素,假设其值为A1,然后看B中第(k + 1 - (m/(m + n)) * (k - 1)个元素(B1)和A1是否相等,或者 大于B中第(k + 1 - (m/(m + n)) * (k - 1))个元素,小于B中第(k + 1 - (m/(m + n)) * (k - 1))+ 1个元素。满足及可以知道A1即为所求。如果两个条件都不满足,请看2.
2)如果两个条件都不满足,那么需要判断第k个元素是处于 A1的左边还是右边,这个就是典型的分治思想。具体的来说:
if A1 > B1 那么k可以排除肯定不在A[0, (m/(m + n)) * (k - 1)]以及B[(k + 1 - (m/(m + n)) * (k - 1))+ 1, n]中
if A1< B1 那么k可以排除肯定不在A[ (m/(m + n)) * (k - 1), m]以及B[0, (k + 1 - (m/(m + n)) * (k - 1))]中.
注意下临界条件(corner condition may not stastify, but the method is right)
第K个元素有可能在B中,同理可以假设在B中,然后再搜索一遍就可以查到。复杂度 log(m)+ log(n)
当然也可以两个数组一起找,总体代码如下:
1 int kthsmallest(int *a,int m,int *b,int n,int k) { 2 if (m == 0) { 3 return b[k - 1]; 4 } 5 if (n == 0) { 6 return a[k - 1]; 7 } 8 if (k == 1) { 9 return (a[0] < b[0])?a[0]:b[0]; 10 } 11 if (k == m + n) { 12 return (a[m - 1] > b[n - 1])?a[m - 1]:b[n - 1]; 13 } 14 int i = ((double) m) / (m + n) * (k - 1); 15 int j = k - 1 - i; 16 if (j >= n) { 17 j = n - 1; 18 i = k - n; 19 } 20 if (((i == 0) || (a[i - 1] <= b[j])) && (b[j] <= a[i])) { 21 return b[j]; 22 } 23 if (((j == 0) || (b[j - 1] <= a[i])) && (a[i] <= b[j])) { 24 return a[i]; 25 } 26 if (a[i] <= b[j]) { 27 return kthsmallest(a + i + 1, m - i - 1, b, j, k - i - 1); 28 } else { 29 return kthsmallest(a, i, b + j + 1, n - j - 1, k - j - 1); 30 } 31 32 }