「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」
快慢指针,顾名思义,就是有快的和慢的两个指针分别指向链表,根据其移动的速度差,来完成一系列问题的实现。下面这两道题,就是利用了快慢指针的方法。
返回倒数第 k 个节点
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
输入: 1->2->3->4->5 和 k = 2
输出: 4
这是一道快慢指针的题,我们需要两个指针,一个快指针,一个慢指针, 快指针比慢指针多走k步,当快指针走完全程的时候,慢指针刚好走到指定位置.
当快指针fast刚开始移动的时候,慢指针slow不移动,直到快指针和慢指针之间的间距
fast-slow = k的时候,慢指针才和快指针一起以相同的速度移动,当快指针走完全程的时候
慢指针slow刚好距离终点k个节点。
var kthToLast = function (head, k) {
// 快指针
let fast = 0;
// 慢指针
let slow = 0;
快指针指向的链表
let link = head;
// 如果快指针指向的链表循环完成,则慢指针指向的链表为所求节点
while (link) {
// 如果快慢指针间距达到所需距离,慢指针移动
if (fast - slow == k) {
head = head.next;
slow++
}
// 快指针移动
link = link.next;
fast++;
}
// 返回慢指针指向节点值
return head.val
判断环形链表
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
这道题有很多种做法,我们先来分析第一种,也就是今天讨论的双指针法,因为我们只需要判断它是不是一个环,并不需要知道环的起始点是哪里,所以只要快慢指针重合,就能证明这是一个闭合链表。
var hasCycle = function (head) {
// 特殊值判断,如果链表不存在或者只有一个值,那么快慢指针则无法用,所以需要先排除掉
if(!head||!head.next) return false
// 定义快慢指针,快指针比慢指针快一步,并且快指针一次走两步。
let fast = head.next.next;
let slow = head.next;
// 终止条件,如果链表不是循环链表,则必有终点
while(fast&&fast.next){
// 如果快慢指针重合,说明是循环链表
if(fast==slow) return true;
// 慢指针走一步,快指针走两步
slow = slow.next;
fast = fast.next.next;
}
// while结束说明没有环
return false
};
上面就是这道题快慢指针的做法,一个一次走一步,一个一次走两步,如果循环一定会相遇,因为比如当快指针开始循环,此时比慢指针落后两步,那下一次一定只落后一步,再下一次就会相逢,因为快指针每次只多走一步,所以必会重逢。
说完了快慢指针,刚刚还说过这道题有多种解法,我们来看一看其他解法。
这里最值得一提的就是hash算法,我们设定一个hash地址,将没有存入的结点存入,判断下一个结点,如果遇到了已经存入的结点,那就说明遇到了循环。
var hasCycle = function (head) {
// 创建一个空地址
let set = new Set();
// 循环链表
while (head) {
// 判断该节点有没有存入过
if (set.has(head)) {
return true
} else {
// 没有存入过的结点存入
set.add(head)
}
head = head.next
}
return false
};
其余方法
var hasCycle = function (head) {
// 方法1 JavaScript属性
// 这是根据JavaScript的属性,如果遇到循环链表则会报错
try {
JSON.stringify(head)
} catch{
return true
}
return false
// 方法2 污染链表
while(head){
// 将每一个路过的结点都进行标记,下次再遇到则证明已经来过
if(head.sign) return true;
head.sign = true;
head = head.next
}
return false
};