#!/usr/bin/env python
# encoding: utf-8
'''
Python程序员面试算法宝典---解题总结: 第1章 链表 1.11 如何判断两个链表(无环)是否交叉
题目:
单链表相交指的是两个链表存在完全重合的部分,如下图所示:
head1->1->2->3->4
->5->6->7
head2->1->2->3->4
在上图中,这两个链表相交于结点5,要求判断两个链表是否相交,如果相交,找出相交处的结点。
分析:
方法1:
两个链表相交的最大特点就是从某个结点开始,两个链表各自对应结点是相同的,一直到链表的最后结点都一样。
基于这个特点,假如分别把两个链表逆置,
然后判断两个链表的各自对应结点是否相同,如果相同,说明链表是交叉的;
两个链表各自继续向下遍历,直到某对结点不同,则不同的这对结点之前的一个结点就是相交处的结点。
这样的话: 等于要遍历两遍链表。
方法2:
假设分别知道链表1的长度为L1, 链表2的长度为L2
那如果相交,也必定相交处后面两个链表剩余的结点相同。
如果L1 > L2, dis=L1 - L2,
让链表1先走dis步,然后两个链表一起走,各自比对对应结点是否相同;
否则, dis = L2 - L1
让链表2先走dis步,然后两个链表一起走,各自比对对应结点是否相同。
问题的关键就变成了:
如何知道两个链表的长度,这个至少先遍历两个链表各一遍。
总结:
在两个链表长度未知的情况下,可以用方法2,方法1毕竟会修改链表的结构,
不推荐用方法1.
是否自己解出:
无。
书上最优解法:
将两个链表连接起来,然后创建两个指针,一个指针利每次走两步,
另一个指针每次走一步,如果两个指针汇合,说明是两个链表是有
交叉的。
那么问题的关键就是判定交叉的第一个节点是哪里。
举例如下:
假设第一个链表为:
head1->1->3->5->7->9
假设第二个链表为:
head2->2->4->6->7->9
假设两个指针相交于节点7
研究这两个链表:
假设链表2加入到链表1的后面,变成
head1->1->3->5->7->9->head2->2->4->6->7->9
假设第一个指针为curr1,指向head1,每次走1步
第二个指针为curr2,指向head1,每次走2步
则行进的路径依次如下:
head1 ->1 ->3 ->5 ->7 ->9 ->head2 ->2 ->4 ->6 ->7 ->9
因为考虑到交叉的第二个链表的7,9结点和第一个链表的7,9节点是一样,则可以删除链表2中的7,9节点,最后简化为如下形式:
head1 ->1 ->3 ->5 ->7 ->9 ->head2 ->2 ->4 ->6
|
<------------------------
再对问题进行简化,删除head2,那么变成如下形式:
head1 ->1 ->3 ->5 ->7 ->9 ->2 ->4 ->6
|
<---------------
其中6指向节点7
为了抽象问题,假设存在这样一个环形链表:
除了头结点以外,共有n个节点,从第k个节点开始出现环,
假设存在两个指针curr1,curr2,两者均从头结点开始,
curr1指针每次走一步,curr2指针每次走两步,两者相遇时,curr1走了s步,
环的大小为m。
求k的大小。
现在需要寻找一个等式:
curr2走了: 2s步
curr1的s步中: 有k-1步是用于环外面的行走,s-k+1步用于环内部的行走
curr2的2s步中: 有k-1步是用于环外面的行走,2s-k+1步用于环内部的行走,并且
curr1的路径: 用#{n}表示第几步停留在对应的结点上, curr2的路径用 *{n}表示第几步停留在对应的结点上
#1 #2 #3 #4 #5 #6 #7 #8
#9 #10
*1 *2 *3 *4
*5 *6
链表长度n=8, 从第k=4个节点开始出现环,慢指针走了s=5步的时候相遇
根据上述分析,curr1和curr2相遇的地方为节点9,但是head2并不是两个链表的交叉点,
假设curr1走了k步,则curr2走了2k步
curr1走了:
测试用例:
关键:
1 将两个链表首尾相连,转换为求环形链表
2 分析环形链表特点:
假设环长为r,快指针每次走2步,慢指针每次走1步,相遇的时候慢指针走了s步。
相遇时,快指针已经在环内走了n圈(1<=n),则
2s = s + nr
则: s=nr
假设链表长度为L,链表到环入口的长度为a, 相遇点到环入口的长度为x
(注意,这里不是环入口的长度到相遇点,两者不一样),那么:
a+x实际就是上述的s,即
a+x=s=nr=(n-1)r + r = (n-1)r + L - a
所以:
a = (n-1)r + (L -a -x)
其中L -a -x 表示的是环入口到相遇点的长度
则上述
a = (n-1)r + (L -a -x)
表示:
链表头到环入口的距离=(n-1)*环长 + 环入口到相遇点的长度
此时从链表头和相遇点各设置一个指针,每次各走一步,一旦
且相遇的第一点为环入口点
参考:
Python程序员面试算法宝典
'''
class Node(object):
def __init__(self, data=None, nextNode=None):
self.data = data
self.nextNode = nextNode
def buildList(arr):
if not arr:
return
head = Node()
current = head
for data in arr:
newNode = Node(data)
current.nextNode = newNode
current = newNode
return head
def printList(head):
if not head:
return
if not head.nextNode:
return
current = head.nextNode
result = ""
while current:
if "" == result:
result += str(current.data)
else:
result += "->" + str(current.data)
current = current.nextNode
print result
def construstIntersectList(head1, head2, meetValue):
if not head1 or not head2 or meetValue is None:
return
meetNode = head1.nextNode
while meetNode:
if meetNode.data == meetValue:
break
meetNode = meetNode.nextNode
prev = head2
curr = head2.nextNode
while curr:
if curr.data == meetValue:
prev.nextNode = meetNode
break
prev = curr
curr = curr.nextNode
def mergeTwoList(head1, head2):
if not head1 and not head2:
return head1
elif not head1:
return head2
elif not head2:
return head1
prev = head1
curr = head1.nextNode
while curr:
prev = curr
curr = curr.nextNode
prev.nextNode = head2.nextNode
return head1
def isIntersected(head1, head2):
if not head1 or not head2 or not head1.nextNode or not head2.nextNode:
return
newHead = mergeTwoList(head1, head2)
fast = newHead.nextNode
slow = newHead.nextNode
while fast:
fast = fast.nextNode.nextNode if fast.nextNode else None
slow = slow.nextNode
if fast == slow:
return slow
return
def findLoopBegin(head, meetNode):
if not head or not head.nextNode or not meetNode:
return
first = head.nextNode
second = meetNode
while second:
if first == second:
return first
first = first.nextNode
second = second.nextNode
return
def process():
arr1 = [1, 3, 5, 7, 9]
arr2 = [2, 4, 6, 7, 9]
head1 = buildList(arr1)
head2 = buildList(arr2)
construstIntersectList(head1, head2, 7)
printList(head1)
printList(head2)
result = isIntersected(head1, head2)
if result != None:
print "Intersect"
else:
print "Not Intersect"
beginLoopNode = findLoopBegin(head1, result)
print beginLoopNode.data if beginLoopNode else ""
if __name__ == "__main__":
process()