链表
反转链表
原地单链表的反转、两个一组反转链表和K个一组反转链表
1. 简单的反转链表
反转一个单链表。【206】
/**
* @param {ListNode} head
* @return {ListNode}
*/
let reverseList = (head) => {
if (!head) return null
let pre = null, cur = head
while (cur) {
// 关键: 保存下一个节点的值
let next = cur.next
cur.next = pre
pre = cur
cur = next
}
return pre
}
2. 区间反转
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。说明: 1 ≤ m ≤ n ≤ 链表长度。【92】
/**
* @param {ListNode} head
* @param {number} m
* @param {number} n
* @return {ListNode}
*/
var reverseBetween = function(head, m, n) {
let p = dummyHead = new ListNode()
let pre, cur, front, end
p.next = head
for(let i = 0; i < m - 1; i ++) {
p = p.next
}
front = p
pre = end = p.next
cur = pre.next
for(let i = 0; i < n - m; i++) {
let next = cur.next
cur.next = pre
pre = cur
cur = next
}
front.next = pre
end.next = cur
return dummyHead.next
}
3. 两个一组翻转链表
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。给定 1->2->3->4, 你应该返回 2->1->4->3.【24】
var swapPairs = function(head) {
if(head === null || head.next === null) return head
let node1 = head, node2 = head.next
node1.next = swapPairs(node2.next)
node2.next = node1
return node2
}
4. K个一组反转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。你的算法只能使用常数的额外空间。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。【25】
递归解法:
/**
* @param {ListNode} head
* @param {number} k
* @return {ListNode}
*/
var reverseKGroup = function(head, k) {
let pre = null, cur = head
let p = head
// 下面的循环用来检查后面的元素是否能组成一组
for(let i = 0; i < k; i++) {
if(p == null) return head
p = p.next
}
for(let i = 0; i < k; i++){
let next = cur.next
cur.next = pre
pre = cur
cur = next
}
// pre为本组最后一个节点,cur为下一组的起点
head.next = reverseKGroup(cur, k)
return pre
}
环形链表:
1. 如何检测链表形成环
var hasCycle = function(head) {
let dummyHead = new ListNode()
dummyHead.next = head
let fast = slow = dummyHead
// 零个结点或者一个结点,肯定无环
if(fast.next === null || fast.next.next === null) return false
while(fast && fast.next) {
fast = fast.next.next
slow = slow.next
// 两者相遇了
if(fast === slow) return true
}
return false
}
2. 如何找到环的起点?
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
**说明:**不允许修改给定的链表。
/**
* @param {ListNode} head
* @return {ListNode}
*/
var detectCycle = function(head) {
let dummyHead = new ListNode()
dummyHead.next = head
let fast = slow = dummyHead
// 零个结点或者一个结点,肯定无环
if(fast.next === null || fast.next.next === null) return null
while(fast && fast.next) {
fast = fast.next.next
slow = slow.next
// 两者相遇了
if(fast === slow) {
let p = dummyHead
while(p !== slow) {
p = p.next
slow = slow.next
}
return p
}
}
return null
}
链表合并
1. 合并两个有序链表
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。【21】
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var mergeTwoLists = function(l1, l2) {
const merge = (l1, l2) => {
if (l1 === null) return l2
if (l2 === null) return l1
if (l1.val > l2.val) {
l2.next = merge(l1, l2.next)
return l2
} else {
l1.next = merge(l1.next, l2)
return l1
}
}
return merge(l1, l2)
}
2. 合并K个有序链表
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。【23】
自上而下递归实现:
/**
* @param {ListNode[]} lists
* @return {ListNode}
*/
var mergeKLists = function(lists) {
var mergeTwoLists = function(l1, l2) {
const merge = (l1, l2) => {
if(l1 == null) return l2;
if(l2 == null) return l1;
if(l1.val > l2.val) {
l2.next = merge(l1, l2.next);
return l2;
}else {
l1.next = merge(l1.next, l2);
return l1;
}
}
return merge(l1, l2);
};
const _mergeLists = (lists, start, end) => {
if(end - start < 0) return null;
if(end - start == 0)return lists[end];
let mid = Math.floor(start + (end - start) / 2);
return mergeTwoList(_mergeLists(lists, start, mid), _mergeLists(lists, mid + 1, end));
}
return _mergeLists(lists, 0, lists.length - 1);
};
自下而上实现:
var mergeKLists = function(lists) {
var mergeTwoLists = function(l1, l2) {
const merge = (l1, l2) => {
if(l1 == null) return l2;
if(l2 == null) return l1;
if(l1.val > l2.val) {
l2.next = merge(l1, l2.next);
return l2;
}else {
l1.next = merge(l1.next, l2);
return l1;
}
}
return merge(l1, l2);
};
// 边界情况
if(!lists || !lists.length) return null;
// 虚拟头指针集合
let dummyHeads = [];
// 初始化虚拟头指针
for(let i = 0; i < lists.length; i++) {
let node = new ListNode();
node.next = lists[i];
dummyHeads[i] = node;
}
// 自底向上进行merge
for(let size = 1; size < lists.length; size += size){
for(let i = 0; i + size < lists.length;i += 2 * size) {
dummyHeads[i].next = mergeTwoLists(dummyHeads[i].next, dummyHeads[i + size].next);
}
}
return dummyHeads[0].next;
};
求链表中间节点
1. 判断回文链表
判断一个单链表是否为回文链表。你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?【234】
/**
* @param {ListNode} head
* @return {boolean}
*/
var isPalindrome = function(head) {
let reverse = (pre, cur) => {
if(!cur) return pre
let next = cur.next
cur.next = pre
return reverse(cur, next)
}
let dummyHead = slow = fast = new ListNode()
dummyHead.next = head
// 找中点, 黄金模板
while (fast && fast.next) {
slow = slow.next
fast = fast.next.next
}
let next = slow.next
slow.next = null
let newStart =