LeetCode 面试热门 100 题(第 026 ~ 037 题)

LeetCode 面试热门 100 题(第 026 ~ 037 题)

  • day09
    • 206. 反转链表
    • 92. 反转链表 II
    • 25. K 个一组翻转链表
  • day10
    • 234. 回文链表
    • 21. 合并两个有序链表
    • 148. 排序链表
  • day11
    • 23. 合并 K 个升序链表
    • 141. 环形链表
    • 142. 环形链表 II
  • day12
    • 160. 相交链表
    • 19. 删除链表的倒数第 N 个结点
    • 143. 重排链表

day09

206. 反转链表

leetcode链接: https://leetcode.cn/problems/reverse-linked-list/description/

题目要求: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

解题思路一:迭代

  1. 初始化两个指针:prev 和 curr。初始时,prev 设置为 None(因为反转后链表的头节点会指向 None),而 curr 设置为头节点 head。
  2. 遍历链表:使用一个循环,当 curr 不为 None 时继续执行,表示还没有到达链表的末尾。
  3. 反转指针:在每次迭代中,先暂时保存 curr 的下一个节点(因为修改 curr.next 之后就无法访问原来的下一个节点了),然后将 curr.next 指向 prev,从而实现反转。
  4. 移动指针:移动 prev 和 curr。将 prev 移动到 curr 的位置,curr 移动到它原本的下一个节点(即之前保存的那个节点)。
  5. 返回新的头节点:当 curr 到达链表末尾时(即 curr 为 None),循环结束,此时 prev 位置即为反转后的新链表的头节点。
class ListNode(object):
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        # 初始化一个指向None的指针prev,它将用来作为新链表的头部
        prev = None
        # 当前节点指针指向头节点
        curr = head

        # 遍历链表
        while curr:
            # 临时保存当前节点的下一个节点,因为接下来需要修改当前节点的next指针
            next_temp = curr.next
            # 反转当前节点的指针,指向prev
            curr.next = prev
            # 将prev和curr都向前移动一步
            prev = curr
            curr = next_temp

        # 当遍历完成时,prev将会指向新的头节点
        return prev

解题思路二:递归

  1. 递归基准情况:如果头节点 head 为空或者 head.next 为空,意味着链表为空或者只有一个元素,直接返回 head 即可,因为这种情况下反转链表没有实际操作。
  2. 递归步骤:假设链表是 N1 -> … -> Nk-1 -> Nk -> Nk+1 <- … <- Nm,我们希望 Nk+1 的 next 指向 Nk。因此,如果我们已经反转了 Nk+1 到 Nm 的部分,我们希望 Nk.next.next = Nk。
  3. 递归调用:递归地反转 head.next 节点后面的链表。
  4. 链接反转后的链表:将 head 节点放到反转后的链表尾部。
  5. 返回新的头节点:递归的最后一步应该返回新的头节点,这个新的头节点实际上是原链表的最后一个节点。
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        # 递归基准情况
        if head is None or head.next is None:
            return head

        # 递归地反转下一个节点
        reversed_head = self.reverseList(head.next)

        # 将当前节点接到反转部分的尾部
        head.next.next = head
        head.next = None

        # 返回新的头节点
        return reversed_head

92. 反转链表 II

leetcode链接: https://leetcode.cn/problems/reverse-linked-list-ii/description/

题目要求: 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

