1. 程式人生 > >(分治演算法)兩個有序陣列中的中位數和Top K問題

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

問題介紹

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

我覺得下面的這個思路特別好,特別容易理解!!請按順序看。是來自leetcode上的stellari英文答案,我整理並自己修改了一下。

預備知識

先解釋下“割”

我們通過切一刀,能夠把有序陣列分成左右兩個部分,切的那一刀就被稱為割(Cut),割的左右會有兩個元素,分別是左邊最大值和右邊最小值。
我們定義L = Max(LeftPart),R = Min(RightPart)

Ps. 割可以割在兩個數中間,也可以割在1個數上,如果割在一個數上,那麼這個數即屬於左邊,也屬於右邊。(後面講單陣列中值問題的時候會說)

比如說[2 3 5 7]這個序列,割就在3和5之間
[2 3 / 5 7]
中值就是(3+5)/2 = 4

如果[2 3 4 5 6]這個序列,割在4上,我們可以把4分成2個
[2 3 (4/4) 5 7]
中值就是(4+4)/2 = 4

這樣可以保證不管中值是1個數還是2個數都能統一運算。

割和第k個元素

對於單陣列,找其中的第k個元素特別好做,我們用割的思想就是:

常識1:如果在k的位置割一下,然後A[k]就是L。換言之,就是如果左側有k個元素,A[k]屬於左邊部分的最大值。(都是明顯的事情,這個不用解釋吧!)

雙陣列

我們設:
CiCi為第i個數組的割。
LiLi為第i個數組割後的左元素.
RiRi為第i個數組割後的右元素。

這裡寫圖片描述

如何從雙數組裡取出第k個元素

這裡寫圖片描述

  1. 首先Li<=RiLi<=Ri是肯定的(因為陣列有序,左邊肯定小於右邊)
  2. 如果我們讓L1<=R2L1<=R2 && L2<=R1L2<=R1
  3. 那麼左半邊 全小於右半邊,如果左邊的元素個數相加剛好等於k,那麼第k個元素就是Max(L1,L2),參考上面常識1。
  4. 如果 L1>R2,說明陣列1的左邊元素太大(多),我們把C1減小,把C2增大。L2>R1同理,把C1增大,C2減小。

假設k=3

對於
[1479][1 4 7 9]
[235][2 3 5]

設C1 = 2,那麼C2 = k-C1 = 1
[14/79][1 4/7 9]
[2/35][2/3 5]

這時候,L1(4)>R2(3),說明C1要減小,C2要增大,C1 = 1,C2=k-C1 = 2
[1/479][1/4 7 9]
[23/5][2 3/5]

這時候,滿足了L1<=R2L1<=R2 && L2<=R1L2<=R1,第3個元素就是Max(1,3) = 3。

如果對於上面的例子,把k改成4就恰好是中值

下面具體來看特殊情況的中值問題。

雙陣列的奇偶

中值的關鍵在於,如何處理奇偶性,單陣列的情況,我們已經討論過了,那雙陣列的奇偶問題怎麼辦,m+n為奇偶處理方案都不同。

讓陣列恆為奇數

有沒有辦法讓兩個陣列長度相加一定為奇數或偶數呢?

其實有的,虛擬加入‘#'(這個trick在manacher演算法中也有應用),讓陣列長度恆為奇數(2n+1恆為奇數)。
Ps.注意是虛擬加,其實根本沒這一步,因為通過下面的轉換,我們可以保證虛擬加後每個元素跟原來的元素一一對應

對映關係

這有什麼好處呢,為什麼這麼加?因為這麼加完之後,每個位置可以通過/2得到原來元素的位置。

在虛擬數組裡表示“割”

不僅如此,割更容易,如果割在‘#'上等於割在2個元素之間,割在數字上等於把數字劃到2個部分。

奇妙的是不管哪種情況

Li = (Ci-1)/2
Ri = Ci/2

例:

  1. 割在4/7之間‘#',C = 4,L=(4-1)/2=1 ,R=4/2=2
    剛好是4和7的原來位置!
  2. 割在3上,C = 3,L=(3-1)/2=1,R=3/2 =1,剛好都是3的位置!

剩下的事情就好辦了,把2個數組看做一個虛擬的陣列A,目前有2m+2n+2個元素,割在m+n+1處,所以我們只需找到m+n+1位置的元素和m+n+2位置的元素就行了。
左邊:A[m+n] = Max(L1+L2)
右邊:A[m+n+1] = Min(R1+R2)

Mid = (A[m+n]+A[m+n+1])/2
= (Max(L1+L2) + Min(R1+R2) )/2

至於在兩個數組裡找割的方案,就是上面的方案。

分治的思路

有了上面的知識後,現在的問題就是如何利用分治的思想。

怎麼分?

最快的分的方案是二分,有2個數組,我們對哪個做二分呢?
根據之前的分析,我們知道了,只要C1或C2確定,另外一個也就確定了。這裡,為了效率,我們肯定是選長度較短的做二分,假設為C1。

怎麼治?

也比較簡單,我們之前分析了:就是比較L1,L2和R1,R2。

  • L1>R2,把C1減小,C2增大—> C1向左二分
  • L2>R1,把C1增大,C2減小—> C1向右二分

越界問題

