力扣-排序的循环链表

267 阅读3分钟

这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

题目

给定循环单调非递减列表中的一个点,写一个函数向这个列表中插入一个新元素 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]

解法:

理清头绪后看下实际需要做的工作是什么:

就是链表循环走一圈 找到一个节点 node, 使得 node.val <= insertVal <= node.next.val。然后再node后面插入一个val===insertVal的新节点 那么要做的工作如下:

围着链表走一圈,如果找到这个节点node, 插入, 返回 head。结束。 如果走一圈没有这样的节点,那么必然存在两种情况: insertVal 比真实的尾节点的值还大 insertVal 比真实的头节点的值还小 这样我们要做的就是将insertVal节点插入在 尾、头 之间。 做好如上工作首要的是找到 真实的头节点(min)和尾节点(max)

在找头节点和尾节点的过程中随时判断是否有合适的位置插入节点。

找到真实头尾节点并不意味着已经围绕着链表走了一圈,而是从给定的head节点走到了真实尾节点处。尾节点 -> head节点的路程还没走过。

因此还需要通过循环走一遍 尾节点->head节点的过程。

循环过程中,随时插入insertVal节点。

走完以上循环。链表算是彻底走了一圈了。

现在只有两种情况:

已经插入return。 没有插入,等待插入。(此时就是上面说的第二种情况)只要插在 真实尾(max)和 真实头(min)之间即可。

var insert = function (head, insertVal) {
    const dummy = new Node(insertVal);
    // 判断 是否是空节点
    if (!head) {
        dummy.next = dummy;
        return dummy;
    }
    // 假的开始节点(不是最小节点)
    let fakeStart = head;
    // 主要为了找到真实节点的头 & 尾
    while (fakeStart.val <= fakeStart.next.val && fakeStart.next != head) {
        if (fakeStart.val <= insertVal && fakeStart.next.val >= insertVal) {
            dummy.next = fakeStart.next;
            fakeStart.next = dummy;
            return head;
        }
        fakeStart = fakeStart.next;
    }
    // 循环链表的真实头 & 尾
    const realHead = fakeStart.next; // 最小节点
    const realLast = fakeStart; // 最大节点
    // 整个算法时间复杂度是O(n), 上面只是从head -> realLast; 下面的循环是为了从 realLast -> head。 从而使整个链表完整走一圈。
    // 然而可能出现的情况是: 上面第一个节点正好是真实尾节点。即上面一步没动。因此下面要走一圈。而fakeStart 和 head 指向一致。
    // 所以最好的方法是先斩后奏。可保证链表完整走一圈。
    do {
        if (fakeStart.val <= insertVal && fakeStart.next.val >= insertVal) {
            dummy.next = fakeStart.next;
            fakeStart.next = dummy;
            return head;
        }
        fakeStart = fakeStart.next;
    } while (fakeStart !== head)

    // 此刻, 链表中还是没能插入所给 “节点”, 那么无论这个节点是 “小于头结点” or “大于尾节点”,插入位置都一样。只管插在头尾之间即可。
    realLast.next = dummy;
    dummy.next = realHead;
    return head;
}