链表最大孪生和

标签: 链表 双指针

难度: Medium

在一个大小为 n 且 n 为 偶数 的链表中,对于 0 <= i <= (n / 2) - 1 的 i ,第 i 个节点(下标从 0 开始)的孪生节点为第 (n-1-i) 个节点 。

  • 比方说,n = 4 那么节点 0 是节点 3 的孪生节点,节点 1 是节点 2 的孪生节点。这是长度为 n = 4 的链表中所有的孪生节点。

孪生和 定义为一个节点和它孪生节点两者值之和。

给你一个长度为偶数的链表的头节点 head ,请你返回链表的 最大孪生和 。

示例 1:

输入:head = [5,4,2,1]
输出:6
解释:
节点 0 和节点 1 分别是节点 3 和 2 的孪生节点。孪生和都为 6 。
链表中没有其他孪生节点。
所以,链表的最大孪生和是 6 。

示例 2:

输入:head = [4,2,2,3]
输出:7
解释:
链表中的孪生节点为:
- 节点 0 是节点 3 的孪生节点,孪生和为 4 + 3 = 7 。
- 节点 1 是节点 2 的孪生节点,孪生和为 2 + 2 = 4 。
所以,最大孪生和为 max(7, 4) = 7 。

示例 3:

输入:head = [1,100000]
输出:100001
解释:
链表中只有一对孪生节点,孪生和为 1 + 100000 = 100001 。

提示:

  • 链表的节点数目是 [2, 105] 中的 偶数 。
  • 1 <= Node.val <= 105

Submission

运行时间: 808 ms

内存: 46.4 MB

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def pairSum(self, head: Optional[ListNode]) -> int:
        s = f = head
        while f and f.next and f.next.next:
            f = f.next.next
            s = s.next

        l1 = head
        l2 = s.next
        s.next = None

        def reverse(h):
            pre = None
            cur = h
            while cur:
                tmp = cur.next
                cur.next = pre
                pre = cur
                cur = tmp
            return pre

        l2 = reverse(l2)
        p1 = l1
        p2 = l2
        m = 0
        while p1 and p2:
            m = max(m, p1.val + p2.val)
            p1 = p1.next
            p2 = p2.next
        return m

Explain

此题解通过使用快慢指针来找到链表的中点,然后将链表一分为二。接着,反转链表的后半部分,以便使得前半部分和反转后的后半部分的节点可以直接相加得到孪生和。通过遍历这两个部分的链表,计算所有可能的孪生和,并记录其中的最大值。

时间复杂度: O(n)

空间复杂度: O(1)

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def pairSum(self, head: Optional[ListNode]) -> int:
        # 初始化快慢指针
        s = f = head
        # 使用快慢指针找到链表中点
        while f and f.next and f.next.next:
            f = f.next.next
            s = s.next

        # 分割链表为两部分
        l1 = head
        l2 = s.next
        s.next = None

        # 反转链表的后半部分
        def reverse(h):
            pre = None
            cur = h
            while cur:
                tmp = cur.next
                cur.next = pre
                pre = cur
                cur = tmp
            return pre

        l2 = reverse(l2)
        # 初始化指针用于遍历两个链表部分
        p1 = l1
        p2 = l2
        # 存储最大孪生和
        m = 0
        while p1 and p2:
            m = max(m, p1.val + p2.val)
            p1 = p1.next
            p2 = p2.next
        return m

Explore

快慢指针方法是一种高效且简洁的方式来找到链表的中点。通过设置两个指针,一个每次移动一步(慢指针),另一个每次移动两步(快指针),当快指针到达链表的末尾时,慢指针正好在链表的中点。这种方法的优点是只需遍历链表一次,空间复杂度为 O(1)。除此之外,另一种方法是先遍历整个链表以确定链表的长度,然后再次遍历到链表长度的一半位置来找到中点,但这种方法需要遍历链表两次。

代码中已经考虑了链表节点数目只有两个的情况。当链表只有两个节点时,快慢指针会使得慢指针停在第一个节点上,将链表分割为一个节点和另一个节点。反转后半部分(只有一个节点的链表)时,反转操作实际上不会改变链表,因为单节点链表反转后仍是自己。因此,这种情况下代码依然可以正确计算出孪生和为这两个节点的值之和。

可以在不改变原链表结构的情况下解决这个问题。一种方法是使用栈。首先,遍历链表的前半部分并将其值推入栈中。然后遍历后半部分的链表,并在每次迭代中从栈中弹出一个元素,将其与当前节点的值相加以计算孪生和。这种方法的空间复杂度较高,为 O(n/2),但可以保持原链表的结构不变。

这种方法可以保证在所有情况下都正确计算出孪生和,包括当链表长度很大时。通过反转后半部分的链表,确保了两个指针 p1 和 p2 在遍历时,对应的节点是孪生节点。每次迭代中,两个指针都会向前移动一步,直到其中一个或两个都到达各自的终点。这确保了每一对孪生节点都被准确地计算了孪生和一次,因此这种方法在链表长度大时依然有效。