小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
原文位于 github仓库-正在起步阶段的前端知识库,其中记录了一名前端初学者的学习日记🤔&学习之路点点滴滴的记录(练手demo🧑💻,必会知识点🧐)
欢迎大家来贡献更多“前端er必会知识点”🧑🎓/分享更多有意义的demo❤️!(请给这个年幼的小仓库更丰富的内容吧🥺🥺🥺)
本篇主要内容(都比较基础~):
- 链表的合并
- 链表的删除
- 链表删除的延申(较复杂的链表删除)
【1】链表的合并
21. 合并两个有序链表(递归法重要!)
这俩题一样哈——剑指 Offer 25. 合并两个排序的链表
迭代法
var mergeTwoLists = function(l1, l2) {
let dummyHead = new ListNode(0, l1);
let cur = dummyHead;
while(l1 !== null && l2 !== null){
if(l1.val < l2.val){
cur.next = l1;
l1 = l1.next;
}
else{
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = l1 === null ? l2 : l1;
return dummyHead.next;
};
递归
之前写过一篇 掌握递归调用栈思想 由浅入深研究递归🎉,结合着递归栈的思想看本题的递归解法会好想很多~
var mergeTwoLists = function(l1, l2){
if(l1 === null){
return l2;// 碰到一个链表走到null 就结束“递” 开始归
}
else if(l2 === null){
return l1;
}
else if(l1.val < l2.val){
l1.next = mergeTwoLists(l1.next, l2);
}
else{
l2.next = mergeTwoLists(l1, l2.next);
}
}
递归就思考第一层第二层最好
每层不要忘了返回当层结果(执行这一次递归函数 返回的结果)!
以原始用例 [1,2] [1,3,4]
【1】开始“递” 一直到最里面那一层
- l1小的时候 l1指向再里层一些的那个递归函数
mergeTwoLists()
别忘了l1要往前挪动一位mergeTwoLists(l1.next, l2)
- l2小的时候同理
【2】return l2 执行时
这里结束了“递”
最里面那层的mergeTwoLists()
执行完了
开始“归” —— 逐步执行外层的mergeTwoLists()
函数
【3】最外面一层函数执行完
就可以返回最终结果了~
小结一下——
-
想一下“哪个是最里层被调用的函数” 在那里返回对应结果
-
也就是主要想第一层
-
【2】链表的删除
83. 删除排序链表中的重复元素
var deleteDuplicates = function(head) {
// let dummyHead = new ListNode(0, head);
// let pre = dummyHead;
let cur = head;
// if(cur === null){
// return null;
// }
while(cur !== null && cur.next !== null){
if(cur.val === cur.next.val){
cur.next = cur.next.next;
}
else{
cur = cur.next;
}
}
return head;
};
【3】链表删除的延伸
82. 删除排序链表中的重复元素 II
但是现在,咱们要做的事情变成了把前驱和后继一起删掉,前面两个值为1的结点要一起狗带才行,起始结点直接变成了第三个:
如果继续沿用刚才的思路,我们会发现完全走不通。因为我们的 cur 指针就是从图中第一个结点出发开始遍历的,无法定位到第一个结点的前驱结点,删除便无法完成。
虚拟头结点应用场景:
其实在链表题中,经常会遇到这样的问题:链表的第一个结点,因为没有前驱结点,导致我们面对它无从下手。这时我们就可以用一个 dummy
结点来解决这个问题。
本题思路
如果想要删除两个连续重复的值为 1 的结点,我们只需要把 dummy 结点的 next 指针直接指向 2:
如此一来,就大功告成啦~
注意:由于重复的结点可能不止一个两个,我们这里需要用一个 while 循环来反复地进行重复结点的判断和删除操作。
const deleteDuplicates = function(head) {
// 极端情况:0个或1个结点,则不会重复,直接返回
if(!head || !head.next) {
return head
}
// dummy 登场
let dummy = new ListNode()
// dummy 永远指向头结点
dummy.next = head
// cur 从 dummy 开始遍历
let cur = dummy
// 当 cur 的后面有至少两个结点时
while(cur.next && cur.next.next) {
// 对 cur 后面的两个结点进行比较
if(cur.next.val === cur.next.next.val) {
// 若值重复,则记下这个值
let val = cur.next.val
// 反复地排查后面的元素是否存在多次重复该值的情况
while(cur.next && cur.next.val===val) {
// 若有,则删除
cur.next = cur.next.next
}
} else {
// 若不重复,则正常遍历
cur = cur.next
}
}
// 返回链表的起始结点
return dummy.next;
};