天天一道算法題:求兩個排序數組的中位數

題目

有兩個大小爲 m 和 n 的排序數組 nums1 和 nums2 。html

請找出兩個排序數組的中位數而且總的運行時間複雜度爲 O(log (m+n)) 。java

示例1:數組

nums1 = [1, 3]
nums2 = [2]

中位數是 2.0
複製代碼

示例2:bash

nums1 = [1, 2]
nums2 = [3, 4]

中位數是 (2 + 3)/2 = 2.5
複製代碼

歸併&topK問題spa

方案1

這個思路就是對於兩個有序數組進行合併,合併到一個大的有序的數組中去,而後求合併後數組的中位數。下面代碼中使用的是歸併排序的方式,對於兩個有序數組進行歸併排序的。從複雜度的角度來講能夠知足題目的要求,可是仍是存在一些問題,主要是怎麼可以使得時間複雜度變成O{MIN(nums1.length,nums2.leng)}。code

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        double result = 0.0d;
        int[] nums = new int[nums1.length+nums2.length];
        int num1index = 0;
        int num2index = 0;
        int index = 0;
        if (nums1.length == 0 && nums2.length == 0){
            return result;
        }
        if (nums1.length == 0){
            return getResult(nums2);
        }
        if (nums2.length == 0){
            return getResult(nums1);
        }
        while(num1index < nums1.length && num2index < nums2.length){
            if (nums1[num1index] < nums2[num2index]){
                nums[index]= nums1[num1index];
                num1index++;
            }else{
                nums[index]= nums2[num2index];
                num2index++;
            }
            index++;
        }

        while (num1index < nums1.length){
            nums[index] = nums1[num1index++];
            index++;
        }

        while (num2index < nums2.length){
            nums[index] = nums2[num2index++];
            index++;
        }

        if (nums.length%2==0)
        {
            result = (nums[nums.length/2]+nums[nums.length/2-1])/2.0;
        }
        else{
            result = nums[nums.length/2];
        }

        return result;
    }
    
    private double getResult(int[] nums){
        double result = 0.0D;
        if (nums.length%2==0)
        {
            result = (nums[nums.length/2]+nums[nums.length/2-1])/2.0;
        }
        else{
            result = nums[nums.length/2];
        }
        return result;
    }
}
複製代碼

方案2

求兩個排序數組的中位數htm

  1. 假設nums1.length = m, nums2.length = n; m < n;
  2. 若(m + n) % 2 == 0, 表示兩數組之和爲偶數,應該是有兩個中位數,所以最終結果爲第9行的代碼所示。不然,結果爲第7行的代碼所示。
  3. 爲了使得方法的統一,在最初時,對數組進行處理,統一使得傳進方法的短數組爲nums1,即第14行代碼所示。
  4. 若是len1-start1 == 0,則表示nums1已經所有加入前k個了,則第k個爲nums2[k -1]; 在方法findKth()中的k是一直變化的,初始時,k爲兩個數組中排序以後的第k個數的位置;k在方法中的真正含義爲「還須要找到多少個數才能達到k個」;所以假設nums1.length ==0;,此時len1-start1 == 0, 則中位數就是nums2[k - 1],即在nums1中找到了0個數,還須要找k個數,第k個數就是nums[k - 1];
  5. 若是k == 1,則表示前k-1小的數已經找過了,則第k個數確定是nums1[start1]和nums2[start2]中較小的那個數。
  6. 下面接着就是常規的狀況:即nums1中包含一部分k,nums2中也包含一部分的k,所以就從每一個數組的k/2那裏開始比較(也至關於每次都會有一半的數被加入前k個,所以時間複雜度爲O(log(m + n))): 採用p1和p2分別記錄當前nums1和nums2須要比較的那個位,因爲nums1比較短,所以有可能2/k的位置已經超出了nums1的長度,所以nums1還須要作特殊處理,即第19行代碼所示;因爲p1作了特殊處理,那p2也就要作特殊處理。總之,start1~p1和start2~p2的和必定爲k。 1)若nums1[p1 - 1] < nums[p2 - 1],則代表【start1, p1)之間的值在前k個數中; 2)若nums[p1 - 1] > nums2[p2- 1],則代表【start2, p2)之間的值在前k個數中; 3)若兩值相等,則代表【start1, p1)+【start2, p2)的個數爲k,則結果直接返回其中一個便可。 爲何比較的p1和p2的前一個位的數,而不是p1和p2位置的數呢? 舉例說明:假設start1== start2 == 0, 則p1 = Math.min(len1, k / 2); p2 = k - p1,即p1 + p2 == k;;假設p1 = 5, p2 = 7;, 則k = 12; 在數組中nums[5]實際上是第6個數,nums[7]實際上是第8個數,因此咱們比較的是nums1[p1 - 1]與nums2[p2 - 1]的值;
public class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int len1 = nums1.length;
        int len2 = nums2.length;
        int size = len1 + len2;
        if(size % 2 == 1)
            return findKth(nums1, 0, len1, nums2, 0, len2, size / 2 + 1);
        else
            return (findKth(nums1, 0, len1, nums2, 0, len2, size / 2) + findKth(nums1, 0, len1, nums2, 0, len2, size / 2 + 1)) /2;
    }
    public double findKth(int[] nums1, int start1, int len1, int[] nums2, int start2, int len2, int k) {
        if(len1 - start1 > len2 -start2)  // 傳進來的時候統一讓短的數組爲nums1
            return findKth(nums2, start2, len2, nums1, start1, len1, k);
        if(len1 - start1 == 0)  // 表示nums1已經所有加入前K個了,第k個爲nums2[k - 1];
            return nums2[k - 1];
        if(k == 1)
            return Math.min(nums1[start1], nums2[start2]); // k==1表示已經找到第k-1小的數,下一個數爲兩個數組start開始的最小值
        int p1 = start1 + Math.min(len1 - start1, k / 2); // p1和p2記錄當前須要比較的那個位
        int p2 = start2 + k - p1 + start1;
        if(nums1[p1 - 1] < nums2[p2 - 1])
            return findKth(nums1,  p1, len1, nums2, start2, len2, k - p1 + start1);
        else if(nums1[p1 - 1] > nums2[p2 -1])
            return findKth(nums1, start1, len1, nums2, p2, len2, k - p2 + start2);
        else
            return nums1[p1 - 1];
        
    }
}
複製代碼
相關文章
相關標籤/搜索