1. 链表 + 快慢指针:环型链表
给定一个链表,判断链表中是否有环,存在环返回true,否则返回false.
用O(1)的内存解决问题。
1. 解题思路
2. 数据结构及算法思维选择
解法一:二次到达解法
数据结构: 数组
算法思维: 遍历
解法二: 追击问题解法
数据结构: 两个辅助变量
算法思维:遍历、快慢指针
3. 基本解法和编码
- 定义数组记录已经访问的结点
- 遍历链表的每个节点,并与数组中已经存放的节点依次比较:
- 相同方法结束,返回true
- 不同则存入最新位置,继续遍历下个节点
- 若next指针为null,则方法结束,返回false
细节问题:
-
遍历链表,通过head = head.next进行迭代
-
当且仅当此节点与容器某个节点相同返回true,其他情况返回false
const hasCycle = function(head) {
const res = [];
while (head) {
if (res.includes(head)) {
return true;
}
res.push(head);
head = head.next;
}
return false;
};
时间复杂度:O(n^2)
4. 思考更优解
- 证明某个节点是否第二次到达,是否可以将已遍历节点进行标记
- 追击问题模拟实现
5. 编码实现
var hasCycle = function(head) {
if(head === null || head.next === null){
return false;
}
let slow = head;
let fast = head.next;
while(fast !== null && fast.next !== null){
if( slow === fast){
return true;
}
fast = fast.next.next;
slow = slow.next;
}
return false;
};
6. 变形延伸
- 分别使用head和head.next作为链表的慢、块指针
- 标记值法:对节点的val属性进行标记,赋一个超出合法范围的值
- 反转指针法
延伸扩展:
- 龟兔赛跑,追击问题
- 地球,火星与太阳连成一线
2. 环型链表2
var detectCycle = function(head) {
if (head === null) {
return null;
}
let slow = head, fast = head;
while (fast !== null) {
slow = slow.next;
if (fast.next !== null) {
fast = fast.next.next;
} else {
return null;
}
// 相遇
if (fast === slow) {
let ptr = head;
while (ptr !== slow) {
ptr = ptr.next;
slow = slow.next;
}
return ptr;
}
}
return null;
}
3. 反转链表
var reverseList = function(head) {
let pre = null;
let current = head;
while(current){
let next = current.next;
// 1. 当前节点下一个指向前一个节点
current.next = pre;
// 2. pre赋給current
pre = current;
// 3. cur赋給next
current = next;
}
return pre;
};