繁体   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