如果C1或C2已經到頭了怎麼辦?
這種情況出現在:如果有個陣列完全小於或大於中值。可能有4種情況:

  • C1 = 0 —— 陣列1整體都比中值大,L1 >R2,中值在2中
  • C2 = 0 —— 陣列1整體都比中值小,L1 <R2,中值在1中
  • C1 = n*2 —— 陣列1整體都比中值小,L1 <R2,中位數在2中
  • C2 = m*2 —— 陣列1整體都比中值大,L1 >R2,中位數在1中

考慮下面兩種情況了,解決方案:

  • 如果C1 = 0 —> 那麼我們縮小L1,L1 = INT_MIN,保證判斷正確。
  • 如果C1 = n*2 —> 那麼我們增大R1,R1 = INT_MAX,保證判斷正確。

剩下兩種情況解決方案類似。

程式碼

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size();
        int m = nums2.size();
        if(n > m)   //保證陣列1一定最短
            return findMedianSortedArrays(nums2,nums1);
        int L1,L2,R1,R2,c1,c2,lo = 0, hi = 2*n;  //我們目前是虛擬加了'#'所以陣列1是2*n+1長度
        while(lo <= hi)   //二分
        {
            c1 = (lo+hi)/2;  //c1是二分的結果
            c2 = m+n- c1;
            L1 = (c1 == 0)?INT_MIN:nums1[(c1-1)/2];   //map to original element
            R1 = (c1 == 2*n)?INT_MAX:nums1[c1/2];
            L2 = (c2 == 0)?INT_MIN:nums2[(c2-1)/2];
            R2 = (c2 == 2*m)?INT_MAX:nums2[c2/2];

            if(L1 > R2)
                hi = c1-1;
            else if(L2 > R1)
                lo = c1+1;
            else
                break;
        }
        return (max(L1,L2)+ min(R1,R2))/2.0;
    }
};

相關推薦

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

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

資料結構演算法題/有序陣列位數

有三種方法,時間複雜度分別是O(m+n) ,O(k),O(log(m+n)) 注意點: 判斷合併後的陣列的元素個數是奇數還是偶數 如果是奇數取中間值;如果是偶數取中間2個數的平均值。 兩種求中位數的方法: (1)方法1,判斷奇數個還是偶數個 if (lengthall % 2 == 0)

leetcode演算法題4: 尋找有序陣列位數

題目:給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2。 請你找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 O(log(m + n))。 你可以假設 nums1 和 nums2 不會

【死磕演算法之1刷Leetcode】——找出有序陣列位數【Median of Two Sorted Arrays】O(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

LeetCode演算法4:java 尋找有序陣列位數

題目描述 給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2。 請你找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 O(log(m + n))。 你可以假設 nums1 和 nums2 不會同時為空。 示例 1: nums1 = [1, 3] n

Leetcode演算法——4、有序陣列位數

題目 There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overal

有序陣列位數-演算法導論

Question There are 2 sorted arrays A and B of size n each. Write an algorithm to find the median of the array obtained after merging th

LeetCode之4. 尋找有序陣列位數

LeetCode之4. 尋找兩個有序陣列的中位數 給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2。 請你找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 O(log(m + n))。 你可以假設 nums1 和 nums2 不會同時為空。

Leetcode(4)尋找有序陣列位數

題目描述 給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2。 請你找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 O(log(m + n))。 你可以假設 nums1 和 nums2 不會同時為空。 示例 1: nums1 = [1, 3] nums2 = [2]

尋找有序陣列位數-- leetCode

尋找兩個有序陣列的中位數 給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2。 請你找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 O(log(m + n))。 你可以假設 nums1 和 nums2 不會同時為空。 示例 1: nums1 =

leetcode第四題:有序陣列位數

給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2。 請你找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 O(log(m + n))。 你可以假設 nums1 和 nums2 不會同時為空。 示例

LeetCode千題斬之找出有序陣列位數

題目: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 ti

尋找有序陣列位數

題目:給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2。          請你找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 O(log(m + n))。          你

LeetCoe No.3 20181114_有序陣列位數

原題: 給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2 。 請找出這兩個有序陣列的中位數。要求演算法的時間複雜度為 O(log (m+n)) 。 你可以假設 nums1 和 nums

4. 尋找有序陣列位數(python3)

 Python3: class Solution: def findMedianSortedArrays(self, nums1, nums2): """ :type nums1: List[int] :type nums2: L

[LeetCode] Median of Two Sorted Arrays 有序陣列位數

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 shou

尋找有序陣列位數(LeetCode)

想法如下: 當數的個數為奇數時,中位數就這一堆已經排好序的數最中間那一個 當數的個數為偶數時,中位數就是中間那兩個數的平均數 class Solution:     def findMedianSortedArrays(self, nums1, nums2): &nb

查詢有序陣列位數

問題描述 問題很簡單,就是在兩個有序的整數數組裡(陣列A長度為m, 陣列B長度為n),找到兩個數組合並後的中位數。 要求時間複雜度 O(log(m+n)) 也就是說不能用先歸併兩個陣列再直接查詢的辦法。 中位數 中位數就是在一個有序陣列中,位於中間的數

Leetcode 4. Median of Two Sorted Arrays 尋找有序陣列位數

Leetcode 4. Median of Two Sorted Arrays 尋找兩個有序陣列的中位數 標籤: Leetcode 題目地址:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/ 題目

leetcode(2)尋找有序陣列位數的js實現

一.題目描述: 給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2。 請你找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 O(log(m + n))。 你可以假設 nums1 和 nums2 不會同時為空。 示例 1: nums1 = [1, 3