Python程序员面试算法宝典---解题总结: 第1章 链表 1.11 如何判断两个链表(无环)是否交叉

#!/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()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值