簡體   English   中英

使用遞歸從兩個鏈接列表中查找公共節點

[英]Find common nodes from two linked lists using recursion

我必須編寫一種方法,該方法使用遞歸在沒有循環的情況下返回兩個鏈表共有的所有節點的鏈表。

例如,

第一個列表是2-> 5-> 7-> 10

第二個列表是2-> 4-> 8-> 10

將返回的列表是2-> 10

我對此一無所知。我一直想到的是遞歸檢查第二個列表的每個值與第一個列表的每個值,但是第二個列表每次都會被一個節點切掉,因此我無法比較下一個值在第一個列表中與第二個列表中。 我希望這是有道理的...

有人可以幫忙嗎?

如果對每個列表中的值進行排序,則此問題僅具有權重。 如果是這樣,那么它將以遞歸方式(以偽代碼)找到重復項

Node merge(Node n1, Node n2) {
   IF n1 == null OR n2 == null
      RETURN null
   ELSE IF n1.value == n2.value
      Node dupNode(n1.value);
      dupNode.next = merge(n1.next, n2.next);
      RETURN dupNode;
   ELSE IF n1.value < n2.value
      RETURN merge(n1.next, n2)
   ELSE
      RETURN merge(n1, n2.next)
}

給定長度L1L2的列表,這會將它們合並到O(L1 + L2) 通過為重復項創建新節點,它可以進行非破壞性操作。 如果願意,您可以輕松地將其修改為從列表之一“竊取”。

如果鏈表已經排序,那么您可以非常有效地應用遞歸,這來自GeeksforGeeks

http://www.geeksforgeeks.org/intersection-of-two-sorted-linked-lists/查看第3個選項。

struct node *sortedIntersect(struct node *a, struct node *b)
{
    /* base case */
    if (a == NULL || b == NULL)
        return NULL;

    /* If both lists are non-empty */

    /* advance the smaller list and call recursively */
    if (a->data < b->data)
        return sortedIntersect(a->next, b);

    if (a->data > b->data)
        return sortedIntersect(a, b->next);

    // Below lines are executed only when a->data == b->data
    struct node *temp = (struct node *)malloc(sizeof(struct node));
    temp->data = a->data;

    /* advance both lists and call recursively */
    temp->next = sortedIntersect(a->next, b->next);
    return temp;
}

此問題取決於約束。

最簡單,最幼稚的解決方案是,如果您有兩個大小為n元素,則遍歷一個列表並將其與第二個列表中的每個項目進行比較。

解:O(n 2

但是,當然您可以做得更好。

現在,如果您有可用的HashSet (或其他Near-O(1))數據結構,則可以這樣做:

遍歷一個列表。 將每個元素添加到集合中。 遍歷第二個列表。 如果元素在集合中,則將其添加到結果列表中。

解:O(n)

如果您不關心使用Set的內置保留方法AllAll(),那么這是一個簡單的解決方案。

  List<T> list1 = ...; // The smaller list
  List<T> list2 = ...; 

  ...
  final Set<T> s1 = new HashSet<T>(list1);
  s1.retainAll(list2); 
  // Try s1.retainAll(new HashSet<T>(list2)); if the lists are really bug

  final List<T> solution = new LinkedList(s1);

有很多方法可以解釋這個問題。 我們是在尋找列表代表的集合的交集,還是在尋找最長的公共子序列? 列表總是排序嗎?

在我的遞歸解決方案中,我假設我們正在尋找最長的子序列 ,並且我不假設有關項目訂單的任何信息:

private static <T> List<T> longestCommonSubseq(List<T> a, int indA, List<T> b, int indB){
    if (indA == a.size() || indB == b.size())
        return Collections.emptyList();

    T itemA = a.get(indA);
    T itemB = b.get(indB);

    List<T> res;
    if (itemA.equals(itemB)){
        res = new ArrayList<T>();
        res.add(itemA);
        res.addAll(longestCommonSubseq(a, indA+1, b, indB+1));
    }else{
        List<T> opt1 = longestCommonSubseq(a, indA+1, b, indB);
        List<T> opt2 = longestCommonSubseq(a, indA, b, indB+1);
        if (opt1.size()>opt2.size())
            res = opt1;
        else
            res = opt2;
    }
    return res;
}

public static <T> List<T> longestCommonSubseq(List<T> a, List<T> b){
    return longestCommonSubseq(a,0,b,0);
}

注意:為簡單起見,在我的解決方案中,列表應該是隨機訪問的(例如ArrayList)。

好的,我沒有對您想要的超出您要求的任何假設。 下面是一個遞歸函數,該函數查找兩個鏈接列表的共同元素。 這需要O(n ^ 2)的時間,這與您的設置所獲得的時間一樣。

請注意,雖然這是尾遞歸的,但Java(通常)不會對其進行優化,因此這會浪費長列表的堆棧。

    import java.util.*;

    public class CommonNodeLinkedList {
        public static void main(String[] args) {
            List<Integer> list1_items = Arrays.asList(2, 5, 7, 10);
            List<Integer> list2_items = Arrays.asList(2, 4, 8, 10);

            LinkedList<Integer> list1 = new LinkedList<Integer>();
            list1.addAll(list1_items);
            LinkedList<Integer> list2 = new LinkedList<Integer>();
            list2.addAll(list2_items);

            System.out.println("List 1      : " + list1);
            System.out.println("List 2      : " + list2);
            System.out.println("Common Nodes: " + findCommonNodes(list1, list2));
        }

        public static LinkedList<Integer> findCommonNodes(LinkedList<Integer> list1,
LinkedList<Integer> list2) {
            return findCommonNodes_helper(list1, list2, new LinkedList<Integer>());
        }

        public static LinkedList<Integer> findCommonNodes_helper(LinkedList<Integer> list1,
LinkedList<Integer> list2,
LinkedList<Integer> result) {
            if (list1.isEmpty()) return result;
            Integer head = list1.pop();
            if (list2.contains(head)) {
                result.add(head);
            }
            return findCommonNodes_helper(list1, list2, result);
        }

    }

有如下兩個鏈接列表:

1 ---> 2 ---> 3 ---> 4 ---> 5 ---> 6 ---> 7 ---> 8

a ---> b ---> c ---> 5 ---> 6 ---> 7 ---> 8

然后我們需要找出合並節點。

算法:

  1. 計算第一個鏈接列表的長度,假設它是“ len1”。
  2. 計算第二個鏈接列表的長度,假設它是“ len2”。
  3. 找出len1和len2中更大的長度。 在給定的示例中,len1> len2。
  4. 在給定的示例中找出len1和len2的正差| len1-len2 |
  5. 現在在大鏈接列表上取1個指針(ptr1)並將其放在| len1-len2 | 從一開始的位置。
  6. 取1個指針(ptr2),並將其放在“鏈接”列表2的開頭。
  7. 兩個指針一個接一個地增加,並檢查它們是否一致,這就是兩個鏈接列表的合並點。

給定示例的算法:1. len1 = 8 2. len2 = 7 3. len1> len2 4. | len1-len2 | = 1 5. ptr1 =第一個鏈接列表6的2個節點。ptr2 =第二個鏈接列表7的1個節點。在鏈接列表1中,鏈接列表2中的3rd-> next和c-> next將指向同一節點,該節點是第4個節點,因此是合並節點。

暫無
暫無

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

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