206. 反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
解法:
var reverseList = function(head) {
let cur = head, prev = null
while(cur) {
[cur.next, prev, cur] = [prev, cur, cur.next]
}
return prev
};
复制代码
思路分析:prev保存前一个节点, 主要逻辑分三步:
- 修改cur的next,指向前一个节点,即prev
- 保存当前节点cur为prev,供下一个节点使用
- 移动当前节点指针cur到下一个节点cur.next
24. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.
解法一:
var swapPairs = function(head) {
let node = new ListNode(0) //新建一个节点,用于保存head节点
node.next = head
let prev = node
while(prev.next && prev.next.next) {
let a = prev.next
let b = a.next
prev.next = a.next
a.next = b.next
prev = b.next = a
}
return node.next
};
复制代码
解题思路: 定义一个新的头节点node,将头节点next指向head,定义prev, a, b分别指向前三项,第一遍循环如下图:
循环过程中node始终指向自定义的头结点,而node.next即为真正的头节点。遍历第二遍时,a, b分别指向3,4,执行流程同上,执行完第二遍即结束循环。
解法二:
var swapPairs = function(head) {
if(!head) return null
if(!head.next) return head
let temp = head.next
head.next = swapPairs(temp.next)
temp.next = head
return temp
};
复制代码
解题思路:主要逻辑是head.next指向递归返回的上一个temp,同时修改temp.next为head,实现后一个节点指向前一个节点,同时前一个节点指向递归返回的节点,最后swapPairs接收到的就是最后返回的temp,即第二个节点2.
141. 环形链表
给定一个链表,判断链表中是否有环。
示例:
输入:head = [3,2,0,-4], 输出:true
解法一:使用快慢指针,快指针每次走两步,慢指针每次走一步,如果有环,二者必定会碰到一起;
var hasCycle = function(head) {
let fast = slow = head
while(fast && slow && fast.next) { //fast.next不存在说明链表已走完
slow = slow.next
fast = fast.next.next
if(fast === slow) {
return true
}
}
return false
};
复制代码
解法二:使用Set保存每个节点的地址,如果有环,Set里元素必定会重复;
var hasCycle = function(head) {
let set = new Set()
while(head) {
if(set.has(head)) return true
set.add(head)
head = head.next
}
return false
};
复制代码
142. 环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
示例:
输入:head = [3,2,0,-4]
输出:tail connects to node index 1
解法一:
var detectCycle = function(head) {
let set = new Set()
let cur = head
while(cur) {
if(set.has(cur)) return cur
set.add(cur)
cur = cur.next
}
return null
};
复制代码
解题思路:用Set保存每次遍历的地址,遍历时发现地址重复即为入口节点
解法二:
var detectCycle = function(head) {
let fast = slow = head
while(slow && fast && fast.next) {
slow = slow.next
fast = fast.next.next
if(slow === fast) {
slow = head
while(slow !== fast) {
slow = slow.next
fast = fast.next
}
return fast
}
}
return null
};
复制代码
解题思路:
- 先用快慢指针方法确定链表是否有环
- 如果有环的,慢指针slow重新从head出发,slow,fast每次都走一步,如果重合,重合处必为环的入口;
入口解释一下:
首先第一次遍历后假设在C点相遇,此时:
慢指针走的S1 = |AB| + |BC|
快指针走的S2 = 2 * ( |AB| + |BC| )
在另一个角度,S2 走的距离就是 |AB| + |BC| + n * s (s为环的大小),即
快指针走的S2 = |AB| + |BC| + n * s
所以可以得到
2 * ( |AB| + |BC| ) = |AB| + |BC| + n * s
可以得到:|AB| = n * s - |BC|
可以看出如果slow从A点开始,fast从相遇点C开始,两者以同样速度必会在B点相遇