簡體   English   中英

查找在某些情況下需要少於 O(m+n) 次比較的兩個已排序數組的交集

[英]Find intersection of two sorted arrays which in some cases require less than O(m+n) comparisons

這是在O(m+n)中執行此操作的一種方法,其中mn是兩個數組的長度:

import random

def comm_seq(arr_1, arr_2):
    if len(arr_1) == 0 or len(arr_2) == 0:
        return []

    m = len(arr_1) - 1
    n = len(arr_2) - 1

    if arr_1[m] == arr_2[n]:
        return comm_seq(arr_1[:-1], arr_2[:-1]) + [arr_1[m]]

    elif arr_1[m] < arr_2[n]:
        return comm_seq(arr_1, arr_2[:-1])

    elif arr_1[m] > arr_2[n]:
        return comm_seq(arr_1[:-1], arr_2)


if __name__ == "__main__":
    arr_1 = [random.randrange(0,5) for _ in xrange(10)]
    arr_2 = [random.randrange(0,5) for _ in xrange(10)]
    arr_1.sort()
    arr_2.sort()
    print comm_seq(arr_1, arr_2)

是否有一種技術在某些情況下使用少於O(m+n)比較? 例如: arr_1=[1,2,2,2,2,2,2,2,2,2,2,100]arr_2=[1,3,100]

(不是在尋找哈希表實現)

二分搜索算法需要O(logm)時間才能在長度為 m 的數組中找到一個數字。 因此,如果我們從長度為 m 的數組中搜索長度為 n 的數組的每個數字,則其總時間復雜度為O(nlogm) 如果 m 遠大於 n ,則O(nlogm)實際上小於O(m+n) 因此,在這種情況下,我們可以基於二分搜索實現一個新的更好的解決方案。 來源

然而,這並不一定意味着二分搜索在 O(m+n) 的情況下更好。 實際上,只有當 n << m(n 與 m 相比非常小)時,二進制搜索方法才會更好。

據我所知,有幾種不同的方法可以解決這個問題,但沒有一種比 O(m + n) 更好 我不知道你怎么能有一個比這更快的算法(除非奇怪的量子計算答案),因為你必須比較兩個數組中的所有元素,否則你可能會錯過重復。

蠻力使用兩個嵌套的 for 循環。 從第一個數組中取出每個元素並在第二個數組中對其進行線性搜索。 O(M*N) 時間,O(1) 空間

地圖查找使用查找結構,如哈希表或二叉搜索樹。 將所有第一個數組放入映射結構中,然后遍歷所有第二個數組並查找映射中的每個元素以查看它是否存在。 無論數組是否排序,這都有效。 對於二叉搜索樹時間為 O(M*log(M) + N*log(M)) 或對於哈希表為 O(M + N) 時間,兩者都是 O(M) 空間。

二分搜索類似於蠻力,但從第一個數組中取出每個元素並在第二個數組中對其進行二分搜索。 O(m*log(N)) 時間,O(1) 空間

Parallel Walk類似於歸並排序的合並部分。 在每個數組的前面有兩個指針。 比較兩個元素,如果它們相等,則存儲重復項,否則將指針移到較小的值一個位置並重復,直到到達數組之一的末尾。 O(M + N) 時間,O(1) 空間

無論如何,您必須檢查兩個數組中的每個元素,否則您將不知道是否找到了所有重復項。 您可以爭論一個數組更大或更小的邊緣情況,但這不適用於您考慮所有輸入范圍的算法。

可以用一個hash_table來保存大數組,然后掃描另一個小數組,計算兩個數組的交集。

import random

def comm_seq(arr_1, arr_2):
    if len(arr_1) < len(arr_2): arr_1, arr_2 = arr_2, arr_1
    cnt = {}
    for item in arr_1: 
        cnt.setdefault(item, 0)
        cnt[item] += 1
    # save the large array in a hash_table
    ret = []
    for item in arr_2:
        p = cnt.get(item, 0)
        if p: 
            ret.append(item):
            cnt[item] -= 1
    # scan the small array and get the answer
    return ret

if __name__ == "__main__":
    arr_1 = [random.randrange(0,5) for _ in xrange(10)]
    arr_2 = [random.randrange(0,5) for _ in xrange(10)]
    arr_1.sort()
    arr_2.sort()
    print comm_seq(arr_1, arr_2)

如果我們考慮py-dictionary操作的復雜度為O(1),則總復雜度為O(min(n, m))

如果您使用單邊搜索和正常二分搜索的組合,則可以使用 O(N*log(M/N)) 比較的算法。 在最壞的情況下(當兩個數組的大小相同時)這等於 O(N) = O(M + N) 次比較。 這里 M 是最大數組的大小,N 是較小數組中不同元素的數量。

獲取兩個數組中最小的一個,並在第二個數組中搜索其每個元素。 從單邊二分搜索開始:嘗試位置 M/N, 2*M/N, 4*M/N, ... 直到找到一個大於必要的元素。 然后使用正常的二分搜索找到位置 0 和 2 k *M/N 之間的元素。

如果找到匹配元素,則使用單邊搜索和普通二分搜索的相同組合來查找重復匹配元素運行的結束位置,並將適當數量的匹配元素復制到輸出。 您可以使用相同的二進制搜索組合來計算較小數組中重復元素的數量,並獲取這些重復計數中的最小值以確定結果中應包含多少元素。

要繼續處理較小數組中的下一個元素,請使用較大數組中的起始位置,即上一步結束的位置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM