链表的题目总结

本文总结了链表中常见的快慢指针应用,如寻找链表环、查找交点和反转链表等。通过具体例子阐述了快慢指针在解决链表问题中的作用,同时还探讨了单链表与双链表的选择考量,并列举了一些经典的链表题目及其解题技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一  单链表中的双指针

快慢指针找环的问题

如果存在环,快指针总能追上慢指针。

什么速度合适?其他速度怎么样?

例子:

 Linked List Cycle

Given a linked list, determine if it has a cycle in it.

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        if head is None or head.next is None:
            return False
        
        slow = head
        fast = head.next
        # 比较安全的速度是fast是slow的两倍,也就是说每次fast比slow多跑一格。
        # 如果fast过快,而且环很小的话,fast每次恰好跨过一个环,和slow永远无法相遇
        # 如果fast过快,需要判断的条件过长 fast.next fast.next.next fast.next.next...
        while fast.next and fast.next.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                return True
        return False

 Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

比较简单的做法,用hash表记录访问过的位置。空间是O(n)的。快慢指针的方法空间是O(1)。

快慢指针相遇的点是不确定的,但是快指针比慢指针多走过的距离是确定,恰好等于慢指针到环入口的位置。

用三个阶段,很容易解释:

设起点到环入口点的距离为x, 环的的长度为c,

一, 当slow指针到达入口点的时候,t = x次,  fast移动的距离为 2 * x,相当于fast不仅到达了环而且已经在环中走了x的距离,此时fast距离slow的距离为 c - x

二 在环中的追击问题,fast 距离 slow 为 c - x,又因为fast的相对速度为1, 所以 移动 t = c - x次后, slow和fast相遇。当slow和fast相遇,此时slow 走过了 t * 1 = c - x 的距离, 那么相遇点距离 入口的距离就是 c - (c - x) =  x

三 得到了距离关系,从起点到入口点距离为x, 从相遇点到入口点的距离也是x,所以两个点同时同速移动就一定会在入口点

相遇。

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        cur = head
        visited = set()

        while cur:
            if cur in visited:
                return cur
            else:
                visited.add(cur)
                cur = cur.next
        return None
        
        
            
#         if head is None or head.next is None:
#             return None
        
#         slow = head
#         fast = head
        
#         hascycle = False
        
#         while fast.next and fast.next.next:
#             fast = fast.next.next
#             slow = slow.next
#             if slow == fast:
#                 hascycle = True
#                 break
        
#         if not hascycle:
#             return None
        
#         slow = head
#         while slow != fast:
#             slow = slow.next
#             fast = fast.next
#         return slow

Intersection of Two Linked Lists

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

class Solution(object):
#     def getIntersectionNode(self, headA, headB):
#         """
#         :type head1, head1: ListNode
#         :rtype: ListNode
#         """
#         visited = set()
#         while headA:
#             if headA not in visited:
#                 visited.add(headA)
#                 headA = headA.next
        
#         while headB:
#             if headB in visited:
#                 return headB
#             else:
#                 headB = headB.next


#     def getIntersectionNode(self, headA, headB):
#         if headA is None or headB is None:
#             return None

#         pa = headA # 2 pointers
#         pb = headB

#         while pa is not pb:
#             # 找到两条链的长度差:短的到了末尾就 回到长的头。 在移动个 长度差,此时就能站到同一起跑线
#             pa = headB if pa is None else pa.next
#             pb = headA if pb is None else pb.next

#         return pa


    # 拼接两条链表,会形成一个环,然后找环即可
    def getIntersectionNode(self, A, B):
        if not A or not B: return None

        # Concatenate A and B
        last = A
        while last.next: last = last.next
        last.next = B

        # Find the start of the loop
        fast = slow = A
        while fast and fast.next:
            slow, fast = slow.next, fast.next.next
            if slow == fast:
                fast = A
                while fast != slow:
                    slow, fast = slow.next, fast.next
                last.next = None
                return slow

        # No loop found
        last.next = None
        return None
                
                
                

Remove Nth Node From End of List

Given a linked list, remove the n-th node from the end of list and return its head.

