[Leetcode] Median of Two Sorted Arrays 有序數組中位數

Median of Two Sorted Arrays

最新解法及思路:https://yanjia.li/zh/2018/11/...

There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

有兩個有序數組nums1和nums2,他們的大小各是m和n,請找出這兩個數組全部數的中位數,總得時間複雜度不超過O(log(m+n))java

歸併計數法 Merge and Count

複雜度

時間O(n) 空間O(1)數組

思路

若是對時間複雜度沒有要求,這個方法是實現起來最簡單的,咱們只須要從下往上依次數(n+m)/2個元素便可。因爲兩個數組都已經排序,咱們可使用兩個指針指向數組「底部」,經過比較兩個數組「底部」的元素大小來決定計哪個元素,同時將其所在數組的指針「向上」移一位。爲了方便處理總元素爲偶數的狀況,這裏將找中位數變成找第k小的元素。ide

注意

  • 計數的循環是用來找到第k-1個元素的,最後return的時候再判斷第k個元素是哪個
  • 在每次計數的循環中要先判斷兩個數組指針是否超界,在最後return以前也要判斷一次

代碼

public class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int len1 = nums1.length;
        int len2 = nums2.length;
        int total = len1 + len2;
        if(total % 2==0){
            return (findKth(nums1,nums2,total/2)+findKth(nums1,nums2,total/2+1))/2.0;
        } else {
            return findKth(nums1,nums2,total/2+1);
        }
    }
    private int findKth(int[] nums1, int[] nums2, int k){
        int p = 0, q = 0;
        for(int i = 0; i < k - 1; i++){
            if(p>=nums1.length && q<nums2.length){
                q++;
            } else if(q>=nums2.length && p<nums1.length){
                p++;
            } else if(nums1[p]>nums2[q]){
                q++;
            } else {
                p++;
            }
        }
        if(p>=nums1.length) {
            return nums2[q];
        } else if(q>=nums2.length) {
            return nums1[p];
        } else {
            return Math.min(nums1[p],nums2[q]);
        }
    }
}

分治法 Divide and Conquer

複雜度

時間O(log(m+n)) 空間O(1)指針

思路

題目要求O(log(m+n))的時間複雜度,通常來講都是分治法或者二分搜索。首先咱們先分析下題目,假設兩個有序序列共有n個元素(根據中位數的定義咱們要分兩種狀況考慮),當n爲奇數時,搜尋第(n/2+1)個元素,當n爲偶數時,搜尋第(n/2+1)和第(n/2)個元素,而後取他們的均值。進一步的,咱們能夠把這題抽象爲「搜索兩個有序序列的第k個元素」。若是咱們解決了這個k元素問題,那中位數不過是k的取值不一樣罷了。code

那如何搜索兩個有序序列中第k個元素呢,這裏又有個技巧。假設序列都是從小到大排列,對於第一個序列中前p個元素和第二個序列中前q個元素,咱們想要的最終結果是:p+q等於k-1,且一序列第p個元素和二序列第q個元素都小於總序列第k個元素。由於總序列中,必然有k-1個元素小於等於第k個元素。這樣第p+1個元素或者第q+1個元素就是咱們要找的第k個元素。排序

因此,咱們能夠經過二分法將問題規模縮小,假設p=k/2-1,則q=k-p-1,且p+q=k-1。若是第一個序列第p個元素小於第二個序列第q個元素,咱們不肯定二序列第q個元素是大了仍是小了,但一序列的前p個元素確定都小於目標,因此咱們將第一個序列前p個元素所有拋棄,造成一個較短的新序列。而後,用新序列替代原先的第一個序列,再找其中的第k-p個元素(由於咱們已經排除了p個元素,k須要更新爲k-p),依次遞歸。同理,若是第一個序列第p個元素大於第二個序列第q個元素,咱們則拋棄第二個序列的前q個元素。遞歸的終止條件有以下幾種:遞歸

  • 較短序列全部元素都被拋棄,則返回較長序列的第k個元素(在數組中下標是k-1)
  • 一序列第p個元素等於二序列第q個元素,此時總序列第p+q=k-1個元素的後一個元素,也就是總序列的第k個元素

注意

  • 每次遞歸不只要更新數組起始位置(起始位置以前的元素被拋棄),也要更新k的大小(扣除被拋棄的元素)

代碼

public class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length, n = nums2.length;
        int k = (m + n) / 2;
        if((m+n)%2==0){
            return (findKth(nums1,nums2,0,0,m,n,k)+findKth(nums1,nums2,0,0,m,n,k+1))/2;
        }   else {
            return findKth(nums1,nums2,0,0,m,n,k+1);
        }
        
    }
    
    private double findKth(int[] arr1, int[] arr2, int start1, int start2, int len1, int len2, int k){
        // 保證arr1是較短的數組
        if(len1>len2){
            return findKth(arr2,arr1,start2,start1,len2,len1,k);
        }
        if(len1==0){
            return arr2[start2 + k - 1];
        }
        if(k==1){
            return Math.min(arr1[start1],arr2[start2]);
        }
        int p1 = Math.min(k/2,len1) ;
        int p2 = k - p1;
        if(arr1[start1 + p1-1]<arr2[start2 + p2-1]){
            return findKth(arr1,arr2,start1 + p1,start2,len1-p1,len2,k-p1);
        } else if(arr1[start1 + p1-1]>arr2[start2 + p2-1]){
            return findKth(arr1,arr2,start1,start2 + p2,len1,len2-p2,k-p2);
        } else {
            return arr1[start1 + p1-1];
        }
    }
}
相關文章
相關標籤/搜索