【问题标题】:Find common nodes from two linked lists using recursion使用递归从两个链表中查找公共节点
【发布时间】:2014-07-11 02:35:46
【问题描述】:

我必须编写一个方法,该方法返回一个链表,其中包含使用递归的两个链表共有的所有节点,没有循环。

例如,

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

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

将返回的列表是 2 -> 10

我对此无能为力..我一直想到的是递归地检查第一个列表的每个值和第二个列表的每个值,但是第二个列表每次都会被一个节点切割,我无法比较第一个列表中的下一个值与第二个列表。我希望这是有道理的......

谁能帮忙?

【问题讨论】:

  • 如果您的列表中包含唯一值并且已排序,那么每次找到匹配项时是否将第二个列表减一也没关系。

标签: java algorithm recursion


【解决方案1】:

只有对每个列表中的值进行排序时,这个问题才有分量。如果是这种情况,那么这将递归地找到重复项(在伪代码中)

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)。它通过为副本创建新节点来非破坏性地做到这一点。如果您愿意,您可以轻松地将其修改为从其中一个列表中“窃取”。

【讨论】:

    【解决方案2】:

    这个问题取决于约束。

    最简单、最幼稚的解决方案是,如果您有两个大小为 n 的元素,则遍历一个列表并将其与第二个列表中的每个项目进行比较。

    解:O(n2)

    当然你可以做得更好。

    现在,如果您有可用的 HashSet(或其他接近 O(1))数据结构,那么您可以这样做:

    遍历一个列表。将每个元素添加到集合中。遍历第二个列表。如果元素在集合中,则将其添加到结果列表中。

    解:O(n)

    【讨论】:

      【解决方案3】:

      如果链表已经排序,那么你可以非常有效地应用递归 这是来自 GeeksforGeeks

      http://www.geeksforgeeks.org/intersection-of-two-sorted-linked-lists/ 看看第三个选项。

      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;
      }
      

      【讨论】:

        【解决方案4】:

        如果您不关心重复,使用 Set 的内置 retainAll() 方法是一个简单的解决方案。

          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);
        

        【讨论】:

        • 必须使用递归。我不确定retainAll 是如何工作的,但即使它确实在内部使用了递归,我怀疑它是否适合这个任务的目的。
        【解决方案5】:

        有很多方法可以解释这个问题。我们是在寻找由列表表示的集合的交集,还是在寻找最长的公共子序列?列表总是排序的吗?

        在我的递归解决方案中,我假设我们正在寻找一些最长的子序列,并且我不假设关于项目顺序的任何事情:

        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)。

        【讨论】:

          【解决方案6】:

          好的,除了你的要求之外,我不会对你想要的东西做出任何假设。下面是一个递归函数,它找到两个链表的共同元素。它需要 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);
                  }
          
              }
          

          【讨论】:

            【解决方案7】:

            有如下两个链接列表:

            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 = 第一个链表的第2个节点 6. ptr2 = 第二个链表的 1 个节点 7.在链表1中,链表2中的3rd-->next和c-->next将指向同一个节点,即第4个节点,因此是合并节点。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-11-06
              • 1970-01-01
              • 1970-01-01
              • 2020-10-31
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多