📌  相关文章
📜  为链表末尾的第 n 个节点编程(1)

📅  最后修改于: 2023-12-03 14:48:58.363000             🧑  作者: Mango

找到链表末尾的第 n 个节点

在本文中,我们将学习如何找到链表末尾的第 n 个节点。对于链表数据结构,遍历整个链表可以找到它的末尾节点。然而,我们也可以使用一些技巧在不遍历整个列表的情况下找到列表中的第 n 个节点。

方法一:反转链表并遍历

一个找到倒数第 k 个节点的常规方式是通过反转链表来实现。我们可以反转链表,然后遍历 k 步并返回相应的节点。 在这个方法中,我们将创建一个新的链表,将原始链表的节点反转,遍历反向列表并从 n 到 n-k 返回第 n-k 个节点。

class Solution:
    def reverseLinkedList(self, head: ListNode) -> ListNode:
        prev = None
        curr = head
        while curr:
            nextNode = curr.next
            curr.next = prev
            prev = curr
            curr = nextNode
        return prev
    
    def findNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        if not head:
            return None
        
        reversed_head = self.reverseLinkedList(head)
        curr = reversed_head
        i = 1
        while curr and i < n:
            curr = curr.next
            i += 1
        if not curr:
            return None
        
        return curr.val

具体过程如下:

  1. 反转原始链表。
  2. 遍历反向列表并从 n 到 n-k 返回第 n-k 个节点。

时间复杂度:$O(n)$,其中 n 是列表的长度。 这个算法遍历了该列表两次,首先是反向列表,然后从 n 到 n-k 返回第 n-k 个节点。但是需要注意的是,代码实现比遍历列表两次要简单。

空间复杂度:$O(1)$。 这个算法只需要常数级别的存储(创建一个新的链表)。

方法二: 双指针

另一个方法是使用两个指针:快指针和慢指针。我们将两个指针都初始化为指向该链表的头部,然后将快指针向前移动 n 个节点。此时,双指针之间会有一个间隔为 n 的距离。接下来,我们将两个指针一起向前移动,直到快指针到达链表的末尾。此时,慢指针将指向链表的倒数第 n 个节点。

class Solution:
    def findNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummyNode = ListNode(-1)
        dummyNode.next = head
        
        slow = fast = dummyNode
        
        #移动 fast 指针,使其指向第n个节点,此时slow和fast之间的距离为n
        for i in range(n):
            fast = fast.next
        
        #同时移动 slow 和 fast 指针
        while fast.next:
            slow = slow.next
            fast = fast.next
        
        #此时slow所指的为倒数第n个节点
        return slow.next

具体过程如下:

  1. 复制头节点的值,将它存储在哑节点的上面。这是为了避免当我们必须返回原始链表的头部节点时,必须对特例进行特殊处理的情况。
  2. 移动快指针 n 个节点。
  3. 将慢指针和快指针一起移动,直到快指针到达链表的末尾。
  4. 返回慢指针的下一个节点。

时间复杂度:$O(n)$. 计算一次需要O(n)的时间,这里只遍历了一次列表。

空间复杂度:$O(1)$. 双指针调用中只使用了常量级别的额外指针。

总结

这篇文章演示了许多方法,在不遍历整个链表的情况下找到链表中的第 n 个节点。其中反转链表和双指针是相对容易理解的,但是在面试中需要思考。 总体来说,这些技巧为在链表中找到节点提供了一种更加高效的方法,尤其是对于较长的链表而言。