1. 程式人生 > >【Leetcode】兩個有序陣列的中位數

【Leetcode】兩個有序陣列的中位數

解法一:
當合並後的總元素個數是奇數時,中位數的下標是n/2。當總元素個數是偶數時,中位數是下標n/2-1和下標n/2兩個元素的平均值。不論總個數奇偶,可以將n/2作為右中位數,n/2-1作為左中位數,只不過總個數是奇數時,沒用到左中位數。也就是說必須要找到第n/2+1個元素。

    private static double findMedian(int[] A,int[] B){
        if(A==null||B==null) return -1;
        int m=A.length;
        int n=B.length;
        int len=m+n;
        if
(len==0) return -1; if(n==0) return findMedian(B,A); int left=-1,right=-1; int aStart=0,bStart=0; for(int i=0;i<=len/2;i++){ left=right; if(aStart<m&&(bStart>=n||A[aStart]<B[bStart])){ right=A[aStart++]; }else
{ right=B[bStart++]; } } if((len&1)==0) return (left+right)/2.0; else return right; }

上面的方法還可以推廣到更普遍的情況,找出兩個有序數組合並後的第k個元素。

    private static int findKth(int[] A,int[] B,int k){
        if(A==null||B==null) return -1;
        int m=A.length;
        int
n=B.length; int len=m+n; if(len<k||len==0) return -1; if(m==0) return B[k-1]; if(n==0) return A[k-1]; int c=0; int aStart=0,bStart=0; int result=-1; while(c<k){ if(aStart<m&&(bStart>=n||A[aStart]<B[bStart])){ result=A[aStart++]; }else{ result=B[bStart++]; } c++; } return result; }

解法二:
解法一的實質是每次剔除一個不可能是中位數的元素。其實也可以利用二分查詢的思想每次剔除大範圍元素提高搜尋效率。因為查詢中位數實質上是查詢第k個元素。
對於陣列A前p個元素A[0]、A[1]、A[2]、……A[p-1],有p-1個元素不大於A[p-1],同樣陣列B前k-p個元素B[0]、B[1]、B[2]、……B[k-p-1],有k-p-1個元素不大於B[k-p-1]。比較A[p-1]和B[k-p-1]存在三種情況:
1、若A[p-1]=B[k-p-1],那麼共有p-1+(k-p-1)+1個元素不大於A[p-1],也就是說A[p-1]即是第k小的數。
2、若A[p-1]< B[k-p-1],那麼第k小的數不可能在區間A[0,p)之內。我們可以用反證法證明,若第k小的數在A[0,p)中設為A[i],那麼A[i]前面只有i個數不比它大,不妨設A[p-1]是第k小的數即i=p-1,也就是說最多隻有p-1個數不比它大,而B[k-p-1]大於A[p-1],所以B[k-p-1]之後的所有數都大於A[p-1],這樣即使B[p-k-1]之前的所有數都不比A[p-1]大,B中也只能找到k-p-1個數不比A[p-1]大。那麼A和B中最多隻有p-1+(k-p-1)=k-2個數不大於A[p-1],從而A[p-1]不是第k小的數。
這裡要注意推論第k小的數不在某區間的前提是同一陣列中元素的相對位置不變,只有之前的數不比某個數大,之後的數不論是否相等都比它“大”。如122234中第2個2只有兩個數1、2不比它大,第3個2不算在內。否則,第k小的數可能出現在任何位置,如A={1,1,1,1,1},B={1,1,1,1,1}。 但是即使認為第k小的數可以出現在任何位置,上面的推論也沒有錯,因為除了區間A[0,p)之外必定會出現一個第k小,而我們只需要找到一個就可以了,因此捨棄掉區間A[0,p)是可行的。如A={0,1,1}和B={0,1,2,4,5,6}找第5小的數字,捨棄掉A(0,1)仍然可以找到解1。
同樣我們可以通過反證法證明,B[k-p-1]之後的數不可能是第k小的數,因為它至少是B[k-p],前面有k-p個數不比B[k-p]大,再加上A[0,p)有p個數不比B[k-p]大,所以至少有k-p+p=k個數不比B[k-p-1]大,也就是說B[k-p]至少是第k+1小的數而不可能是第k小的數。 捨棄區間之後問題就變成尋找第k-p小的數了。
3、同理若A[p-1]>B[k-p-1]可以捨棄掉B[0,p)區間和A[p-1]之後的區間。

    public static double findMedianSortedArrays(int A[], int B[]) {
        if(A==null||B==null) return -1.0;
        int m=A.length;
        int n=B.length;
        int len=m+n;
        if((len&1)==0){
            return (findK(A,0,m-1,B,0,n-1,len/2)+findK(A,0,m-1,B,0,n-1,len/2+1))/2.0;
        }else{
            return findK(A,0,m-1,B,0,n-1,len/2+1);
        }
    }
    //k表示第k小,對應下標為k-1
    public static int findK(int[] A,int startA,int endA,int[] B,int startB,int endB,int k){
        int lenA=endA-startA+1;
        int lenB=endB-startB+1;
        if(lenA<=0&&lenB>0){
            return B[startB+k-1];
        }else if(lenB<=0&&lenA>0){
            return A[startA+k-1];
        }
        if(lenA>lenB) {
            return findK(B,startB,endB,A,startA,endA,k);
        }
        if(k==1) {
            return Math.min(A[startA],B[startB]);
        }
        int ka=Math.min(lenA,k/2);
        int kb=k-ka;
        if(A[startA+ka-1]==B[startB+kb-1]){
            return A[startA+ka-1];
        }else if(A[startA+ka-1]<B[startB+kb-1]){
            return findK(A,startA+ka,endA,B,startB,startB+kb-1,k-ka);
        }else{
            return findK(A,startA,startA+ka-1,B,startB+kb,endB,k-kb);
        }
    }

