python数据结构之链表——带环链表及交叉链表(双指针法)

这一篇是LeetCode上关于链表的两道题目,属于做过就会,没做过难死的那种。都不是常规的单链表,一个是两个单链表交叉,一个是带环链表,均用到双指针的办法解决。

1)160.Intersection of Two Linked Lists 两链表交叉

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.

无交叉返回none,链表没有环,链表需保留原始结构。时间复杂度O(n),空间复杂度O(1)。

解决方法用到了双指针,代码如下十分简洁,跑的很快。想不到这种方法就会陷入江局,或者写了十分复杂的一段code。

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        p1, p2 = headA, headB
        while p1 is not p2:
            p1 = headB if not p1 else p1.next
            p2 = headA if not p2 else p2.next
        return p1

其实这一方法的原理与下面带环的链表的原理基本相似,放在一起看大彻大悟。

链表a和b的长度不知,但是取两个指针,分别从a,b开始,走到末尾后走另一个链表,那么二者的步伐一定在交叉处一致、相遇,(因为二者都走了a+b+c,所以在c1处相遇)如果没有交叉点,则指针a,b全都走到末尾None,返回的也是None。

2)142. Linked List Cycle II 带环的链表

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Note: Do not modify the linked list.

Follow up:Can you solve it without using extra space?

力扣的题目往往就是如此简洁有力,连示例都没有,就是解决一个很明确的问题。(虽然有许多题没有示例造成题意理解偏差)

看如下代码再解析:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head == None:
            return None
        pslow = head
        pfast = head
        k = 0
        while pfast.next and pfast.next.next:
            pslow = pslow.next
            pfast = pfast.next.next
            k += 1
            if pslow == pfast:
                break
        if pfast.next==None or pfast.next.next==None:
            return None
        # 确定有环,开始寻找
        p1 = head
        p2 = pslow
        while p1!=p2:
            p1 = p1.next
            p2 = p2.next
        return p1

这一方法是关于有环链表的龟兔算法,鄙人也是学习了一个才写的,当然盆友们可以去看题目后面的讨论。设置两个指针,一个快(每次两步)一个慢(每次一步),此时想象在操场上绕圈跑的样子。第一部分判断是否有环(141的题目就是这样的),此时快指针在环上第二圈,遇到了刚第一圈的慢指针,如此确定有环;第二部分找到环的起点,一个从头开始走,一个从相遇处开始走,相遇处为环的起点。参考如下解释:

my solution is like this: using two pointers, one of them one step at a time. another pointer each take two steps. Suppose the first meet at step k,the length of the Cycle is r. so…2k-k=nr,k=nr
Now, the distance between the start node of list and the start node of cycle is s. the distance between the start of list and the first meeting node is k(the pointer which wake one step at a time waked k steps).the distance between the start node of cycle and the first meeting node is m, so…s=k-m,

s=nr-m=(n-1)r+(r-m),here we takes n = 1…so, using one pointer start from the start node of list, another pointer start from the first meeting node, all of them wake one step at a time, the first time they meeting each other is the start of the cycle.

这两题都是双指针,放在一起可以发现,构造两个指针让他们走合适的距离(一般是相等的距离),相遇在你想要的位置。这就是解决这一类问题的基本思路,再面对交叉的链表、有环的链表的问题可以在此基础上进行设计。

猜你喜欢

转载自blog.csdn.net/xutiantian1412/article/details/79714705