解题思路一:迭代

  1. 定位反转区间:使用两个指针 prev 和 curr,初始时 prev 指向 None,curr 指向 head。移动这两个指针,直到 curr 指向第 left 个节点。
  2. 记住连接点:在开始反转之前,记住反转部分的前一个节点和开始反转的第一个节点,因为反转后需要重新连接。
  3. 局部反转链表:在 left 和 right 之间迭代,使用与标准链表反转相同的方法来反转节点。
  4. 重新连接:完成局部反转后,需要重新连接反转部分的前后节点。
  5. 返回新的头节点:如果 left 是 1,意味着我们反转了从头节点开始的链表部分,所以新的头节点是反转后的头节点;否则,返回原始头节点。
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def reverseBetween(self, head, left, right):
        """
        :type head: ListNode
        :type left: int
        :type right: int
        :rtype: ListNode
        """
        # 特殊情况:链表为空或不需要反转
        if not head or left == right:
            return head

        # 初始化指针
        dummy = ListNode(0, head)
        prev = dummy

        # 移动prev到反转部分的前一个节点
        for _ in range(left - 1):
            prev = prev.next

        # 开始反转的第一个节点
        curr = prev.next

        # 进行局部反转
        for _ in range(right - left):
            temp = curr.next
            curr.next = temp.next
            temp.next = prev.next
            prev.next = temp

        # 返回头节点,如果反转包括了头节点,则返回新头节点
        return dummy.next

解题思路二:一趟扫描完成反转

  1. 初始化哑节点:创建一个哑节点(dummy node),其 next 指向头节点 head。这有助于简化对链表头部修改的处理。
  2. 定位到反转起始位置:移动一个指针(我们称之为 pre)来定位到 left 的前一个位置。这是反转区域的开始。
  3. 开始反转:使用两个指针 curr 和 next 来进行实际的反转操作。curr 指向 pre 的下一个节点(即反转区域的第一个节点),而 next 是 curr 的下一个节点。在反转过程中,我们会逐步将 next 节点移动到反转部分的起始位置。
  4. 一趟扫描完成反转:继续移动 curr 和 next,直到完成指定区间的反转。
  5. 返回结果:返回哑节点的下一个节点作为新的头节点。
class ListNode(object):
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution(object):
    def reverseBetween(self, head, left, right):
        """
        :type head: ListNode
        :type left: int
        :type right: int
        :rtype: ListNode
        """
        # 特殊情况处理:如果链表为空或不需要反转,则直接返回原链表
        if not head or left == right:
            return head

        # 创建一个哑节点(dummy node),其next指向头节点head。
        # 这有助于简化对链表头部修改的处理。
        dummy = ListNode(0)
        dummy.next = head

        # 初始化pre指针,用于定位到left的前一个位置
        pre = dummy
        for _ in range(left - 1):
            pre = pre.next

        # 初始化curr和next指针,用于实际的反转操作
        curr = pre.next
        next = curr.next

        # 执行反转操作
        for _ in range(right - left):
            # 将next节点移动到反转部分的起始位置
            curr.next = next.next
            next.next = pre.next
            pre.next = next

            # 更新next指针到curr的下一个节点
            next = curr.next

        # 返回哑节点的下一个节点作为新的头节点
        return dummy.next

25. K 个一组翻转链表

leetcode链接: https://leetcode.cn/problems/reverse-nodes-in-k-group/description/

题目要求: 给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

