循环有序列表的插入

标签: 链表

难度: Medium

给定循环单调非递减列表中的一个点,写一个函数向这个列表中插入一个新元素 insertVal ,使这个列表仍然是循环升序的。

给定的可以是这个列表中任意一个顶点的指针,并不一定是这个列表中最小元素的指针。

如果有多个满足条件的插入位置,可以选择任意一个位置插入新的值,插入后整个列表仍然保持有序。

如果列表为空(给定的节点是 null),需要创建一个循环有序列表并返回这个节点。否则。请返回原先给定的节点。

示例 1:


 

输入:head = [3,4,1], insertVal = 2
输出:[3,4,1,2]
解释:在上图中,有一个包含三个元素的循环有序列表,你获得值为 3 的节点的指针,我们需要向表中插入元素 2 。新插入的节点应该在 1 和 3 之间,插入之后,整个列表如上图所示,最后返回节点 3 。


示例 2:

输入:head = [], insertVal = 1
输出:[1]
解释:列表为空(给定的节点是 null),创建一个循环有序列表并返回这个节点。

示例 3:

输入:head = [1], insertVal = 0
输出:[1,0]

提示:

  • 0 <= Number of Nodes <= 5 * 10^4
  • -10^6 <= Node.val <= 10^6
  • -10^6 <= insertVal <= 10^6

注意:本题与主站 708 题相同: https://leetcode-cn.com/problems/insert-into-a-sorted-circular-linked-list/

Submission

运行时间: 44 ms

内存: 15.6 MB

"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, next=None):
        self.val = val
        self.next = next
"""

class Solution:
    def insert(self, head: 'Node', insertVal: int) -> 'Node':
        if head is None:
            head = Node(insertVal)
            head.next = head
            return head
        if head == head.next:
            node = Node(insertVal)
            head.next = node
            node.next = head
            return head
        pre = head
        cur = head.next
        max_node = head
        while True:
            if pre.val <= insertVal <= cur.val:
                node = Node(insertVal)
                pre.next = node
                node.next = cur
                return head
            if max_node.val <= pre.val:
                max_node = pre
            if pre.next == head:
                node = Node(insertVal, max_node.next)
                max_node.next = node
                return head
            pre = pre.next
            cur = cur.next
        return head

Explain

本题解的核心思路是在一个循环单调非递减列表中插入一个新元素,同时保持列表的循环单调非递减特性。首先,如果列表为空(head为None),则创建一个新节点并使其成为循环列表。如果列表中只有一个节点,直接在此节点后插入新节点并更新指针。对于多节点的情况,遍历列表寻找插入位置:1. 直接找到两个相邻节点,使得前一个节点的值小于等于插入值且插入值小于等于后一个节点的值;2. 如果遍历一圈后未找到这样的相邻节点,说明插入值应当插入在最大值节点的后面。这种情况下,需要维护一个最大值节点的引用,在遍历过程中更新。最后,将新节点插入到合适的位置。

时间复杂度: O(n)

空间复杂度: O(1)

"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, next=None):
        self.val = val
        self.next = next
"""

class Solution:
    def insert(self, head: 'Node', insertVal: int) -> 'Node':
        if head is None:
            # 列表为空,创建新节点并形成循环
            head = Node(insertVal)
            head.next = head
            return head
        if head == head.next:
            # 列表只有一个节点,直接插入新节点
            node = Node(insertVal)
            head.next = node
            node.next = head
            return head
        pre = head
        cur = head.next
        max_node = head
        while True:
            # 找到两个节点,前一个不大于插入值,后一个不小于插入值
            if pre.val <= insertVal <= cur.val:
                node = Node(insertVal)
                pre.next = node
                node.next = cur
                return head
            # 更新最大值节点
            if max_node.val <= pre.val:
                max_node = pre
            # 如果遍历回到起点,插入到最大值节点后
            if pre.next == head:
                node = Node(insertVal, max_node.next)
                max_node.next = node
                return head
            pre = pre.next
            cur = cur.next
        return head
"""

Explore

在这种情况下,`insertVal`无法直接找到一个符合条件的插入位置(即不存在一个区间使得前一个节点的值小于等于`insertVal`且`insertVal`小于等于后一个节点的值)。因此,我们需要找到列表中的最大节点,然后将新节点插入到这个最大节点的后面。这样做能保证列表的循环单调非递减的特性不被破坏。在遍历过程中,我会维护一个引用指向当前遍历过的最大值节点,一旦完成一圈的遍历且没有找到合适的插入点,就将新节点插入到这个最大节点的后面。

在找到合适的插入位置后立即返回头节点是为了保持链表的引用不变,这对于调用者来说是重要的。在循环链表中,头节点通常作为整个链表的访问入口,若改变头节点的位置可能会导致引用混乱或不一致,特别是在多次插入操作时。此外,返回头节点后,调用者可以继续使用原有的头节点引用进行其他操作,如遍历、查找等,这提高了代码的可用性和一致性。

维护`最大值节点的引用`是为了处理插入值不处于任何两个相邻节点值之间的情形,特别是当插入值大于所有现有节点的值或小于所有现有节点的值的情况。如果存在多个具有相同最大值的节点,我的实现选择了在遍历过程中遇到的第一个最大值节点作为插入点。这是因为在单次完整的遍历中,第一个遇到的最大值节点之后的位置是维持循环单调非递减顺序的合适位置。这种方法简化了插入逻辑,同时保持了列表的结构和顺序的正确性。