簡體   English   中英

python最低共同祖先

[英]python lowest common ancestor

在Python中實現最低共同祖先的最簡單方法是什么? 我有一個樹,每個節點都有一個指向其父節點的節點,我希望能夠找到給定兩個節點的第一個共同祖先。 我想出了幾個想法,但沒有一個特別有吸引力

  1. 讓每個節點包含其基數列表,並執行連接,找到最長的公共前綴,然后取最后一個元素。 不幸的是,我不知道有任何內置的方法來做最長的公共前綴,所以這需要手動循環。

  2. 讓每個節點包含一組基礎並執行集合交集,並獲取最大元素。 但這需要定義自定義比較運算符,我甚至不確定它是否可行。

我該怎么辦? 我正在尋找一些有利於簡化而不是性能的東西,因此需要復雜處理的解決方案已經完成。

編輯:我發現雖然沒有內置方式,但你可以使用zip在一行中做最長的公共前綴,所以它仍然相當簡單。

common = [x for x in zip(*baselists) if len(set(x)) == 1][-1]

假設您無法修改樹以包含深度,您可以執行以下操作:

對於每個節點,遞歸地向上遍歷樹,直到您到達根。 在每個父節點上,將節點插入list 這應該給你list_alist_b 迭代最短列表,比較每個列表中的元素。 當您找到一個不匹配的條目時,前一個條目是您最大的父元素。

取每個節點的深度(與根的距離)。 如果一個低於另一個,則從下部節點向上移動樹,直到深度相等。 然后檢查身份,每次檢查失敗時在每一側移動每個身份。

您可以使用單個while循環執行此操作。 一旦你選擇了同等深度的祖先:

while (ancestorA !== ancestorB) {
    ancestorA = ancestorA.parent();
    ancestorB = ancestorB.parent();
}

當while循環終止時, ancestorAancestorB將各自成為你的共同祖先。

這不僅應該非常簡單,而且還要相當快。

Python有內置集。 為什么不使用(偽代碼)的東西:

a_ancestors = set()
while a:
  a_ancestors.add(a)
  a = a.parent

while b:
  if b in a_ancestors:
    return b
  b = b.parent

return None # <- can't reach that for a *tree* !!!

這將構建節點a (包括自身)的所有祖先的(無序)集合。

然后,我們第二次循環遍歷b的所有祖先。 根據定義,B中的第一個祖先是祖先將是第一個共同的祖先。 這適用於O(n) (空間和時間)


您可以通過同時收集ab的祖先集合來加速進程(最終以占用空間為代價) - 在找到公共節點時立即停止。 代碼有點做作,因為你必須處理其中一個分支到達另一個之前的根:

visited = set()
while a or b:
  if a:
    if a in visited:
      return a
    visited.add(a)
    a = a.parent

  if b:
    if b in visited:
      return b
    visited.add(b)
    b = b.parent

return None # <- can't reach that for a *tree* !!!

我想這取決於你的樹,以及它將包含多少個對象。 如果這個數字在記憶方面是合理的(可能少於幾百萬個節點,但這只是我個人的猜測),我會使用你的建議#2。 在集合中只保留每個基數的字符串表示,因此內置比較將起作用。 應該非常快,我想你可以用幾行代碼實現它。 如果字符串表示不實用,或者如果您需要對象本身並且無法實現所有對象的主dict,則只需在節點對象中定義比較方法(如果我記得,則為eqneq )。

想要維護父項集合,最好使用帶有=的哈希映射,因為在這種情況下,您不會在父項列表中搜索登錄。 因此,在每次迭代時檢查此映射,如果當前節點的父節點已存在於映射中,則此父節點是您的結果。 在最壞的情況下,它會給出O(n),但是如果你在某些情況下開始對這兩個節點進行分析,你將能夠更快地找到它。

因為在這兩種情況下你已經有一個指向父節點的指針,為什么不這樣做:(類似於weirdcanada所說但是...)

創建每個節點父節點的列表,在每個階段按順序構建列表。 所以當你走得更高時, list_alist_b會增長。 將每個列表添加的最后一項與另一項的項進行比較。 只要list_a中的項與list_b中的某些內容匹配,您就擁有最低的共同祖先。

while (parent_a not in list_b) or (parent_b not in list_a):
    ...

你不需要一直重新構建鏈直到root。 在任何情況下,您都必須按順序檢查每個父母(向前或向后)。

暫無
暫無

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

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