链表
链表题目的解决, 通常要结合画图来理解
876 链表的中间结点
入门级别, 快慢指针即可解决
var middleNode = function(head) {
let quick = head,
slow = head
while (quick!==null && quick.next!==null) {
quick = quick.next.next
slow = slow.next
}
return slow
};
160 相交链表
让我用一道很浪漫的题开启大家的链表之旅吧!
关键词: 重走一遍你的路, 我们终究会相遇
function getIntersectionNode (headA, headB) {
let pA = headA
let pB = headB
while (pA!==pB) {
pA = (pA===null) ? headB : pA.next
pB = (pB===null) ? headA : pB.next
}
return pA
}
206 反转链表
pre, cur, next三指针
本题目不使用dummy节点, 使用pre和cur双节点的方式, 由于单链表的限制, 额外使用一个next指针记录cur节点的下一个节点的位置, 每次遍历时, 让cur指向pre, 并让pre前移, cur前移(指向next)
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
// pre始终落后cur和next一个身位, cur和next指向同一个节点
let pre = null,
cur = head,
next = head
while (cur!==null) {
// 使用next指针记录cur后一位的地址
next = cur.next
cur.next = pre
// pre前移一位
pre = cur
// cur前移一位
cur = next
}
return pre
};
利用es6的解构赋值, 我们有更为精巧的解法
var reverseList = function(head) {
// pre始终落后cur一个身位, cur指向当前节点
let pre = null,
cur = head
while (cur!==null) {
// 利用es6的解构赋值, 你甚至不需要额外的next指针
[cur.next, pre, cur] = [pre, cur, cur.next]
// pre, cur移动的顺序也可以改变
// [cur.next, cur, pre] = [pre, cur.next, cur]
}
return pre
};
92 反转链表 II
pre, cur, next三指针 + 额外存储
想法:
- 反转位置m到位置n之间的链表
- 将原链表与反转后的链表拼接
步骤:
- 初始化变量: pre指向前方, curr遍历当前链表, 二者依次增加, pre始终落后curr一个身位
- 将pre, curr递增到位置m之前, 使用pre2和curr2分别存储当前pre和curr的位置
- 反转位置m到位置n之间的链表
- 利用pre2和curr2, 将反转后的部分链表与原链表拼接
var reverseBetween = function (head, m, n) {
let prev = null;
let curr = head;
for (let i = 1; i < m; i++) {
prev = curr;
curr = curr.next;
}
let prev2 = prev;
let curr2 = curr;
for (let i = m; i <= n; i++) {
[curr.next, prev, curr] = [prev, curr, curr.next];
}
if (m > 1) {
prev2.next = prev;
} else {
head = prev;
}
curr2.next = curr;
return head;
};
141 环形链表
快慢指针
- 创建两个指针都指向头部, 快指针走两步, 慢指针走一步
- 如果二者能相遇, 则为环形链表
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
if (head===null) return false
let fast = head,
slow = head
while (fast.next!==null && fast.next.next!==null) {
fast = fast.next.next
slow = slow.next
if (fast === slow) {
return true
}
}
return false
};
142 环形链表 II
原理图解:
代码:
var detectCycle = function(head) {
if (head===null) return null
let fast = head,
slow = head,
isCircle = false
while (fast.next!==null && fast.next.next!==null) {
fast = fast.next.next
slow = slow.next
if (fast===slow) {
// 此时说明有环, 退出循环
fast = head
isCircle = true
break
}
}
if (!isCircle) return null
while (fast!==slow) {
fast = fast.next
slow = slow.next
}
return fast
};
328 奇偶链表
奇偶双指针
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var oddEvenList = function(head) {
if (head===null) return null
if (head.next===null) return head
let odd = head
let even = head.next,
evenHead = even
while (even!==null && even.next!==null) {
odd.next = odd.next.next
odd = odd.next
even.next = even.next.next
even = even.next
}
odd.next = evenHead
return head
};
沉没效应
419 甲板上的战舰
沉没, 类似计算岛屿数量
通解
var countBattleships = function(board) {
let result = 0
function dfs(row, col) {
if (row<0 || row>=board.length || col<0 || col>=board[0].length || board[row][col]!=='X') {
return
}
board[row][col] = '.'
dfs(row-1, col)
dfs(row+1, col)
dfs(row, col-1)
dfs(row, col+1)
}
for (let row=0; row<board.length; row++) {
for (let col=0; col<board[0].length; col++) {
if (board[row][col]==='X') {
result++
// 从当前点出发, 进行深度优先搜索
dfs(row, col)
}
}
}
return result
};
特解: 结合这个题目的特点, 还有一个特殊解法, 大家了解即可
var countBattleships = function(board) {
let result = 0
for (let row=0; row<board.length; row++) {
for (let col=0; col<board[0].length; col++) {
if (board[row][col]==='X') {
if (row>0 && board[row-1][col]==='X') {
continue
}
if (col>0 && board[row][col-1]==='X') {
continue
}
result++
}
}
}
return result
};
695 岛屿的最大面积
/**
* @param {number[][]} grid
* @return {number}
*/
var maxAreaOfIsland = function(grid) {
let maxArea = 0
for (let row=0; row<grid.length;row++) {
for (let col=0; col<grid[0].length;col++) {
if (grid[row][col]===1) {
let area = dfs(row, col)
maxArea = Math.max(area, maxArea)
}
}
}
function dfs(row, col) {
// 边界判断
if (row<0 || row>=grid.length || col<0 || col>=grid[0].length || grid[row][col]===0) {
return 0
}
// 先沉没当前点, 并将面积+1
grid[row][col] = 0
let area = 1
// 递归调用, 并加上四周点的返回值
area += dfs(row-1, col)
area += dfs(row+1, col)
area += dfs(row, col-1)
area += dfs(row, col+1)
return area
}
return maxArea
};
733 图像渲染
var floodFill = function(image, sr, sc, color) {
if (image[sr][sc]===color) return image
const oldColor = image[sr][sc]
function dfs(sr, sc) {
if (sr<0 || sc<0 || sr>=image.length || sc>=image[0].length || oldColor!==image[sr][sc]) {
return
}
image[sr][sc] = color
dfs(sr-1, sc)
dfs(sr+1, sc)
dfs(sr, sc-1)
dfs(sr, sc+1)
}
dfs(sr, sc)
return image
};
动态规划
动态规划的本质就是: 递归(recursion) + 记忆化(memoization)
509 斐波那契数列 - 梦开始的地方
botom-up
var fib = function(n) {
// bottom-up + 空间复杂度优化
if (n<=1) return n
let cur1 = 0,
cur2 = 1,
result = 0
for (let i=2; i<=n; i++) {
result = cur1 + cur2
cur1 = cur2
cur2 = result
}
return result
};
up-bottom 比较难理解
var fib = function(n) {
// bottom-up + 空间复杂度优化
if (n<=1) return n
let cache = []
cache[0] = 0
cache[1] = 1
function memoize(num) {
if (cache[num]!==undefined) return cache[num]
cache[num] = memoize(num-1) + memoize(num-2)
return cache[num]
}
let result = memoize(n)
return result
};
55
62
198
二分搜索
704 二分查找
- 循环
while left<=rightlet mid = left和right的中间位置if (nums[mid]<target) left = mid+1砍掉左半边if (nums[mid]<target) right = mid-1砍掉右半边
- 循环结束都没有找到, 就返回
-1
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
let left = 0
let right = nums.length-1
while (left<=right) {
let mid = left + Math.floor((right-left)/2)
if (nums[mid]<target) {
// 砍掉左半边
left = mid + 1
}else if (nums[mid]>target) {
right = mid-1
}else {
return mid
}
}
return -1
};