算法记录 | Day4链表基础Ⅱ

87 阅读5分钟

算法记录 | Day4链表基础

昨天没有更新,实在是没有能挤出做题的时间。( ̄︶ ̄)

LeetCode 24-两两交换链表中的节点

题目描述:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

输入:head = [1,2,3,4]
输出:[2,1,4,3]

题目链接:leetcode.cn/problems/sw…

解题思路
  • 想清楚交换过程就不是很难,正常跟链表基础操作差不多。

双指针法

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 两两交换相邻链表中的节点
        # 头节点的转换和中间节点转换不一样,设置虚拟头节点与其他是一样的,更方便。
        dummy_node = ListNode(next=head)
        cur = dummy_node
        while cur.next and cur.next.next:
            p = cur.next
            q = cur.next.next
            # cur.next= cur.next.next
            cur.next = q
            p.next = q.next
            q.next = p
            cur = cur.next.next
        return dummy_node.next
难点
  • 总是企图用最少的指针来做,像只设置两个指针就直接写了 # cur.next= cur.next.next 然后就发现中间的节点就这样被我删除了。只得老老实实多写个q。ಠ_ಠ
  • 注意考虑交换结点的条件,需要至少两个结点的存在。
总结

本题不是很难,画出节点来,试着交换一次就知道过程了。


LeetCode 19-删除链表的倒数第N个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

题目链接:leetcode.cn/problems/re…

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
解题思路
  • 链表的基本的查找操作- 先找到倒数第n+1个结点

    • 思考:链表是从头开始遍历的,如何找到第 n+1 个
    • ① 先从头遍历到尾,求出链表长度,再求倒数第n个。
    • ② 设置快慢指针,两者相差k,快的到最后一个节点,慢的就指向倒数第k+1个
  • 删除操作

解法

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        # 设置头节点
        pre = ListNode(next = head)
        fast = slow = pre
        while (n+1):
            fast = fast.next
            n -= 1
        while fast:
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next
        return pre.next

难点
  • 快慢指针第一次接触的时候想不到,现在自然而然地知道了,可能也因为前不久还做了双指针吧。
总结

这道题的关键点在于如何找到倒数第n+1个节点的位置


LeetCode 2.07-链表相交

题目描述:给两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

题目链接:leetcode.cn/problems/in…

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
  • 思路:算出两个链表的长度,长度相互作差,使两个指针在同一个起始位置。然后知道有两个指针相等的位置。返回该节点。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        p_A = headA
        p_B = headB
        cur_A = headA
        cur_B = headB
        count_A = 0
        count_B = 0
        while p_A:
            p_A = p_A.next
            count_A += 1
        while p_B:
            p_B = p_B.next
            count_B += 1
        if count_A > count_B:
            k = count_A - count_B
            for i in range(k):
                cur_A = cur_A.next
        else:
            k = count_B - count_A
            for i in range(k):
                cur_B = cur_B.next
        while cur_A != cur_B:
            cur_A = cur_A.next
            cur_B = cur_B.next
        return cur_A
难点
  • 如何使两个指针在同一个初始位置,我之前做过这类题,所以知道解法,然后瞬间写出解法通过了。
总结

题目关键在于如何找到两个指针同时移动的位置,否则暴力解法一个一个的进行比较过于复杂了。


LeetCode 142-环形链表

题目描述:给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

题目链接:leetcode.cn/problems/li…

解题思路
  • 思路:快慢指针的方法判断是否有环
  • 如何判断链表是否有环-再次经过同一位置-第一个想法是利用哈希表,遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。

方法一:哈希表

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
# 这个答案就直接通过了
class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        # 直接利用哈希表记录
        dicts = dict()
        p = head
        while p:
            if p not in dicts:
                dicts[p] = 1
                p = p.next
            else:
                # print(dicts)
                return p

方法二:快慢指针

  • 快慢指针主要是思路不太容易想到
  • 如何判断有环:快指针fast走两个节点,慢指针slow走一个节点,有环的时候,一定会在环内相遇。因为快指针一定会在环中的某个节点追上慢指针(快指针比慢指针多走一个节点,两者的差距每次都会增加一个,则即快指针每次靠近慢指针一个节点,直到相遇)
  • 如何找入口:这个是按照数学来计算的
class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        # 方法二:快慢指针
        if head and head.next:
            fast = head.next.next
            slow = head.next
        else:
            return None
        
        while fast:
            # 快慢指针相遇
            if fast != slow:
                if fast.next:
                    fast = fast.next.next
                else:
                    return None
                slow = slow.next
            # 两者相遇之后找入口
            else:
                p = head
                while p != slow:
                    slow = slow.next
                    p = p.next
                return p
难点
  • 快慢指针的两个关键点理解
总结

总算补上了最后这一题,很容易忘记这种解法,看看快慢指针的题可能和别的题目可以关联起来的。