小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
原文位于 github仓库-正在起步阶段的前端知识库,其中记录了一名前端初学者的学习日记🤔&学习之路点点滴滴的记录(练手demo🧑💻,必会知识点🧐)
欢迎大家来贡献更多“前端er必会知识点”🧑🎓/分享更多有意义的demo❤️!(请给这个年幼的小仓库更丰富的内容吧🥺🥺🥺)
本篇汇总一下链表题型中双指针的妙用&环形链表的经典问题~
【1】双指针的妙用
【medium】29.删除链表的倒数第 N 个结点
两个要点——
- 虚拟头结点
- 双指针(快指针先走一步~)
var removeNthFromEnd = function(head, n) {
let dummy = new ListNode(0, head);
let slow = dummy;
let fast = dummy;
for(let i = 0; i < n; i++){
fast = fast.next;
}
while(fast !== null && fast.next !== null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
};
206.反转链表
- 双指针法
这题的双指针写法相当经典!
在很多其他题型的解决方案种也有体现!(比如回文链表)
var reverseList = function(head) {
let pre = null;
let cur = head;
while(cur !== null){
let temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
};
分享一篇GIF图解,当初我就是看着这篇的GIF明白的😄
- 递归法速解
还可以使用递归的方法秒杀本题~
var reverseList = function(head) {
// “递”的过程
if(head === null || head.next === null){
return head;
}
let newHead = reverseList(head.next);// 把head.next这个子问题传进去
// “归”的过程
head.next.next = head;
head.next = null;
return newHead;
};
【medium】92.反转链表 II
好题!局部反转!
要点:保存断开连接的边缘,用于连接反转过后得到的头节点
var reverseBetween = function(head, left, right) {
let dummy = new ListNode(0,head);
let p = dummy;// 用于遍历
for(let i = 0; i < left - 1; i++){
p = p.next;
}
let leftHead = p;// 保存断开的边缘
let start = leftHead.next;// 保存开始反转的边界
// 设置双指针
let pre = start;
let cur = pre.next;
for(let j = left; j < right; j++){
// 反转局部链表
let temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
leftHead.next = pre;// 连接最开始(断链的边缘)和局部反转链表反转过后得到的头节点
start.next = cur;// 连接反转后的链表和后面那段链表
return dummy.next;
};
【2】经典环形链表问题
141. 环形链表
快慢指针的经典(也是很简单的一个)实践~
var hasCycle = function(head) {
let slow = head;
let fast = head;
while(fast !== null && fast.next !== null){
slow = slow.next;
fast = fast.next.next;
if(slow === fast){
return true;
}
}
return false;
};
【medium】142. 环形链表 II
超级经典的面试题,尽量对其中的数学原理多了解些!面试时候可以说出点东西最好!
我写的题解
推荐看的图文并茂的题解(当时我就是看这个懂的!)
简单说一下这题的数学原理——
首先 设链表长度为 a + b(环内结点个数)
- 【1】slow指针a+nb步之后一定会到入口;
-
【2】二者第一次相遇时慢指针已经走了nb步;
- 数学推导得来——fast走了
2*slow且第一次相遇时fast比slow快了b*n步 联立一下: fast = 2 * slow ; fast = slow + n*b;——slow = n*b
- 数学推导得来——fast走了
所以要让slow在第一次与fast相遇后再走a步停下来(满足刚好走完一整圈链表的长度)
这里建议看一下大佬们的图解XD 就很好理解了!
var detectCycle = function(head) {
let slow = head;
let fast = head;
// 01 让两个指针第一次相交 此时slow指针走了n*b步
while(true){
if(fast === null || fast.next === null){
return null;
}
slow = slow.next;
fast = fast.next.next;
if(slow === fast){
break;
}
}
// 02 换新的结点从头结点开始丈量a步 保证slow指针一共走了a+n*b步
let newFast = head;
while(slow !== newFast){
newFast = newFast.next;
slow = slow.next;
}
// 03 返回slow指向的结点 一定刚好走完一整圈
return slow;
}