程式設計時沒有注意整數/2結果是取商不是小數,下標越界、陣列首指標習慣寫成0、A[startA+ka-1]偷懶複製成A[startB+kb-1]卻忘記將A改成B等問題結果除錯了很久。

相關推薦

Leetcode有序陣列位數

解法一: 當合並後的總元素個數是奇數時,中位數的下標是n/2。當總元素個數是偶數時,中位數是下標n/2-1和下標n/2兩個元素的平均值。不論總個數奇偶,可以將n/2作為右中位數,n/2-1作為左中位數,只不過總個數是奇數時,沒用到左中位數。也就是說必須要找到第

分步詳解有序陣列位數和Top K問題

問題介紹 這是個超級超級經典的分治演算法!!這個問題大致是說,如何在給定的兩個有序數組裡面找其中的中值,或者變形問題,如何在2個有序陣列陣列中查詢Top K的值(Top K的問題可以轉換成求第

有序陣列位數

大小m和n分別有兩個排序陣列A和B。找到兩個排序陣列的中值。總的執行時間複雜度應該是O(log(m+n))。class Solution {  public:      /**      * @param A: An integer array.      * @param 

LeetCode 4. Median of Two Sorted Arrays有序陣列位數求解

一、題目描述 給定兩個已經排好序的陣列nums1和nums2,長度分別是m和n,要求求出這兩個有序數組合並後的新陣列中的中位數,並要求整個程式實現的時間複雜度為O(log(m+n))。例如陣列1 nums1=[1,3],給定的陣列2為 nums2=[2],則輸出陣列[1,2

[LeetCode]4 有序陣列位數

Median of Two Sorted Arrays(兩個有序陣列的中位數) 【難度:hard】 There are two sorted arrays nums1 and nums2 of size m and n respectively. Find

LeetCode-合併有序陣列

.title { text-align: center; margin-bottom: .2em } .subtitle { text-align: center; font-size: medium; font-weight: bold; margin-top: 0 } .todo { font-famil

LeetCode80. 刪除排序陣列的重複項 II(Remove Duplicates from Sorted Array II)

【 英文練習 | 中文練習 】 題目描述: 給定一個排序陣列,你需要在原地刪除重複出現的元素,使得每個元素最多出現兩次,返回移除後陣列的新長度。 不要使用額外的陣列空間,你必須在原地修改輸入陣列並在使用 O(1) 額外空間的條件下完成。 解題思路 : 遍歷一遍陣列,用一個下標

leetcode 合併有序陣列

給定兩個有序整數陣列 nums1 和 nums2,將 nums2 合併到 nums1 中,使得 num1 成為一個有序陣列。 說明: 初始化 nums1 和 nums2 的元素數量分別為 m 和 n。 你可以假設 nums1 有足夠的空間(空間大小大於或等於 m

LeetCode448. 找到所有陣列消失的數字

1.題目 給定一個範圍在 1 ≤ a[i] ≤ n ( n = 陣列大小 ) 的 整型陣列,陣列中的元素一些出現了兩次,另一些只出現一次。 找到所有在 [1, n] 範圍之間沒有出現在陣列中的數字。

Leetcode108. 將有序陣列轉換為二叉搜尋樹

題目描述:將一個按照升序排列的有序陣列,轉換為一棵高度平衡二叉搜尋樹。本題中,一個高度平衡二叉樹是指一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過 1。示例:給定有序陣列: [-10,-3,0,5,9], 一個可能的答案是:[0,-3,9,-10,null,5],

Java實現O(log(n+m))有序陣列第K大元素或位數

假設有兩個從小到大的有序陣列A和B,他們的元素個數為N和M,那麼怎麼求得其第K大元素呢?同理,求其中位數就是當N+M為奇數求其第(N+M+1)/2大元素,為偶數時求(N+M)/2和(N+M+2)/2大元素的平均值。 那麼我們怎麼才能求得第K大元素呢? 分別取兩個陣列中間索

(分治演算法)有序陣列位數和Top K問題

問題介紹 這是個超級超級經典的分治演算法!!這個問題大致是說,如何在給定的兩個有序數組裡面找其中的中值,或者變形問題,如何在2個有序陣列陣列中查詢Top K的值(Top K的問題可以轉換成求第k個元素的問題)。這個演算法在很多實際應用中都會用到,特別是在當前大資料的

Algorithm 04 : 尋找有序陣列的第N個數,要求時間複雜度為O(logm+logn)

Question : Give a divide and conquer algorithm for the following problem : you are given two sorted lists of size m and n

LeetCode295. 資料流的位數 結題報告 (C++)

原題地址:https://leetcode-cn.com/problems/find-median-from-data-stream/ 題目描述: 中位數是有序列表中間的數。如果列表長度是偶數,中位數則是中間兩個數的平均值。 例如, [2,3,4] 的中位數是 3 [2,3] 的

死磕演算法之1刷Leetcode——找出有序陣列位數Median of Two Sorted ArraysO(log(m+n))

Median of Two Sorted Arrays 題目難度:hard 題目要求: There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two s

LeetCode88. 合併有序陣列

題目連結:https://leetcode-cn.com/problems/merge-sorted-array/description/ 題目描述 給定兩個有序整數陣列 nums1 和 nums2,將 nums2 合併到 nums1 中,使得 num1 成為一個有序陣列。

LeetCode88 合併有序陣列

給定兩個有序整數陣列 nums1 和 nums2,將 nums2 合併到 nums1 中,使得 num1 成為一個有序陣列。 說明: 初始化 nums1 和 nums2 的元素數量分別為 m 和 n。 你可以假設 nums1 有足夠的空間(空間大小大於或等於 m +n)來儲存 num

LeetCode88. 合併有序陣列(Merge Sorted Array)

【 英文練習 | 中文練習 】 題目描述: 給定兩個有序陣列,合併它們,合併之後的陣列依舊有序。 解題思路: 從後向前存放。 public void merge(int[] nums1, int m, int[] nums2, int n) { if(nums

LeetCode 簡單題17-合併有序陣列

宣告: 今天是第17道題。給定兩個有序整數陣列 nums1 和 nums2,將 nums2 合併到 nums1 中,使得 num1 成為一個有序陣列。以下所有程式碼經過樓主驗證都能在LeetCode上執行成功,程式碼也是借鑑別人的,在文末會附上參考的部落格連結,如果侵犯了博

leetcode#陣列Python88. Merge Sorted Array 合併有序陣列

題目: 給定兩個有序整數陣列 nums1 和 nums2,將 nums2 合併到 nums1 中,使得 num1 成為一個有序陣列。 說明: 初始化 nums1 和 nums2 的元素數量分別為 m