求两个有序数组归并之后的第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

posted @ 2012-08-16 14:08  kasuosuo  阅读(903)  评论(0编辑  收藏  举报