class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """        
        curr = head
        tail = head
        for i in range(n):
            tail = tail.next
            
        if tail is None:
            return head.next
               
        while tail.next:
            curr = curr.next
            tail = tail.next
            
        curr.next = curr.next.next
        
        return head

快慢指针的痛点:

只需要判断fast是不是空即可(fast比slow快),

如果要判断fast.next 就必须是fast and fast.next(会报None没有next的错误)

总结:

快慢指针基本上都是用来找差值的,然后再去利用这差值去做事情。

二 单链表中的经典问题

Reverse Linked List  

is the foundation of many linked-list problems

class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        prev = None
        while head:
            curr = head
            head = head.next
            curr.next = prev
            prev = curr
        
        return prev
        

Remove Linked List Elements

Remove all elements from a linked list of integers that have value val.

要点:为了不对head做特殊处理,使用dummy head指向head

class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        dummy = ListNode(-1)
        dummy.next = head
        
        curr = dummy
        while curr.next:
            if curr.next.val == val:
                curr.next = curr.next.next
            else:
                curr = curr.next
                
        return dummy.next

 Odd Even Linked List

Given a singly linked list, group all odd nodes together followed by the even nodes. 

要点:时刻关注链表的结构变化,使用了一个dummy去保存head.next,因为在while循环后, head和原先的head.next断开

class Solution(object):
    def oddEvenList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head:
            return head
        
        odd = head
        even = head.next
        dummy = head.next
        
        while even and even.next:
            odd.next = odd.next.next
            odd = odd.next
            
            even.next = even.next.next
            even = even.next
        
        odd.next = dummy
        
        return head

Palindrome Linked List

Given a singly linked list, determine if it is a palindrome.

1. 先用快慢指针找到中点:

 关于初始的slow和fast如何设置?slow和fast都设在head,中点偏左,fast在slow后一位,中点偏右。

2 反转slow后的链表,反转后的链表mid指向空

3 head => mid <= tail:遍历一遍,当left和righ指向空的时候,说明遍历完成。

class Solution(object):
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        # find midian
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        
        # reverse the link from slow to end            
        prev = None
        while slow:
            curr = slow
            slow = slow.next
            curr.next = prev
            prev = curr
        
        # traversal the link node
        left = head
        right = prev
        while left and right:
            if left.val == right.val:
                left = left.next
                right = right.next
            else:
                return False
        
        return True

三 双链表和单链表的比较

If you need to add or delete a node frequently, a linked list could be a good choice.

If you need to access an element by index often, an array might be a better choice than a linked list.

四 经典题目

Merge Two Sorted Lists

Merge two sorted linked lists and return it as a new list. 

技巧:构造一个dummy head,用or拼接剩下的部分

class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        cur1 = l1
        cur2 = l2
        head = ListNode(-1)
        dummy = head
        while cur1 and cur2:
            if cur1.val < cur2.val:
                head.next = cur1
                cur1 = cur1.next
            else:
                head.next = cur2
                cur2 = cur2.next
            
            head = head.next
        
        head.next = cur1 or cur2
        # if cur1:
        #     head.next = cur1
        # if cur2:
        #     head.next = cur2
        
        return dummy.next

 Add Two Numbers

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)

Output: 7 -> 0 -> 8


class Solution(object):
#     def addTwoNumbers(self, l1, l2):
#         """
#         :type l1: ListNode
#         :type l2: ListNode
#         :rtype: ListNode
#         """       
#         head1 = l1
#         head2 = l2
        
#         res = 0
#         n = 0
#         while head1 and head2:
#             res += pow(10, n) * (head1.val + head2.val)
#             n += 1
#             head1 = head1.next
#             head2 = head2.next
        
#         head = head1 or head2
#         while head:
#             res += pow(10, n) * head.val
#             n+= 1
#             head = head.next

#         if res == 0:
#             return ListNode(0)
        
#         out = ListNode(-1)
#         dummy = out
#         while res:
#             res, mod = divmod(res, 10)
#             out.next = ListNode(mod)
#             out = out.next
        
#         return dummy.next
        def addTwoNumbers(self, l1, l2):
            carry = 0
            root = n = ListNode(0)
            while l1 or l2 or carry:
                v1 = v2 = 0
                if l1:
                    v1 = l1.val
                    l1 = l1.next
                if l2:
                    v2 = l2.val
                    l2 = l2.next
                carry, val = divmod(v1+v2+carry, 10)
                n.next = ListNode(val)
                n = n.next
            return root.next

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值