LeetCode 相交链表

编写一个程序,找到两个单链表相交的起始节点。python

 

例如,下面的两个链表:spa

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

在节点 c1 开始相交。.net

 

注意:指针

  • 若是两个链表没有交点,返回 null.
  • 在返回结果后,两个链表仍须保持原有的结构。
  • 可假定整个链表结构中没有循环。
  • 程序尽可能知足 O(n) 时间复杂度,且仅用 O(1) 内存。

 

 方法一:code

 1 /* C++ */
 2 /**  3  * Definition for singly-linked list.  4  * struct ListNode {  5  * int val;  6  * ListNode *next;  7  * ListNode(int x) : val(x), next(NULL) {}  8  * };  9  */
10 class Solution { 11 public: 12     ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { 13         if(headA == NULL || headB == NULL){ 14             return NULL; 15  } 16         ListNode *a = headA; 17         ListNode *b = headB; 18         // 计算A和B的长度
19         int lenA = 0,lenB = 0; 20         while(a->next != NULL){ 21             a = a->next; 22             lenA++; 23  } 24         while(b->next != NULL){ 25             b = b->next; 26             lenB++; 27  } 28         int step = lenB - lenA; 29         if(step>0){ 30             //说明B长
31             a = headB; 32             b = headA; 33         }else{// 说明A长
34             a = headA; 35             b = headB; 36             step *= -1; 37  } 38         while(step -- ){ 39             a = a->next; 40  } 41         while(a != b){ 42             a = a->next;b = b->next; 43  } 44         return a; 45  } 46 };

 python:blog

 1 # Definition for singly-linked list.
 2 # class ListNode(object):
 3 # def __init__(self, x):
 4 # self.val = x
 5 # self.next = None
 6 
 7 class Solution(object):  8     def getIntersectionNode(self, headA, headB):  9         """
10  :type head1, head1: ListNode 11  :rtype: ListNode 12         """
13         if headA==None or headB==None: 14             return None 15          # 计算A和B的长度
16         lenA, lenB = 0, 0 17         a, b = headA, headB 18         while a.next != None: 19             a = a.next 20             lenA += 1
21         while b.next != None: 22             b = b.next 23             lenB += 1
24         if lenA > lenB: 25             step, a, b = lenA - lenB, headA, headB 26         else: 27             step, a, b = lenB - lenA, headB, headA 28         
29         while step: 30             step -= 1
31             a = a.next 32         
33         while a != b: 34             a, b = a.next, b.next 35         return a

 

 

方法二:图片

  原理来自:如何判断单链表是否有环、环的入口、环的长度和总长 - CSDN博客内存

1)首先判断链表是否有环get

  要想判断有环,咱们能够联系实际生活中的例子,很容易就想到操场上跑圈,由于是环形,因此快的确定会追上慢的,因此咱们能够应用到链表上,用一个快指针和一个慢指针,可是细想一下发现,咱们在跑操的时候相遇时坐标位置不必定是整数啊(这里相比链表节点而言的),而链表是一个节点链接起来,咱们怎么作,能让他们在节点上相遇呢,这里就要为2个指针找到合适的速度,使之可以恰巧在某一结点上相遇。博客

原理:若是快的指针走到NULL,说明无环;而fast==slow相遇,则证实确定存在环。

公式推导

  为何存在环的状况下,两个指针会相遇呢?如下推到n都是指 环长!

这里写图片描述 
1.假定2个指针同一个起点 
  们让两个指针所有指向头节点,而后给slow指针的速度为一步,而fast指针的速度为M步,则在第i次迭代的时候,slow指针走到i mod n,而fast指向Mi mod n,要想slow和fast相遇,则i mod n=Mi mod n,(M-1)i mod n,则咱们能够令M=2(最小的能够取得值),i mod n = 0,则 i=n时,相遇,因此咱们能够给fast 2倍的速度,这样它们会在 最后一个节点相遇。 
2.假定不在同一个起点,而且fast提早K位置 
  其实这个相似链表中含有个小环的状况,即不是全部点在环中的状况,这样当slow即将进入环状的时候,fast已经在环中k mod n位置了,因此问题转化为假定不在同一个起点,而且fast提早K位置,是否会在一点相遇? 
