Intersection of Two Linked Lists

本文介绍了一种高效算法,用于在给定的两个单链表中找到它们相交的第一个节点。该算法在O(n)时间内运行,且仅使用O(1)内存。通过分别遍历两个链表并记录长度,可以确定相交点的位置,从而避免了重复遍历整个链表。

Write a program to find the node at which the intersection of two singly linked lists begins.


For example, the following two linked lists:

A:          a1 → a2
                   ↘
                     c1 → c2 → c3
                   ↗            
B:     b1 → b2 → b3

begin to intersect at node c1.


Notes:

  • If the two linked lists have no intersection at all, return null.
  • The linked lists must retain their original structure after the function returns.
  • You may assume there are no cycles anywhere in the entire linked structure.
  • Your code should preferably run in O(n) time and use only O(1) memory.

Credits:
Special thanks to @stellari for adding this problem and creating all test cases.

  public class ListNode {

      int val;

      ListNode next;

      ListNode(int x) {

          val = x;

          next = null;

      }

  }

  

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

        

    }



分析:

这是一个链表的问题,并且已经给出了链表的数据结构的定义。

简而言之,就是给你2个链表,返回两个链表相遇的那个节点。

注意:

1,如果2个链表没有相遇,返回null。

2,函数返回时,不破坏给定的两个链表(java不是值传递么?)

3,假定在整个链表中,是没有环的。

4,你的实现必须是O(n)时间复杂度。O(1)空间复杂度。


解法:

分别遍历两个链表,记录长度(若已知则不需此步),短链表从头结点开始,长链表从第| list1.len - list2.len | 个节点开始,依次遍历并比较,相等的第一个节点则为相交的第一个节点。也可以使用hash法如(2)所示。


格外注意,函数的实参有可能是null的。


  

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
       	if(headA==null||headB==null) return null;
    	ListNode tempA=headA;
    	ListNode tempB=headB;
    	//1计算2个链表的长度。
    	//短的从头开始,长的从长-短的位置开始,因为长度一样了,遍历判断方便多了
    	int lengthA=1;int lengthB=1;
    	while(tempA.next!=null){
    		lengthA++;
    		tempA=tempA.next;
    	}
    	while(tempB.next!=null){
    		lengthB++;
    		tempB=tempB.next;
    	}
    	//开始循环遍历
    	int diff = Math.abs(lengthA-lengthB);
    	if(lengthA>lengthB) while(diff>0){
    		headA=headA.next;
    		diff--;
    	}
    	else if(lengthA<lengthB) while(diff>0){
    		headB=headB.next;
    		diff--;
    	}
    	if(headA.next==null) {
    		if(headA==headB) return headA;
    		else return null;
    	}
        while(headA.next!=null){

        	if(headA==headB) return headA;
        	headA=headA.next;
        	headB=headB.next;
        	if(headA==headB) return headA;
        }
        return null;
    
    }

代码过了,但是实现的太粗糙

以下对使用双指针 `p` 和 `q` 遍历链表来求解两个链表相交节点的逻辑和原理进行分析。 ### 代码逻辑 ```cpp /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { ListNode* p = headA; ListNode* q = headB; while (p != q) { p = p ? p->next : headB; q = q ? q->next : headA; } return p; } }; ``` ### 原理分析 1. **初始化指针**:定义两个指针 `p` 和 `q`,分别指向链表 `A` 和链表 `B` 的头节点。 2. **遍历链表**:使用 `while` 循环,只要 `p` 不等于 `q`,就继续循环。在每次循环中,如果 `p` 不为空,就将 `p` 指向下一个节点;如果 `p` 为空,就将 `p` 指向链表 `B` 的头节点。对于 `q` 指针同理,如果 `q` 不为空,就将 `q` 指向下一个节点;如果 `q` 为空,就将 `q` 指向链表 `A` 的头节点。 3. **找到相交节点**:当 `p` 等于 `q` 时,循环结束,此时 `p`(或 `q`)所指向的节点就是两个链表的相交节点。如果两个链表不相交,那么最终 `p` 和 `q` 都会指向 `nullptr`。 ### 具体解释 - **相交情况**:假设链表 `A` 的长度为 `m`,链表 `B` 的长度为 `n`,两个链表相交部分的长度为 `k`。那么链表 `A` 不相交部分的长度为 `m - k`,链表 `B` 不相交部分的长度为 `n - k`。当 `p` 遍历完链表 `A` 后,会指向链表 `B` 的头节点;当 `q` 遍历完链表 `B` 后,会指向链表 `A` 的头节点。此时,`p` 和 `q` 走过的路程分别为 `m + (n - k)` 和 `n + (m - k)`,由于 `m + (n - k) = n + (m - k)`,所以 `p` 和 `q` 会在相交节点处相遇。 - **不相交情况**:如果两个链表不相交,那么 `p` 和 `q` 会分别遍历完链表 `A` 和链表 `B`,然后同时指向 `nullptr`,此时 `p` 等于 `q`,循环结束,返回 `nullptr`。 ### 复杂度分析 - **时间复杂度**:$O(m + n)$,其中 `m` 和 `n` 分别是两个链表的长度。因为每个指针最多遍历两个链表各一次。 - **空间复杂度**:$O(1)$,只使用了常数级的额外空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值