这是我参与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;
}