这里写图片描述
  fast的速度仍设置为2倍,假定第i次迭代时,slow指向i mod n,fast指向k+2i mod n,其k大于0小于你,那么i ≡ (2i+k)(mod n) -> (i+k) mod n = 0 -> 当i=n-k时,p与q相遇。 
这里写图片描述
变相理解,如何同一个起点出发,他们会在整圈(也就是最后一个节点)相遇,如今fast在提早K位置出发,这样就会使相遇点原本是最后节点,如今fast少走k步,便可与slow相遇,因此在n-K位置相遇。相似问题求倒数第K个节点:http://blog.csdn.net/dawn_after_dark/article/details/73611115 
因此无论是图1的链表,仍是图2的链表,只要有环,快指针跟慢指针相遇,逆命题也成立;全部当快指针跟慢指针相遇,就必定存在环。 

2)找到两链表的交点

  咱们已经在上面的讨论中,已经得知slow与fast会在环中n-k位置相遇,咱们先靠主观方面来探讨这个问题,两个指针同时从头节点开始走,当慢指针即将进入环中的时候,快指针位于k mod n,说明慢指针走的这段路程也能对应k mod n, 由于快指针是慢指针速度的2倍,因此快指针在环中走的距离与慢指针走的距离同样。而咱们发现相遇点位于n-k,再走k步就能够到达环的入口,而且慢指针走的路程也能对应k mod n,因此咱们再令取2指针,一个指向头节点,另外一个指向碰撞点,都以1步的速度前进,这两个指针相遇点就是环的入口,这个结论适用于全环的链表,由于这时k=0,头节点走一步就到了环的入口了。 
  以上只是咱们主观的理解方式,若是采用推导呢,slow走过的路程为s,环长为n,因此,2s=s+k+(m-1)n,化简为s=k+(m-1)n,因此slow在环外至关于走了k+(m-1)n。 
  而碰撞点位于n-k的位置,因此要想走到环入点,则须要走k+mn步,这时你就会发现只要让这两个指针从头节点与碰撞点以一步的速度过来,就必定会在环入点相遇,从而求出环入点!

 

简言之:

  若存在环:

  slow指针速度为1,fast指针速度为2。当slow到达环入口点时,fast已经在环内走了k步,这时候slow总共走了k步,fast总共走了2*k步,也就是环外的链表的长度为k。若slow又走了i步,与fast相遇(此时fast又走了2*i步),这时候有

 i ≡ (2i+k)(mod n) 

即:(i+k) mod n = 0

即:当i=n-k时,p与q相遇

  因此这时候再从相遇点走k步即到达环的入口点。因此咱们可使用一个新指针new从环外链表的头开始走,同时slow继续走,直到相遇,相遇点即为环的入口点。

 1 # Definition for singly-linked list.
 2 # class ListNode(object):
 3 # def __init__(self, x):
 4 # self.val = x
 5 # self.next = None
 6 
 7 class Solution(object):  8     def getIntersectionNode(self, headA, headB):  9         """
10  :type head1, head1: ListNode 11  :rtype: ListNode 12         """
13         if headA == None or headB == None: 14             return None 15         
16         # 检查是否有交点, 若是没有交点,返回None
17         a, b = headA, headB 18         while a.next!=None: 19             a = a.next 20         while b.next!=None: 21             b = b.next 22         if a.val != b.val: 23             return None 24         
25         # 接下来找出交点,先将链表B的首尾相连
26         b = headB 27         while b.next != None: 28             b = b.next 29         b.next = headB 30 
31         a = headA.next #慢指针
32         c = headA.next.next #快指针
33         while a.val != c.val: # 找相遇点
34             a, c = a.next, c.next.next 35 
36         c = headA 37         while a.val != c.val: 38             a, c = a.next, c.next 39             
40         # 找到相交点以后,须要将两链表复原
41         b.next = None 42 
43         return c

咱们看到这种方法貌似没有方法一高效::

相关文章
相关标签/搜索