簡體   English   中英

僅使用python列表(不設置)的列表交集算法實現

[英]list intersection algorithm implementation only using python lists (not sets)

我一直在嘗試用python寫下一個列表交集算法,該算法負責重復。 我是python和編程的新手,如果聽起來效率不高,請原諒我,但我無能為力。 這里,L1和L2是所討論的兩個列表,L是交集。

  1. 遍歷L1
  2. 遍歷L2
  3. 如果元素在L1和L2中
  4. 將其添加到L
  5. 從L1和L2刪除
  6. 遍歷L
  7. 將元素添加回L1和L2

我100%肯定這不是Mathematica中用於評估列表交集的算法,但是我真的無法提出更有效的方法。 我不想在此過程中修改L1和L2,因此我將交集重新添加到兩個列表中。 有任何想法嗎? 我不想利用列表以外的任何內置函數/數據類型,因此沒有導入集或類似的東西。 就我而言,這是算法和實現練習,而不是編程練習。

遍歷L1所有內容,每次遍歷L2所有內容,都將花費二次時間。 改進的唯一方法是避免迭代所有L2 (存在類似的問題,最后從L刪除重復項。)

如果對L2 (和L )使用set ,那么in L2步驟中的每個步當然都是恆定時間,因此整個算法是線性的。 而且,您始終可以構建自己的哈希表實現,而不是使用set 但這是很多工作。

使用二叉搜索樹,甚至只是一個排序列表和binary_find函數,都可以在O(N log N)中進行。 而且, binary_find更容易編寫自己。 所以:

S2 = sorted(L2)
L = [element for element in L1 if binary_find(element, S2)]
S = remove_adjacent(sorted(L))

或者,更簡單地說,也對L1進行排序,那么您就不需要remove_adjacent

S1, S2 = sorted(L1), sorted(L2)
L = []
for element in S1:
    if binary_find(element, S2) and (not L or L[-1] != element):
        L.append(element)

無論哪種方式,這都是O(N log N),其中N是較長列表的長度。 相比之下,原始答案是O(N ^ 2),其他答案是O(N ^ 3)。 當然,它有點復雜,但是仍然很容易理解。

您需要編寫binary_find (如果適用,還需要編寫remove_adjacent ),因為我假設即使您不想使用額外的內置函數,也不想使用stdlib中的內容。 但這真的很容易。 例如:

def binary_find(element, seq):
    low, high = 0, len(seq), 
    while low != high:
        mid = (low + high) // 2
        if seq[mid] == element:
            return True
        elif seq[mid] < element:
            low = mid+1
        else:
            high = mid
    return False

def remove_adjacent(seq):
    ret = []
    last = object()
    for element in seq:
        if element != last:
            ret.append(element)
        last = element
    return ret

如果您甚至不想使用sortedlist.sort ,也可以很容易地編寫自己的排序。

這是一個更快的解決方案:

def intersect_sorted(a1, a2):
  """Yields the intersection of sorted lists a1 and a2, without deduplication.

  Execution time is O(min(lo + hi, lo * log(hi))), where lo == min(len(a1),
  len(a2)) and hi == max(len(a1), len(a2)). It can be faster depending on
  the data.
  """
  import bisect, math
  s1, s2 = len(a1), len(a2)
  i1 = i2 = 0
  if s1 and s1 + s2 > min(s1, s2) * math.log(max(s1, s2)) * 1.4426950408889634:
    bi = bisect.bisect_left
    while i1 < s1 and i2 < s2:
      v1, v2 = a1[i1], a2[i2]
      if v1 == v2:
        yield v1
        i1 += 1
        i2 += 1
      elif v1 < v2:
        i1 = bi(a1, v2, i1)
      else:
        i2 = bi(a2, v1, i2)
  else:  # The linear solution is faster.
    while i1 < s1 and i2 < s2:
      v1, v2 = a1[i1], a2[i2]
      if v1 == v2:
        yield v1
        i1 += 1
        i2 += 1
      elif v1 < v2:
        i1 += 1
      else:
        i2 += 1

它以O(min(n + m, n * log(m)))時間運行,其中n是長度的最小值,而m是最大值。 它同時遍歷兩個列表,並在開頭盡可能地跳過盡可能多的元素。

可在此處進行分析: http : //ptspts.blogspot.ch/2015/11/how-to-compute-intersection-of-two.html

怎么樣:

  1. 遍歷L1
  2. 遍歷L2
  3. 如果(在L1和L2中)而不在L中->添加到L

效率不是特別高,但是在代碼中看起來像這樣(重復說明):

>>> L1 = [1,2,3,3,4]
>>> L2 = [2,3,4,4,5]
>>> L = list()
>>> for v1 in L1:
        for v2 in L2:
            if v1 == v2 and v1 not in L:
                L.append(v1)
>>> L
[2,3,4]

您只需通過檢查元素是否已在L中,然后將其添加到L中來避免從L1和L2中刪除。 然后,L1和L2中是否存在重復都沒關系。

編輯:我讀錯了標題,並瀏覽了內置部分。 我還是要把它留在這里,可能會幫助別人。

您可以使用set類型來實現。

>>> a = [1,2,3,4]
>>> b = [3,4,5,6]
>>> c = list(set(a) & set(b))
>>> c
[3, 4]
  1. 做一個臨時清單。
  2. 遍歷兩個列表之一。 哪一個都沒關系。
  3. 對於每個元素,請檢查該元素是否在另一個列表中( if element in list2 )並且不在您的臨時列表中(相同的語法)
  4. 如果兩個條件都成立,請將其附加到您的臨時列表中。

我為發布解決方案感到難過,但說實話,它比我的文字可讀性強:

def intersection(l1, l2):
    temp = []

    for item in l1:
        if item in l2 and item not in temp:
            temp.append(item)

    return temp

以下是一種Python高效的方法,用於計算兩個列表的交集以保留順序並消除重復項:

L1 = [1,2,3,3,4,4,4,5,6]
L2 = [2,4,6]
aux = set()
L = [x for x in L1 if x in L2 and not (x in aux or aux.add(x)) ]

該解決方案使用集合“ aux”來存儲已經添加到結果列表中的元素。

請注意,您不需要“導入”集合,因為它們是Python中的本機數據類型。 但是,如果您堅持不使用集合,則可以選擇使用列表的效率較低的版本:

L1 = [1,2,3,3,4,4,4,5,6]
L2 = [2,4,6]
aux = []
L = [x for x in L1 if x in L2 and not (x in aux or aux.append(x)) ]

暫無
暫無

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

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