「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
每日刷题第26天 2021.1.21
剑指 Offer II 029. 排序的循环链表
- leetcode原题链接:leetcode-cn.com/problems/4u…
- 难度:中等
- 方法:模拟
题目
- 给定循环单调非递减列表中的一个点,写一个函数向这个列表中插入一个新元素
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
解法
解题思路
补充知识
- 单调非递减列表:不是单调递增的,因为可能存在重复的值。举例:
[1,2,3,3,5] - 同理可知:单调非递增列表:
[6,3,2,2,1]
解题思路
- 分析(处理数据):
insertVal为一个数值,但是我们需要插入的是一个节点,因此先新建一个节点newInsert,并设置newInsert.val = insertVal; newInsertVal.next = null;
第一步:(‼️链表的题目首要判断‼️)链表为空,直接插入newInsert节点,返回。
第二步:将head节点保存到cur指针中(防止链表的头节点丢失)
第三步:分情况讨论:
-
情况1:
head.val == insertVal -
举例:
[5,6,2,3,3] insertVal = 5,直接插入到head节点后面即可。 -
情况2:
head.val < insertVal,当head节点的值小于insertVal的时候,要找到合适的插入位:head -> min(链表中最小的值) -
举例1:
[5,6,8,1,2,3] insertVal = 7,从5找到1。 -
举例2:
[7,7,7,7] insertVal = 8 -
实现的代码思路:
while将当前节点cur和cur.next进行比较(cur与cur.next形成一个区间,判断该区间是否可以插入)
// 伪代码
while(当前节点值 <= 下一节点值){
// 因为从`head -> min`,相当于找完大于head的值,如果找到min还没有找到,那么就放在min前面一个节点
if(insertVal 小于等于 下一节点的值){
// 找到合适的位置,在当前节点和下一节点之间插入即可
}else {
// 没有找到合适,就需要将找下一个区间
}
}
// 还有一种情况:一直没有找到小于等于的数,也就是我们需要插入的数为当前链表的最大值
// while循环跳出后,在当前节点与min之间的区间,插入即可
- 情况3:
head.val > insertVal,当head节点的值大于insertVal的时候,要找到合适的插入位:min(链表中最小的值) -> head - 举例:
[5,6,8,1,2,3] insertVal = 4 - 举例:
[7,7,7,7] insertVal = 5 - 实现的代码思路:由于
head.val > insertVal,那么如果想:只查找min -> head之间的区间❌,其实应该查找链表中所有的数,知道找到位置才跳出while循环。为什么这样呢?因为样例[5,1,3] 0,此时的如果只查找min -> head,那么就丢掉了head -> min之间的区间,就会出错❌(详细分析见:错误🙅♂️反思)。因此最终将while循环条件写成1,就通过了。
// 伪代码
while(当前节点 <= 下一节点){
if(下一个节点 等于 head){
// 自身成为一个循环链表,[1]
// 插入新建的节点,返回
}else{
// 不相等即没有找到合适的位置,继续找下一个区间
}
}
// 当while循环结束后,就找到链表最大的节点和最小的节点之间的区间
while(1){
if(insertVal 是否小于 下一个节点){
// 找到合适的位置,插入新建的节点,返回
}else {
// 没有找到合适的位置,就需要找下一个区间
}
}
// 因为是必定会找到插入点,因此不需要额外补充代码
错误🙅♂️反思
出错样例:
[1,3,5] 0[5,1,3] 0[5,1,3] 2
代码
/**
* // Definition for a Node.
* function Node(val, next) {
* this.val = val;
* this.next = next;
* };
*/
/**
* @param {Node} head
* @param {number} insertVal
* @return {Node}
*/
var insert = function(head, insertVal) {
// 单调非递减 -> 1 2 3 3 4
// 分三种情况
// 需要插入的节点
let newInsert = new Node(insertVal,null);
if (head == null){
// 直接加入节点即可
head = newInsert;
head.next = head;
return head;
}
// 当前所给的链表不为空
// 3. head == insert
// 临时存储的节点
let tempt;
// 两个指针
let cur = head;
if (head.val == insertVal){
// 表示数值相同,插入到当前节点的下一个节点即可
// 1.后面一定有节点,循环
tempt = head.next;
head.next = newInsert;
newInsert.next = tempt;
}else if (head.val < insertVal) {
// 1. head < insert
// 表示:需要找的区间:head -> min,临界条件:找到最小值都没有找到合适的位置,就放在最小值前面即可
// 如何判断为最小值,即next.val < cur.val
while (cur.val <= cur.next.val) {
// console.log('cur',cur.val,'curnext',cur.next.val);
// 找到合适的位置跳出循环 或 找到最小还是没找到跳出循环 cur < cur.next
if (insertVal <= cur.next.val || cur.next == head){
// 找到合适的位置
tempt = cur.next;
cur.next = newInsert;
newInsert.next = tempt;
return head;
}else {
cur = cur.next;
}
}
// 找到最后min都没有找到合适的位置,直接插入即可
// 找到合适的位置
tempt = cur.next;
cur.next = newInsert;
newInsert.next = tempt;
// !!!还有一种情况:【7,7,7】 8
// 如果链表的所有数值都相等,则插入到head的前一个节点
}else if (head.val > insertVal) {
// 2. head > insert
// 表示:需要找的区间:min -> head,临界条件:找到head都没有找到,就放在head前面即可
// 情况:链表的值不全部相等
// 3 -> 5 -> 1 -> 2
// 5 -> 3 -> 4
// 1 -> 3 -> 5
while (cur.val <= cur.next.val) {
// console.log('111');
if (cur.next == head){
// 找到合适的位置
tempt = cur.next;
cur.next = newInsert;
newInsert.next = tempt;
return head;
}
// 找到最小值min与最大值之间,还没有地方,那么就插入尾部
// cur.next 即为最小值min
cur = cur.next;
}
// console.log('>最小的节点',cur);
// 最小值到head之间寻找合适的位置
while (1) {
// console.log('循环',cur,'h',head);
if (insertVal <= cur.next.val) {
// 找到合适的位置
tempt = cur.next;
cur.next = newInsert;
newInsert.next = tempt;
return head;
}else {
cur = cur.next;
}
}
// 漏掉一种情况:head就是最大值,刚好要插入head前面
// 5 1 3
// tempt = cur.next;
// cur.next = newInsert;
// newInsert.next = tempt;
// 情况:链表值全部相等
}
return head;
};
附录
- 边界需要多注意,可以多想一些有关边界的样例