解题思路一:

  1. 创建哑节点:创建一个哑节点(dummy node)并将其 next 指向头节点 head。这有助于简化边界条件的处理。
  2. 检查是否有足够的节点进行翻转:在开始每一次翻转之前,先检查从当前节点开始的下 k 个节点是否存在。如果不足 k 个,不进行翻转。
  3. 翻转 k 个节点:如果有足够的节点进行翻转,则翻转这 k 个节点。
  4. 连接翻转后的链表:将翻转后的子链表重新连接到主链表上。
  5. 处理剩余的节点:如果链表的长度不是 k 的整数倍,那么最后剩余的节点保持原有顺序。
  6. 返回新的头节点:返回哑节点的下一个节点作为新的头节点。
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def reverseKGroup(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if head is None or k == 1:
            return head

        # 创建哑节点
        dummy = ListNode(0)
        dummy.next = head
        pre = dummy

        # 计算链表长度
        count = 0
        while head:
            count += 1
            head = head.next

        while count >= k:
            cur = pre.next
            nex = cur.next
            # 翻转 k 个节点
            for _ in range(1, k):
                cur.next = nex.next
                nex.next = pre.next
                pre.next = nex
                nex = cur.next
            # 移动 pre 到下一组
            pre = cur
            count -= k

        return dummy.next

解题思路二:一个只用 O(1) 额外内存空间

  1. 创建哑节点和初始化指针:创建一个哑节点(dummy node),并将其 next 指向头节点 head。这有助于处理头节点可能发生变化的情况。同时,初始化一个 pre 指针指向哑节点,用于指向每个子列表反转前的第一个节点的前一个节点。
  2. 计算链表长度:遍历整个链表以计算其长度,确定可以进行多少组 k 节点的反转。
  3. 分组并反转每组:在每组 k 个节点内进行原地反转。反转时,需要维护几个指针:curr 指向当前正在处理的节点,next 指向 curr 的下一个节点。
  4. 连接反转后的子链表:完成每组内部节点的反转后,需要将这组节点重新连接回主链表。
  5. 处理剩余节点:如果链表的长度不是 k 的整数倍,剩余节点保持原有顺序。
  6. 返回新的头节点:最后返回哑节点的下一个节点作为新的头节点。
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def reverseKGroup(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        # 创建哑节点
        dummy = ListNode(0)
        dummy.next = head
        pre = dummy

        # 计算链表长度
        length = 0
        while head:
            length += 1
            head = head.next

        # 按照长度进行分组反转
        while length >= k:
            curr = pre.next
            nex = curr.next
            # 反转k个节点
            for _ in range(1, k):
                curr.next = nex.next
                nex.next = pre.next
                pre.next = nex
                nex = curr.next
            # 更新pre为下一组的前一个节点
            pre = curr
            # 减去已处理的节点数
            length -= k

        return dummy.next

day10

234. 回文链表

leetcode链接: https://leetcode.cn/problems/palindrome-linked-list/description/

题目要求:
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

解题思路一:

  1. 找到链表的中点:使用快慢指针的方法找到链表的中间节点。快指针每次移动两步,慢指针每次移动一步。当快指针到达链表末尾时,慢指针将位于中间。
  2. 反转链表的后半部分:从慢指针开始,反转链表的后半部分。反转后,慢指针将指向反转后链表的头节点。
  3. 比较前半部分和反转后的后半部分:从头节点和慢指针开始,逐个比较两部分的节点值。如果所有对应节点的值都相等,则链表是回文的。
  4. 还原链表(可选):如果需要保持链表原有的结构,可以再次反转链表的后半部分,还原链表。
  5. 返回结果:根据比较结果返回 True 或 False。
class ListNode(object):
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution(object):
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        # 找到链表中点
        fast, slow = head, head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next

        # 反转链表后半部分
        prev = None
        while slow:
            temp = slow.next
            slow.next = prev
            prev = slow
            slow = temp

        # 比较前半部分和反转后的后半部分
        left, right = head, prev
        while right:
            if left.val != right.val:
                return False
            left = left.next
            right = right.next

        return True

解题思路二: O(n) 时间复杂度和 O(1) 空间复杂

  1. 快慢指针找中点:使用快慢指针的方法找到链表的中点。快指针每次移动两步,慢指针每次移动一步。当快指针到达链表末尾时,慢指针将位于中间。
  2. 反转链表的后半部分:从慢指针开始,反转链表的后半部分。这样,可以在不使用额外空间的情况下改变链表的部分结构。
  3. 前后半部分比较:将头指针(指向链表前半部分的开始)和慢指针(现在指向反转后的后半部分的开始)用于比较链表的前半部分和后半部分。
  4. 再次反转恢复链表(可选):如果需要保留链表的原始结构,可以再次反转链表的后半部分以恢复链表。
  5. 返回结果:如果前半部分和后半部分的所有元素都相同,则链表是回文的,返回 True,否则返回 False。
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        if head is None or head.next is None:
            return True

        # 快慢指针找中点
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next

        # 反转链表后半部分
        prev = None
        while slow:
            temp = slow.next
            slow.next = prev
            prev = slow
            slow = temp

        # 比较前后半部分
        left, right = head, prev
        while right:
            if left.val != right.val:
                return False
            left = left.next
            right = right.next

        return True

21. 合并两个有序链表

leetcode链接: https://leetcode.cn/problems/merge-two-sorted-lists/description/

题目要求: 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

解题思路一:

  1. 创建哑节点:创建一个哑节点(dummy node)作为新链表的起始节点。这有助于简化边界条件的处理。
  2. 初始化指针:创建一个指针 curr,最初指向哑节点。这个指针将用于构建新的链表。
  3. 比较和合并节点:比较两个链表中的当前节点,并将较小的节点添加到新链表中。移动 list1 或 list2 中较小节点的指针,以及 curr 指针。
  4. 处理剩余节点:如果 list1 或 list2 中还有剩余的节点,将它们添加到新链表的末尾。
  5. 返回合并后的链表:返回哑节点的下一个节点,即合并后的链表的头节点。
class ListNode(object):
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution(object):
    def mergeTwoLists(self, list1, list2):
        """
        :type list1: Optional[ListNode]
        :type list2: Optional[ListNode]
        :rtype: Optional[ListNode]
        """
        # 创建哑节点
        dummy = ListNode(0)
        curr = dummy

        # 合并两个链表
        while list1 and list2:
            if list1.val < list2.val:
                curr.next = list1
                list1 = list1.next
            else:
                curr.next = list2
                list2 = list2.next
            curr = curr.next

        # 添加剩余的节点
        if list1:
            curr.next = list1
        elif list2:
            curr.next = list2

        # 返回合并后的链表的头节点
        return dummy.next

148. 排序链表

leetcode链接: https://leetcode.cn/problems/sort-list/description/

题目要求: 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

解题思路一:迭代

  1. 计算链表长度:首先遍历整个链表,计算其长度。
  2. 迭代分割和合并:从长度为 1 的子链表开始,逐步增加长度,每次将链表分割成多个长度逐渐增加的子链表,然后对这些子链表进行合并。
  3. 合并链表:将分割出的两个子链表合并为一个有序链表。
  4. 重复上述步骤:不断增加子链表的长度,重复分割和合并步骤,直到整个链表变成一个有序链表。
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head

        # 计算链表长度
        length = 0
        current = head
        while current:
            length += 1
            current = current.next

        # 初始化哑节点
        dummy = ListNode(0, head)

        # 每次将链表分割成多个长度逐渐增加的子链表
        subLength = 1
        while subLength < length:
            prev, curr = dummy, dummy.next
            while curr:
                # 分割出两个子链表
                head1 = curr
                for i in range(1, subLength):
                    if curr.next:
                        curr = curr.next
                    else:
                        break
                head2 = curr.next
                curr.next = None
                curr = head2

                for i in range(1, subLength):
                    if curr and curr.next:
                        curr = curr.next
                    else:
                        break

                succ = None
                if curr:
                    succ = curr.next
                    curr.next = None

                # 合并两个子链表
                merged = self.merge(head1, head2)
                prev.next = merged
                while prev.next:
                    prev = prev.next
                curr = succ
            subLength <<= 1
        return dummy.next

    def merge(self, head1, head2):
        """
        合并两个排序好的链表
        """
        dummy = ListNode(0)
        tail = dummy
        while head1 and head2:
            if head1.val < head2.val:
                tail.next = head1
                head1 = head1.next
            else:
                tail.next = head2
                head2 = head2.next
            tail = tail.next
        tail.next = head1 if head1 else head2
        return dummy.next

解题思路二:归并排序( O(1) 的空间复杂度,时间复杂度是 O(n log n))

  1. 找到链表的中点:使用快慢指针法找到链表的中点。快指针每次移动两步,慢指针每次移动一步。当快指针到达链表尾部时,慢指针就位于链表的中点。
  2. 递归分割链表:以中点将链表分割为两部分,然后对每部分递归地应用归并排序。
  3. 合并排序后的链表:将两个排序后的链表合并成一个单一的、排序好的链表。
  4. 递归结束条件:当链表为空或只有一个节点时,不需要排序,直接返回。
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        # 递归结束条件,链表为空或只有一个节点时返回
        if not head or not head.next:
            return head

        # 使用快慢指针法找到链表中点
        slow, fast = head, head.next
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next

        # 分割链表为两部分
        mid = slow.next
        slow.next = None

        # 对两个子链表递归排序
        left = self.sortList(head)
        right = self.sortList(mid)

        # 合并两个排序好的链表
        return self.merge(left, right)

    def merge(self, l1, l2):
        """
        合并两个排序好的链表
        """
        dummy = ListNode(0)
        tail = dummy

        # 合并过程
        while l1 and l2:
            if l1.val < l2.val:
                tail.next = l1
                l1 = l1.next
            else:
                tail.next = l2
                l2 = l2.next
            tail = tail.next

        # 将剩余部分链接到排序好的链表后面
        tail.next = l1 if l1 else l2
        return dummy.next

day11

23. 合并 K 个升序链表

leetcode链接: https://leetcode.cn/problems/merge-k-sorted-lists/description/

题目要求: 给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

解题思路一:

  1. 特殊情况处理:如果链表数组为空或者长度为 0,返回 None。
  2. 递归分治合并:使用分治法递归地将链表数组分为更小的部分,直到每个部分只有一个或两个链表,然后合并这些链表。
  3. 合并两个链表:定义一个函数来合并两个链表。这个函数可以是之前实现过的合并两个有序链表的函数。
  4. 合并完成:当所有链表都合并完成时,返回合并后的链表的头节点。
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        if not lists:
            return None
        if len(lists) == 1:
            return lists[0]

        mid = len(lists) // 2
        # 递归分治合并
        l1 = self.mergeKLists(lists[:mid])
        l2 = self.mergeKLists(lists[mid:])

        return self.mergeTwoLists(l1, l2)

    def mergeTwoLists(self, l1, l2):
        """
        合并两个有序链表
        """
        dummy = ListNode(0)
        tail = dummy

        while l1 and l2:
            if l1.val < l2.val:
                tail.next = l1
                l1 = l1.next
            else:
                tail.next = l2
                l2 = l2.next
            tail = tail.next

        tail.next = l1 if l1 else l2
        return dummy.next

141. 环形链表

leetcode链接: https://leetcode.cn/problems/linked-list-cycle/description/

题目要求: 给你一个链表的头节点 head ,判断链表中是否有环。

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

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

解题思路一:快慢指针(O(1)(即,常量)内存)

  1. 初始化两个指针,slow 和 fast,都指向链表的头节点 head。
  2. 在链表上移动这两个指针,slow 每次向前移动一个节点,fast 每次向前移动两个节点。
  3. 如果链表中不存在环,fast 会先到达链表的尾部(即 fast 或 fast.next 为 None)。
  4. 如果链表中存在环,那么 fast 和 slow 会在某个节点相遇(即 fast == slow),这时可以返回 True 表示存在环。
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        # 如果链表为空或只有一个节点,则不可能形成环
        if not head or not head.next:
            return False

        # 初始化两个指针:慢指针和快指针
        slow = head
        fast = head.next

        # 遍历链表
        while slow != fast:
            # 如果快指针到达链表尾部(或其下一个节点为None),说明没有环
            if not fast or not fast.next:
                return False
            
            # 慢指针移动一步,快指针移动两步
            slow = slow.next
            fast = fast.next.next

        # 如果快慢指针相遇,则说明链表中有环
        return True

142. 环形链表 II

leetcode链接: https://leetcode.cn/problems/linked-list-cycle-ii/

题目要求:

解题思路一:快慢指针(空间复杂度是 O(1))

  1. 确定链表是否有环:使用两个指针,slow 和 fast。slow 指针每次移动一步,fast 指针每次移动两步。如果链表有环,那么 fast 和 slow 最终会在环内的某个节点相遇。如果 fast 指针到达链表末尾(即 fast 或 fast.next 为 None),则链表没有环,返回 null。
  2. 找到环的起始节点:当确定链表有环后,将 fast 指针重新指向头节点,然后 slow 和 fast 都每次移动一步。当它们再次相遇时,相遇点即为环的起始节点。
# 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 not head or not head.next:
            return None

        slow, fast = head, head

        # 判断链表是否有环
        while True:
            if not fast or not fast.next:
                return None
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                break

        # 找到环的起始节点
        fast = head
        while slow != fast:
            slow = slow.next
            fast = fast.next

        return slow

day12

160. 相交链表

leetcode链接: https://leetcode.cn/problems/intersection-of-two-linked-lists/description/

题目要求: 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

解题思路一:时间复杂度 O(m + n) 、仅用 O(1) 内存

  1. 初始化两个指针:创建两个指针 pointerA 和 pointerB,分别指向两个链表的头节点 headA 和 headB。
  2. 遍历链表:同时移动这两个指针,每步各移动一个节点。
  3. 交换链表:当任一指针到达链表末尾时,将其重置到另一个链表的头节点。例如,当 pointerA 到达链表 headA 的末尾时,将其重置到 headB 的头节点,反之亦然。
  4. 相遇判断:如果两个链表相交,pointerA 和 pointerB 最终会在相交节点相遇;如果不相交,它们将同时到达各自链表的末尾(都为 None)。
  5. 返回结果:返回相交节点或 None。
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        if not headA or not headB:
            return None

        pointerA, pointerB = headA, headB

        # 当两个指针不相等时继续遍历
        while pointerA != pointerB:
            # 如果指针 A 到达末尾,则跳到链表 B 的头部
            pointerA = pointerA.next if pointerA else headB
            # 如果指针 B 到达末尾,则跳到链表 A 的头部
            pointerB = pointerB.next if pointerB else headA

        # 如果两个链表相交,pointerA 和 pointerB 会在相交节点相遇
        # 如果不相交,最终会同时为 None
        return pointerA

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

leetcode链接: https://leetcode.cn/problems/remove-nth-node-from-end-of-list/

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

解题思路一:一趟扫描实现

  1. 初始化两个指针:创建两个指针 fast 和 slow,都指向头节点 head。
  2. 移动快指针:先将 fast 指针向前移动 n 步
  3. 同时移动快慢指针:然后同时移动 fast 和 slow 指针,直到 fast 指针的 next 是 None(即 fast 指向链表末尾)。此时 slow 指针指向的是要删除节点的前一个节点。
  4. 删除节点:调整 slow.next 指向 slow.next.next,从而删除倒数第 n 个节点。
  5. 处理特殊情况:如果要删除的是头节点(即 n 等于链表长度),我们需要返回 head.next 作为新的头节点。
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        # 初始化一个哑节点,用于处理边界情况
        dummy = ListNode(0, head)
        fast = slow = dummy

        # 快指针先走 n 步
        for _ in range(n):
            fast = fast.next

        # 快慢指针一起走
        while fast.next:
            slow = slow.next
            fast = fast.next

        # 删除倒数第 n 个节点
        slow.next = slow.next.next

        # 返回新的头节点
        return dummy.next

143. 重排链表

leetcode链接: https://leetcode.cn/problems/reorder-list/description/

题目要求:
给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

解题思路一:

  1. 找到链表的中点:使用快慢指针方法找到链表的中点。慢指针每次移动一个节点,快指针每次移动两个节点。当快指针到达链表末尾时,慢指针就位于链表的中点。
  2. 反转链表的后半部分:从中点开始,反转链表的后半部分。
  3. 重新组合链表:将链表的前半部分和反转后的后半部分交替合并,形成最终的链表结构。
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution(object):
    def reorderList(self, head):
        """
        :type head: ListNode
        :rtype: None Do not return anything, modify head in-place instead.
        """
        if not head or not head.next:
            return

        # 找到中点
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next

        # 反转后半部分链表
        prev, curr = None, slow
        while curr:
            curr.next, prev, curr = prev, curr, curr.next

        # 重新组合链表
        first, second = head, prev
        while second.next:
            first.next, first = second, first.next
            second.next, second = first, second.next

你可能感兴趣的:(leetcode,面试,算法)