求两个有序数组归并之后的第k个最小值
1. 题目:给定两个有序数组(升序)。求两个数组合并之后的第k小值。
2. 分析:常规算法将数组合并,之后从前向后遍历一次,找到k位的值,时间复杂度O(m+n)。改进之后的算法:不需要归并两个数组,而是使用两个指针分别从两个有序数组的起始向后走,如果a[i]<=b[j],那么i++,直到a[i]>b[j] ; 如果a[i]>=b[j],那么j++,直到a[i]<b[j],不过i和j要满足条件i+j+1<k。具体算法流程见代码。算法时间复杂度为O(k)。文章中给出了一个很好的算法:二分搜索的变形。具体见代码。
3. 代码:
1 #include <iostream> 2 #include <cassert> 3 4 using namespace std; 5 6 //改进的二分搜索算法 7 //时间复杂度为O(lgm+lgn) 8 int findKthSmallest(int A[], int m, int B[], int n, int k) { 9 assert(m >= 0); assert(n >= 0); assert(k > 0); assert(k <= m+n); 10 11 int i = (int)((double)m / (m+n) * (k-1)); 12 int j = (k-1) - i; 13 14 assert(i >= 0); assert(j >= 0); assert(i <= m); assert(j <= n); 15 // invariant: i + j = k-1 16 // Note: A[-1] = -INF and A[m] = +INF to maintain invariant 17 int Ai_1 = ((i == 0) ? INT_MIN : A[i-1]); 18 int Bj_1 = ((j == 0) ? INT_MIN : B[j-1]); 19 int Ai = ((i == m) ? INT_MAX : A[i]); 20 int Bj = ((j == n) ? INT_MAX : B[j]); 21 22 if (Bj_1 <= Ai && Ai <= Bj) 23 return Ai; 24 else if (Ai_1 <= Bj && Bj <= Ai) 25 return Bj; 26 27 assert((Ai > Bj && Ai_1 > Bj) || 28 (Ai < Bj && Ai < Bj_1)); 29 30 // if none of the cases above, then it is either: 31 if (Ai < Bj) 32 // exclude Ai and below portion 33 // exclude Bj and above portion 34 return findKthSmallest(A+i+1, m-i-1, B, j, k-i-1); 35 else /* Bj < Ai */ 36 // exclude Ai and above portion 37 // exclude Bj and below portion 38 return findKthSmallest(A, i, B+j+1, n-j-1, k-j-1); 39 } 40 41 42 int findKthSmallest1(int A[], int m, int B[], int n, int k) { 43 assert(A && B && m>0 && n>0 && k>=1 && k<=m+n); 44 int i=0; 45 int j=0; 46 while (true) 47 { 48 //考虑到数组A或B已经走到末尾 49 if (i>=m) 50 { 51 while (i+j+1<k) 52 { 53 j++; 54 } 55 return B[j]; 56 } 57 else if (j>=n) 58 { 59 while (i+j+1<k) 60 { 61 i++; 62 } 63 return A[i]; 64 } 65 else 66 { 67 if (A[i]<=B[j]) 68 { 69 while(i<m && A[i]<=B[j] && i+j+1<k) 70 { 71 i++; 72 } 73 //找到第k个数,那么A[i]和B[j]中的较小的 74 //那个就是第k个数 75 if ((i<m) && (i+j+1==k)) 76 { 77 if (A[i]<=B[j]) 78 { 79 return A[i]; 80 } 81 else 82 return B[j]; 83 } 84 } 85 else 86 { 87 while (j<n && A[i]>=B[j] && i+j+1<k) 88 { 89 j++; 90 } 91 if ((j<n) && (i+j+1==k)) 92 { 93 if (A[i]>=B[j]) 94 { 95 return B[j]; 96 } 97 else 98 return A[i]; 99 } 100 } 101 } 102 } 103 } 104 105 106 void testFindKthSmallest(int A[], int m, int B[], int n) 107 { 108 for (int i=1;i<=m+n;i++) 109 { 110 cout<<i<<"th number="<<findKthSmallest1(A,m,B,n,i)<<endl; 111 } 112 for (int i=1;i<=m+n;i++) 113 { 114 cout<<i<<"th number="<<findKthSmallest(A,m,B,n,i)<<endl; 115 } 116 } 117 118 119 int main() 120 { 121 enum{m=6,n=5}; 122 int A[m]={1,3,5,5,14,14}; 123 int B[n]={2,3,3,3,15}; 124 testFindKthSmallest(A,m,B,n); 125 return 0; 126 }
4. 原题中给定的数组A和B不含有重复的元素,这里对原题的代码稍作修改,可以满足有重复元素出现的情况。
5. 参考文章:
http://www.leetcode.com/2011/01/find-k-th-smallest-element-in-union-of.html