leetCode 141环形链表:判断一个链表是否存在环
- 把每一项都做一个标记_invited,如果存在环,那么就一定会访问到_invited
var hasCycle = function (head) {
if (!head) return false
while (head.next) {
if (head.next._invited) return true
else {
head = head.next
head._invited = true
}
}
return false
};
// 执行用时:72 ms
// 内存消耗:43.7 MB
- 设置快慢指针,慢指针走一步,快指针走两步(这里需要注意一点,快慢指针的设置一定要保证他们会相遇,当然也是存在环的时候)
var hasCycle = function (head) {
let fast = head,
slow = head;
while (fast && slow && fast.next) {
slow = slow.next
fast = fast.next.next
if (slow === fast) return true
}
return false
};
// 执行用时:68 ms
// 内存消耗:43.9 MB
- 使用set集合的自动判重能力
var hasCycle = function(head) {
let set = new Set()
set.add(head)
while(head && head.next){
head = head.next
if(set.has(head)) return true
else set.add(head)
}
return false
};
// 执行用时:60 ms
// 内存消耗:44.5 MB
方法2的双指针形式leetCode里面比较流行
leetCode206:反转链表
- 使用递归的方式
var reverseList = function (head) {
if (!head) return null
let reverseList;
let recursion = function (head) {
if (head.next) return recursion(head.next).next = head
else return reverseList = head
}
let lastIndex = recursion(head)
lastIndex.next = null
return reverseList
};
- 使用循环的方式:这是leetCode的官方方法。
我们举个例子,假如之前是这样的 1 -> 2 -> 3 -> null,那么反转之后就应该是 3 -> 2 -> 1 -> null,这里面解法的过程是这样的:
(1)let next = cur.next;
: let next = 2 -> 3 -> null
(2)cur.next = prev;
: cur = { value:1, next:null }
(3)prev = cur;
: prev = { value:1, next:null }
(4)cur = next
: cur = 2 -> 3 -> null
(1)let next = cur.next;
: let next = 3 -> null
(2)cur.next = prev;
: cur = { value:2, next: { value:1, next:null } } = { value:2, next: prev }
(3)prev = cur;
: prev = { value:2, next: { value:1, next:null } }
(4)cur = next
: cur = 3 -> null
(1)let next = cur.next;
: let next = null
(2)cur.next = prev;
: cur = { value:3, next: { value:2, next: { value:1, next:null } } } = { value:3, next: prev }
(3)prev = cur;
: prev = { value:3, next: { value:2, next: { value:1, next:null } } } = cur
(4)cur = next
: cur = null
cur = null 循环结束,完成反转
var reverseList = function (head) {
let prev = null;
let cur = head;
while(cur){
let next = cur.next;
cur.next = prev;
prev = cur;
cur = next
}
return prev
};
- 使用递归方式
递归方式1:
循环的这种方式其实还是是从第一层逐渐向内层进行反转,而这种递归的方式是从最里面,逐渐向外面一次反转,这也是leetCode的官方方法。
有人可能会感觉递归里面的第一句是不是有一点冗余,既然head.next == null,那么head一定有值,那么head == null一定不成立,其实不然head == null判断的是一开始刚刚进来的状态也就是reverseList(null)这种情况。
再来看看newHead返回的作用,第一次返回的newHead其实是尾部,也就是反转之后的头部。
var reverseList = function (head) {
let reverseFn = (head) => {
if (head == null || head.next == null) return head;
let newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead
}
return reverseFn(head)
};
递归方式2:
这种方式和循环那种方式的解法基本一样,就不赘述了。
let prev = null;
let reverseFn = (head) => {
if (!head) return
let next = head.next
head.next = prev
prev = head
reverseFn(next)
}
reverseFn(head)
return prev