两个有序数组取第K大

题目:给定两个一维int数组A和B. 其中:A是长度为m、元素从小到大排好序的有序数组。B是长度为n、元素从小到大排好序的有序数组。希望从A和B数组中,找出最大的k个数字,要求:使用尽量少的比较次数。

1、首先我们定义一个算法模型,然后形成一个函数 f 

  模型:两个等长的数组,我们得到这两个数组的上中位数

  1.1  两数组的长度是偶数:假设长度为5

      数组1: 1 2 3 4     1这些数字只是这个数组元素的称呼,并不是实际值

      数组2: a b c d 

   那我们如果要取出这两个数组的上中位数,我们就有几种假设如下:

  •         假设1: 数组1上中位数  [2] == [b] 数组2中上中位数 ,那么我们就可以得到 [2] 或 [b]就是这两个数组的上中位数
  •      假设2: 数组1上中位数  [2] >  [b] 数组2中上中位数 , 我们可以知道数组1中 [3] 、[4] 和 数组 2 中的 [a]、[b]不可能是上中位数,那么此时数组1和数组中可能为上中位数的数个数是一样的,循环调用我们这个函数f,得到的上中位数就是数组1 和 数组2 整体的上中位数
  •      假设3: 数组1上中位数  [2] <  [b] 数组2中上中位数,和假设2相似

    1.2  两数组的长度是奇数

  数组1: 1 2 3 4 5     1这些数字只是这个数组元素的称呼,并不是实际值

       数组2: a b c d e

  那我们如果要取出这两个数组的上中位数,我们就有几种假设如下:

  •     假设1: 数组1上中位数  [3] == [c] 数组2中上中位数

               那么我们就可以得到 [3] 或 [c]就是这两个数组的上中位数

  •     假设2: 数组1上中位数  [3] >  [c] 数组2中上中位数 ,

          我们可以知道数组1中 [3] 、[4] 、[5]和 数组 2 中的 [a]、[b]不可能是上中位数。那么此时,数组1中可能为上中位数的数个数是2,数组2的个数是3,长度不相等,不能调用我们的函数 f 来获取上中位数。解决方式是验证下数组2中的 [c] 是否比数组1中的 [3] 大。如果相等,则说明 [2] 和 [c] 就是这两个数组的上中位数,如果 [2] < [c] ,那么 [c] 就是这两个数组的上中位数,如果 [2] > [c] ,那么我们就排除了 [c] 为上中位数的可能性,数组1和数组2中可能为上中位数的个数都变成2,我们又可调用我们函数 f 来得到上中位数。

  •       假设3: 数组1上中位数  [3] <  [c] 数组2中上中位数,和假设2相似

2、取出我们的数组A和数组B的第k大元素:假设数组A长度为10,数组B长度为15

       我们分为三种情况来描述如何取这两个数组的第k大元素:

  数组A: 1‘ 、2‘ 、3‘、4‘、5‘、6‘、7‘、8‘、9‘、10‘

       数组B: 1  、2 、 3 、4 、5 、6 、7、8 、9 、10、11、12、13、14、15

  •        情况 1 :   1 <= k <= 10 , 我们假设k为5

      我们可知,只有数组A和数组B的前5个元素中才有可能是第5大的元素,那我们从数组A和数组B中分别取出前5个元素,然后调用我们的函数 f 就可以得到上中位数,这样也是我们的第 5 大 元素

  •        情况 2 :    15 < k < 25 ,我们假设为 21

     我们可知,数组A中 1‘~5‘不可能,可能个数为5,数组B中1~10不可能,可能个数5。此刻我们可能性个数是一样的,但是却不能像之前那样直接调用函数f ,因为数组A+数组B淘汰的可能性个数是5+10=15种,我们如果在数组A+数组B可能的数中取出中位数,也才是第15+5=20个数,不是第21大的。因此我们需要人为的再淘汰两个数,这样就是淘汰的个数是17,数组A+数组B可能的个数是4,这样取出的上中位数才是第21大的数。因为我们需要淘汰掉 6' 和 11。那我们就判断下6‘ 和 15的大小。如果 6 ‘> 15,那6‘就是上中位数,否则淘汰6‘这个可能性;其次我们在判断下11和10'的大小,如果11 > 10',那么11就是上中位数,否则我们就去掉11这个可能性。这样最终淘汰的个数是17,数组A+数组B可能的个数是4,这样取出的上中位数就是第21大的数。

  •        情况3 :     10 < k < 15,我们假设为13

     我们可知,数组A中数都有可能,可能的个数为10;数组B中1~2和14~15不可能,可能的个数为11;没法调用我们的函数f,那我们再在数组B中多淘汰一个数,这样就可以调用我们的函数f。因此我们需要再判断下 3 和 10‘ 的大小,如果3 > 10',那么3就是第13大的数,如果不是我们就淘汰了3这个可能性,再调用我们的函数f这样,就可以得到第13大的数。

具体代码如下:

 1  public static int findKthNum(int[] arr1, int[] arr2, int kth) {
 2         if (arr1 == null || arr2 == null) {
 3             throw new RuntimeException("Your arr is invalid!");
 4         }
 5         if (kth < 1 || kth > arr1.length + arr2.length) {
 6             throw new RuntimeException("K is invalid!");
 7         }
 8         int[] longs = arr1.length >= arr2.length ? arr1 : arr2;
 9         int[] shorts = arr1.length < arr2.length ? arr1 : arr2;
10         int l = longs.length;
11         int s = shorts.length;
12         if (kth <= s) {
13             return getUpMedian(shorts, 0, kth - 1, longs, 0, kth - 1);
14         }
15         if (kth > l) {
16             if (shorts[kth - l - 1] >= longs[l - 1]) {
17                 return shorts[kth - l - 1];
18             }
19             if (longs[kth - s - 1] >= shorts[s - 1]) {
20                 return longs[kth - s - 1];
21             }
22             return getUpMedian(shorts, kth - l, s - 1, longs, kth - s, l - 1);
23         }
24         if (longs[kth - s - 1] >= shorts[s - 1]) {
25             return longs[kth - s - 1];
26         }
27         return getUpMedian(shorts, 0, s - 1, longs, kth - s, kth - 1);
28     }
29     //获取两个数组的上中位数
30     public static int getUpMedian(int[] a1, int s1, int e1, int[] a2, int s2,int e2) {
31         int mid1 = 0;
32         int mid2 = 0;
33         int offset = 0;//判断数组长度是奇数还是偶数
34         while (s1 < e1) {
35             mid1 = (s1 + e1) / 2;
36             mid2 = (s2 + e2) / 2;
37             offset = ((e1 - s1 + 1) & 1) ^ 1;
38             if (a1[mid1] > a2[mid2]) {
39                 e1 = mid1;
40                 s2 = mid2 + offset;
41             } else if (a1[mid1] < a2[mid2]) {
42                 s1 = mid1 + offset;
43                 e2 = mid2;
44             } else {
45                 return a1[mid1];
46             }
47         }
48         return Math.min(a1[s1], a2[s2]);
49     }
View Code

 

猜你喜欢

转载自www.cnblogs.com/liujunj/p/10432453.html