模板 - num

leetcode地址
标签:
解题思路
解题步骤

两数之和 - 1

leetcode地址
标签:字典
解题思路
解题步骤

  1. /**
  2. * @param {number[]} nums
  3. * @param {number} target
  4. * @return {number[]}
  5. */
  6. var twoSum = function(nums, target) {
  7. const map = new Map()
  8. for (let i = 0; i < nums.length; i += 1) {
  9. const n = nums[i]
  10. const n2 = target - n
  11. if (map.has(n2)) {
  12. return [map.get(n2), i]
  13. } else {
  14. map.set(n, i)
  15. }
  16. }
  17. };

两数相加 - 2

leetcode地址
标签:链表
解题思路
解题步骤

  1. /**
  2. * Definition for singly-linked list.
  3. * function ListNode(val, next) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.next = (next===undefined ? null : next)
  6. * }
  7. */
  8. /**
  9. * @param {ListNode} l1
  10. * @param {ListNode} l2
  11. * @return {ListNode}
  12. */
  13. var addTwoNumbers = function(l1, l2) {
  14. const l3 = new ListNode(0)
  15. let p1 = l1
  16. let p2 = l2
  17. let p3 = l3
  18. let carry = 0
  19. while (p1 || p2) {
  20. const val = (p1 ? p1.val : 0) + (p2 ? p2.val : 0) + carry
  21. carry = Math.floor(val / 10)
  22. p3.next = new ListNode(val % 10)
  23. p1 && (p1 = p1.next)
  24. p2 && (p2 = p2.next)
  25. p3 = p3.next
  26. }
  27. carry && (p3.next = new ListNode(carry))
  28. return l3.next
  29. };

无重复字符的最长子串 - 3

  1. /**
  2. * @param {string} s
  3. * @return {number}
  4. */
  5. var lengthOfLongestSubstring = function(s) {
  6. // 双指针维护滑动窗口
  7. let l = 0
  8. let res = 0
  9. const map = new Map()
  10. for (let r = 0; r < s.length; r += 1) {
  11. if (map.has(s[r]) && map.get(s[r]) >= l) {
  12. l = map.get(s[r]) + 1
  13. }
  14. res = Math.max(res, r - l + 1)
  15. map.set(s[r], r)
  16. }
  17. return res
  18. };

有效的括号 - 20

leetcode地址
标签:栈
解题思路
解题步骤

  1. /**
  2. * @param {string} s
  3. * @return {boolean}
  4. */
  5. var isValid = function(s) {
  6. if (s.length % 2 === 1) { return false }
  7. const stack = []
  8. const map = new Map()
  9. map.set('{', '}')
  10. map.set('[', ']')
  11. map.set('(', ')')
  12. // 方法一
  13. // const left = ['(', '{', '[']
  14. // const right = [')', '}', ']']
  15. // for (let i = 0; i < s.length; i += 1) {
  16. // if (left.some(v => v === s[i])) {
  17. // stack.push(s[i])
  18. // }
  19. // const index = right.findIndex(v => v === s[i])
  20. // if (index > -1) {
  21. // if (stack[stack.length - 1] === left[index]) {
  22. // stack.pop()
  23. // } else {
  24. // return false
  25. // }
  26. // }
  27. // }
  28. // 方法二
  29. // for (let i = 0; i < s.length; i += 1) {
  30. // const c = s[i]
  31. // if (c === '(' || c === '{' || c === '[') {
  32. // stack.push(c)
  33. // } else {
  34. // t = stack[stack.length - 1]
  35. // if (
  36. // (t === '(' && c === ')') ||
  37. // (t === '{' && c === '}') ||
  38. // (t === '[' && c === ']')
  39. // ) {
  40. // stack.pop()
  41. // } else {
  42. // return false
  43. // }
  44. // }
  45. // }
  46. // return stack.length === 0
  47. // 方法三:使用字典优化
  48. for (let i = 0; i < s.length; i += 1) {
  49. const c = s[i]
  50. if (map.has(c)) {
  51. stack.push(c)
  52. } else {
  53. t = stack[stack.length - 1]
  54. if (map.get(t) === c) {
  55. stack.pop()
  56. } else {
  57. return false
  58. }
  59. }
  60. }
  61. return stack.length === 0
  62. };

合并两个有序链表 - 21

leetcode地址
标签:排序、归并排序
解题思路

  • 与归并排序中合并两个有序数组相似

解题步骤

  1. 新建一个链表,作为返回结果
  2. 用指针遍历两个有序链表,并比较两个链表的当前节点,较小者先接入新链表,并将指针后移一步
  3. 遍历结果,返回新链表

    1. /**
    2. * Definition for singly-linked list.
    3. * function ListNode(val, next) {
    4. * this.val = (val===undefined ? 0 : val)
    5. * this.next = (next===undefined ? null : next)
    6. * }
    7. */
    8. /**
    9. * @param {ListNode} l1
    10. * @param {ListNode} l2
    11. * @return {ListNode}
    12. */
    13. var mergeTwoLists = function(l1, l2) {
    14. const res = new ListNode()
    15. let p1 = l1
    16. let p2 = l2
    17. let p3 = res
    18. while (p1 && p2) {
    19. if (p1.val < p2.val) {
    20. p3.next = p1
    21. p1 = p1.next
    22. } else {
    23. p3.next = p2
    24. p2 = p2.next
    25. }
    26. p3 = p3.next
    27. }
    28. if (p1) p3.next = p1
    29. if (p2) p3.next = p2
    30. return res.next
    31. };

合并 K 个升序链表 - 23

leetcode地址
标签:堆、最小堆、链表
解题思路

  • 新链表的下一个节点一定是 k 个链表头中最小的节点
  • 考虑使用最小堆

解题步骤

  1. 构建最小堆,并依次把链表头插入堆中
  2. 弹出堆顶接到输出链表,并将堆顶所在链表的新链表头插入堆中
  3. 等堆元素全部弹出,合并工作即完成 ```javascript class MinHeap { constructor() { this.heap = [] }

    swap(i1, i2) { const temp = this.heap[i1] this.heap[i1] = this.heap[i2] this.heap[i2] = temp }

    getParentIndex(i) { return (i - 1) >> 1 }

    getLeftIndex(i) { return i * 2 + 1 }

    getRightIndex(i) { return i * 2 + 2 }

    shiftUp(index) { if (index === 0) return const parentIndex = this.getParentIndex(index)

    if (this.heap[parentIndex] && this.heap[parentIndex].val > this.heap[index].val) { this.swap(parentIndex, index) this.shiftUp(parentIndex) } }

    shiftDown(index) { const leftIndex = this.getLeftIndex(index) const rightIndex = this.getRightIndex(index)

    if (this.heap[leftIndex] && this.heap[leftIndex].val < this.heap[index].val) { this.swap(leftIndex, index) this.shiftDown(leftIndex) } if (this.heap[rightIndex] && this.heap[rightIndex].val < this.heap[index].val) { this.swap(rightIndex, index) this.shiftDown(rightIndex) } }

    // 插入方法 insert(value) { this.heap.push(value) this.shiftUp(this.heap.length - 1) }

    // 删除方法 pop() { if (this.heap.length === 1) return this.heap.pop() const top = this.heap[0] this.heap[0] = this.heap.pop() this.shiftDown(0) return top }

    // 获取堆顶 top() { return this.heap[0] }

    // 获取堆大小 size() { return this.heap.length } }

/**

  • Definition for singly-linked list.
  • function ListNode(val, next) {
  • this.val = (val===undefined ? 0 : val)
  • this.next = (next===undefined ? null : next)
  • } / /*
  • @param {ListNode[]} lists
  • @return {ListNode} */ var mergeKLists = function(lists) { const res = new ListNode(0) const h = new MinHeap() let p = res lists.forEach(v => { v && h.insert(v) }) while(h.size()) { const n = h.pop() p.next = n p = p.next n.next && h.insert(n.next) } return res.next }; ```

接雨水 - 42

leetcode地址
标签:栈、单调栈
解题思路

  • 出现高的柱子时,即右边第一个比左边大的数,才会出现坑洼,即能接到雨水
  • 利用单调递减栈的思想
  • 存放索引,再逐个配对,消除

解题步骤

  1. 低柱子入栈,构建单调递减栈
  2. 出现比栈顶大的柱子时,进行一次结算
  3. 接水区域面积 = 宽 * 高
  4. 其中宽(底部的左边到右边的距离)、高(左右两边更低的一遍减去底部)
    1. /**
    2. * @param {number[]} height
    3. * @return {number}
    4. */
    5. var trap = function(height) {
    6. // 单调栈
    7. const stack = []
    8. let count = 0
    9. for (let i = 0; i < height.length; i++) {
    10. while (stack.length && height[i] > height[stack[stack.length - 1]]) {
    11. const cur = stack.pop()
    12. if (!stack.length) {
    13. break
    14. }
    15. const l = stack[stack.length - 1]
    16. const h = Math.min(height[l], height[i]) - height[cur]
    17. count += (i - l - 1) * h
    18. }
    19. stack.push(i)
    20. }
    21. return count
    22. };

全排列 - 46

leetcode地址
标签:回溯算法
解题思路

  • 要求:1、所有排列情况;2、没有重复元素
  • 有出路、有死路
  • 考虑使用回溯算法

解题步骤

  1. 用递归模拟出所有情况
  2. 遇到包含重复元素的情况,就回溯
  3. 收集所有到达递归终点的情况,并返回
    1. /**
    2. * @param {number[]} nums
    3. * @return {number[][]}
    4. */
    5. var permute = function(nums) {
    6. const res = []
    7. const rec = (path) => {
    8. if (path.length === nums.length) {
    9. res.push(path)
    10. }
    11. nums.forEach(v => {
    12. if (path.includes(v)) return
    13. rec(path.concat(v))
    14. })
    15. }
    16. rec([])
    17. return res
    18. };

有效数字 - 65

解题思路
有效数字解题思路.png

  1. /**
  2. * @param {string} s
  3. * @return {boolean}
  4. */
  5. var isNumber = function(s) {
  6. const graph = {
  7. 0: { blank: 0, sign: 1, '.': 2, digit: 6 },
  8. 1: { '.': 2, digit: 6 },
  9. 2: { digit: 3 },
  10. 3: { digit: 3, e: 4 },
  11. 4: { digit: 5, sign: 7 },
  12. 5: { digit: 5 },
  13. 6: { '.': 3, e: 4, digit: 6 },
  14. 7: { digit: 5 }
  15. }
  16. let state = 0
  17. for (c of s.trim()) {
  18. if (c === '+' || c === '-') {
  19. c = 'sign'
  20. } else if (c >= '0' && c <= '9') {
  21. c = 'digit'
  22. } else if (c === ' ') {
  23. c = 'blank'
  24. } else if (c === 'E') {
  25. c = 'e'
  26. }
  27. state = graph[state][c]
  28. if (state === undefined) {
  29. return false
  30. }
  31. }
  32. if (state === 3 || state === 5 || state === 6) {
  33. return true
  34. }
  35. return false
  36. };

爬楼梯 - 70

leetcode地址
标签:动态规划
解题思路

  • 爬到第 n 阶可以在 第 n-1 阶爬 1 个台阶,或者在第 n-2 阶爬 2 个台阶
  • F(n) = F(n - 1) + F(n - 2)

解题步骤

  1. 定义子问题:F(n) = F(n - 1) + F(n - 2)
  2. 反复执行:从 2 循环到 n,执行上述公式
    1. /**
    2. * @param {number} n
    3. * @return {number}
    4. */
    5. var climbStairs = function(n) {
    6. if (n === 1) return 1
    7. let dp0 = 1
    8. let dp1 = 1
    9. for (let i = 2; i <= n; i += 1) {
    10. const temp = dp0
    11. dp0 = dp1
    12. dp1 = dp1 + temp
    13. }
    14. return dp1
    15. };

    最小覆盖子串 - 76

  1. /**
  2. * @param {string} s
  3. * @param {string} t
  4. * @return {string}
  5. */
  6. var minWindow = function(s, t) {
  7. // 用双指针维护滑动窗口
  8. // 移动右指针,找到包含 t 的子串,再移动左指针,尽量减少包含 t 的子串长度
  9. let l = 0
  10. let r = 0
  11. const need = new Map()
  12. for (let c of t) {
  13. need.set(c, need.has(c) ? need.get(c) + 1 : 1)
  14. }
  15. let needType = need.size
  16. let res = ''
  17. while (r < s.length) {
  18. const c = s[r]
  19. if (need.has(c)) {
  20. need.set(c, need.get(c) - 1)
  21. if (need.get(c) === 0) { needType -= 1 }
  22. while (needType === 0) {
  23. const newRes = s.substring(l, r + 1)
  24. if (!res || newRes.length < res.length) { res = newRes }
  25. const c2 = s[l]
  26. if (need.has(c2)) {
  27. need.set(c2, need.get(c2) + 1)
  28. if (need.get(c2) === 1) { needType += 1 }
  29. }
  30. l += 1
  31. }
  32. }
  33. r += 1
  34. }
  35. return res
  36. };

子集 - 78

leetcode地址
标签:回溯算法
解题思路

  • 要求:1、所有子集;2、没有重复元素
  • 有出路、有死路
  • 考虑使用回溯算法

解题步骤

  1. 用递归模拟出所有情况
  2. 保证接的数字都是后面的数字
  3. 收集所有到达递归终点的情况,并返回
    1. /**
    2. * @param {number[]} nums
    3. * @return {number[][]}
    4. */
    5. var subsets = function(nums) {
    6. const res = []
    7. const rec = (path, len, start) => {
    8. if (path.length === len) {
    9. res.push(path)
    10. return
    11. }
    12. for (let i = start; i < nums.length; i += 1) {
    13. rec(path.concat(nums[i]), len, i + 1)
    14. }
    15. }
    16. for (let i = 0; i <= nums.length; i += 1) {
    17. rec([], i, 0)
    18. }
    19. return res
    20. };

    删除排序链表中的重复元素 - 83

  1. /**
  2. * Definition for singly-linked list.
  3. * function ListNode(val, next) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.next = (next===undefined ? null : next)
  6. * }
  7. */
  8. /**
  9. * @param {ListNode} head
  10. * @return {ListNode}
  11. */
  12. var deleteDuplicates = function(head) {
  13. let p = head
  14. while (p && p.next) {
  15. if (p.val === p.next.val) {
  16. p.next = p.next.next
  17. } else {
  18. p = p.next
  19. }
  20. }
  21. return head
  22. };

二叉树的中序遍历 - 94

  1. /**
  2. * Definition for a binary tree node.
  3. * function TreeNode(val, left, right) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.left = (left===undefined ? null : left)
  6. * this.right = (right===undefined ? null : right)
  7. * }
  8. */
  9. /**
  10. * @param {TreeNode} root
  11. * @return {number[]}
  12. */
  13. var inorderTraversal = function(root) {
  14. const res = []
  15. // 递归版
  16. // const rec = (n) => {
  17. // if (!n) return
  18. // rec(n.left)
  19. // res.push(n.val)
  20. // rec(n.right)
  21. // }
  22. // rec(root)
  23. // 迭代算法,使用栈
  24. const stack = []
  25. let p = root
  26. while (stack.length || p) {
  27. while (p) {
  28. stack.push(p)
  29. p = p.left
  30. }
  31. const n = stack.pop()
  32. res.push(n.val)
  33. p = n.right
  34. }
  35. return res
  36. };

相同的树 - 100

leetcode地址
标签:二叉树、分而治之
解题思路

  • 两个树:根节点的值相同,左子树相同,右子树相同
  • 符合“分、解、合”,考虑分而治之

解题步骤

  1. 分:获取两个树的左子树合右子树
  2. 解:递归判断两个树的左子树是否相同,右子树是否相同
  3. 合:将上述结果合并,如果根节点的值也相同,树就相同
    1. /**
    2. * Definition for a binary tree node.
    3. * function TreeNode(val, left, right) {
    4. * this.val = (val===undefined ? 0 : val)
    5. * this.left = (left===undefined ? null : left)
    6. * this.right = (right===undefined ? null : right)
    7. * }
    8. */
    9. /**
    10. * @param {TreeNode} p
    11. * @param {TreeNode} q
    12. * @return {boolean}
    13. */
    14. var isSameTree = function(p, q) {
    15. if (!p && !q) return true
    16. if (p && q && p.val === q.val &&
    17. isSameTree(p.left, q.left) &&
    18. isSameTree(p.right, q.right)
    19. ) {
    20. return true
    21. }
    22. return false
    23. };

    对称二叉树 - 101

    leetcode地址
    标签:二叉树、分而治之
    解题思路
  • 转换为:左右子树是否镜像
  • 分解为:树1的左子树和树2的右子树是否镜像,树1的右子树和树2的左子树是否镜像
  • 符合“分、解、合”

解题步骤

  1. 分:获取两个树的左子树合右子树
  2. 解:递归判断树1的左子树和树2的右子树是否镜像,树1的右子树和树2的左子树是否镜像
  3. 合:将上述结果合并,如果根节点的值也相同,两个树就镜像
    1. /**
    2. * Definition for a binary tree node.
    3. * function TreeNode(val, left, right) {
    4. * this.val = (val===undefined ? 0 : val)
    5. * this.left = (left===undefined ? null : left)
    6. * this.right = (right===undefined ? null : right)
    7. * }
    8. */
    9. /**
    10. * @param {TreeNode} root
    11. * @return {boolean}
    12. */
    13. var isSymmetric = function(root) {
    14. if (!root) return true
    15. const isMirror = (l, r) => {
    16. if (!l && !r) return true
    17. if (l && r && l.val === r.val &&
    18. isMirror(l.left, r.right) &&
    19. isMirror(l.right, r.left)
    20. ) {
    21. return true
    22. }
    23. return false
    24. }
    25. return isMirror(root.left, root.right)
    26. };

    二叉树的层序遍历 - 102

  1. /**
  2. * Definition for a binary tree node.
  3. * function TreeNode(val) {
  4. * this.val = val;
  5. * this.left = this.right = null;
  6. * }
  7. */
  8. /**
  9. * @param {TreeNode} root
  10. * @return {number[][]}
  11. */
  12. var levelOrder = function(root) {
  13. // 广度优先遍历
  14. // 方法一
  15. // if (!root) return []
  16. // const q = [[root, 0]]
  17. // let res = []
  18. // while (q.length) {
  19. // const [n, level] = q.shift()
  20. // if (!res[level]) {
  21. // res.push([n.val])
  22. // } else {
  23. // res[level].push(n.val)
  24. // }
  25. // n.left && q.push([n.left, level + 1])
  26. // n.right && q.push([n.right, level + 1])
  27. // }
  28. // return res
  29. // 方法二
  30. if (!root) return []
  31. const q = [root]
  32. let res = []
  33. while (q.length) {
  34. let len = q.length
  35. res.push([])
  36. while(len--) {
  37. const n = q.shift()
  38. res[res.length - 1].push(n.val)
  39. n.left && q.push(n.left)
  40. n.right && q.push(n.right)
  41. }
  42. }
  43. return res
  44. };

二叉树的最大深度 - 104

  1. /**
  2. * Definition for a binary tree node.
  3. * function TreeNode(val, left, right) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.left = (left===undefined ? null : left)
  6. * this.right = (right===undefined ? null : right)
  7. * }
  8. */
  9. /**
  10. * @param {TreeNode} root
  11. * @return {number}
  12. */
  13. var maxDepth = function(root) {
  14. // 深度优先遍历
  15. let res = 0
  16. const dfs = (n, l) => {
  17. if (!n) return
  18. (!n.left && !n.right) && (res = Math.max(res, l))
  19. dfs(n.left, l + 1)
  20. dfs(n.right, l + 1)
  21. }
  22. dfs(root, 1)
  23. return res
  24. };

二叉树的最小深度 - 111

  1. /**
  2. * Definition for a binary tree node.
  3. * function TreeNode(val, left, right) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.left = (left===undefined ? null : left)
  6. * this.right = (right===undefined ? null : right)
  7. * }
  8. */
  9. /**
  10. * @param {TreeNode} root
  11. * @return {number}
  12. */
  13. var minDepth = function(root) {
  14. // 广度优先遍历
  15. if (!root) return 0
  16. const q = [[root, 1]]
  17. while (q.length) {
  18. const [n, l] = q.shift()
  19. if (!n.left && !n.right) {
  20. return l
  21. }
  22. n.left && q.push([n.left, l + 1])
  23. n.right && q.push([n.right, l + 1])
  24. }
  25. };

路径总和 - 112

  1. /**
  2. * Definition for a binary tree node.
  3. * function TreeNode(val, left, right) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.left = (left===undefined ? null : left)
  6. * this.right = (right===undefined ? null : right)
  7. * }
  8. */
  9. /**
  10. * @param {TreeNode} root
  11. * @param {number} targetSum
  12. * @return {boolean}
  13. */
  14. var hasPathSum = function(root, targetSum) {
  15. // 深度优先遍历
  16. if (!root) return false
  17. let res = false
  18. const dfs = (n, s) => {
  19. if (!n.left && !n.right && s === targetSum) {
  20. res = true
  21. }
  22. n.left && dfs(n.left, s + n.left.val)
  23. n.right && dfs(n.right, s + n.right.val)
  24. }
  25. dfs(root, root.val)
  26. return res
  27. };

买卖股票的最佳时机 - 122

leetcode地址
标签:贪心算法
解题思路

  • 前提:上帝视角,知道未来的价格
  • 局部最优:见好就收,见差就不动,不做任何长远打算

解题步骤

  1. 新建一个变量,用来统计总利润
  2. 遍历价格数组,如果当前价格比昨天高,就在昨天买,今天卖,否则就不交易
  3. 遍历结束后,返回总利润

    1. /**
    2. * @param {number[]} prices
    3. * @return {number}
    4. */
    5. var maxProfit = function(prices) {
    6. let profit = 0
    7. for (let i = 1; i < prices.length; i += 1) {
    8. if (prices[i] > prices[i - 1]) {
    9. profit += prices[i] - prices[i - 1]
    10. }
    11. }
    12. return profit
    13. };

    克隆图 - 133

    leetcode地址
    标签:图、深度/广度优先遍历
    解题思路

  4. 拷贝所有的节点

  5. 拷贝所有的边

解题步骤

  1. 深度或广度优先遍历所有节点
  2. 拷贝所有的节点,并存储
  3. 将拷贝的节点,按原图的连接方式连接 ```javascript /**
    • // Definition for a Node.
    • function Node(val, neighbors) {
    • this.val = val === undefined ? 0 : val;
    • this.neighbors = neighbors === undefined ? [] : neighbors;
    • }; */

/**

  • @param {Node} node
  • @return {Node} */ var cloneGraph = function(node) { if (!node) return

    const visited = new Map() // 深度优先遍历 // const dfs = (n) => { // const nCopy = new Node(n.val) // visited.set(n, nCopy); // (n.neighbors || []).forEach(ne => { // if (!visited.has(ne)) { // dfs(ne) // } // nCopy.neighbors.push(visited.get(ne)) // }) // } // dfs(node)

    // 广度优先遍历 const q = [node] visited.set(node, new Node(node.val)) while (q.length) { const n = q.shift(); (n.neighbors || []).forEach(ne => {

    1. if (!visited.has(ne)) {
    2. q.push(ne)
    3. visited.set(ne, new Node(ne.val))
    4. }
    5. visited.get(n).neighbors.push(visited.get(ne))

    }) }

    return visited.get(node) }; ```

    环形链表 - 141

    leetcode地址
    标签:链表、双指针
    解题思路

  1. 双指针遍历
  2. 一前一后,快的每次前进两格,慢的每次前进一格,若存在环,则两个指针会相遇,否则遍历结束 ```javascript /**
    • Definition for singly-linked list.
    • function ListNode(val) {
    • this.val = val;
    • this.next = null;
    • } */

/**

  • @param {ListNode} head
  • @return {boolean} */ var hasCycle = function(head) { // 双指针解法 let p1 = head let p2 = head

    while (p1 && p2 && p2.next) { p1 = p1.next p2 = p2.next.next if (p1 === p2) { return true } } return false }; ```

打家劫舍 - 198

leetcode地址
标签:动态规划
解题思路

  • f(k) = 从前 k 个房屋中能偷窃到的最大数额
  • Ak = 第 k 个房屋的钱数
  • f(k) = max(f(k - 2) + Ak, f(k - 1))

解题步骤

  1. 定义子问题:max(f(k - 2) + Ak, f(k - 1))
  2. 反复执行:从 2 循环到 n,执行上述公式

    1. /**
    2. * @param {number[]} nums
    3. * @return {number}
    4. */
    5. var rob = function(nums) {
    6. if (nums.length === 0) return 0
    7. let dp0 = 0
    8. let dp1 = nums[0]
    9. for (let i = 1; i < nums.length; i += 1) {
    10. const temp = dp0
    11. dp0 = dp1
    12. dp1 = Math.max(temp + nums[i], dp1)
    13. }
    14. return dp1
    15. };

反转链表 - 206

leetcode地址
标签:链表、双指针、递归
解题思路
1、双指针

  • 双指针 cur、pre 一前一后遍历
  • 将 pre 指向 cur 实现局部交换,再各自向后移动一位

2、递归

  • 逐个递归遍历至链表尾
  • 此后,每次函数在返回的过程中,让当前结点的下一个结点的 next 指针指向当前节点。
  • 同时让当前结点的 next 指针指向 NULL ,从而实现从链表尾部开始的局部反转

解题步骤

  1. /**
  2. * Definition for singly-linked list.
  3. * function ListNode(val) {
  4. * this.val = val;
  5. * this.next = null;
  6. * }
  7. */
  8. /**
  9. * @param {ListNode} head
  10. * @return {ListNode}
  11. */
  12. var reverseList = function(head) {
  13. // 双指针
  14. let pre = head
  15. let cur = null
  16. while (pre) {
  17. const tmp = pre.next
  18. pre.next = cur
  19. cur = pre
  20. pre = tmp
  21. }
  22. return cur
  23. // 递归
  24. if (head === null || head.next === null) return head
  25. const next = head.next
  26. const reverseNode = reverseList(next)
  27. next.next = head
  28. head.next = null
  29. return reverseNode
  30. };

数组中的第 K 个最大元素 - 215

leetcode地址
标签:堆、最小堆
解题思路

  1. 看到 “第 K 个最大元素”
  2. 考虑使用最小堆

解题步骤

  1. 构建一个最小堆,依次把数组的值插入堆中
  2. 当堆的容量超过 K,删除堆顶
  3. 插入结束后,堆顶即是第 K 个最大元素 ```javascript class MinHeap { constructor() { this.heap = [] }

    swap(i1, i2) { const temp = this.heap[i1] this.heap[i1] = this.heap[i2] this.heap[i2] = temp }

    getParentIndex(i) { return (i - 1) >> 1 }

    getLeftIndex(i) { return i * 2 + 1 }

    getRightIndex(i) { return i * 2 + 2 }

    shiftUp(index) { if (index === 0) return const parentIndex = this.getParentIndex(index) if (this.heap[parentIndex] > this.heap[index]) { this.swap(parentIndex, index) this.shiftUp(parentIndex) } }

    shiftDown(index) { const leftIndex = this.getLeftIndex(index) const rightIndex = this.getRightIndex(index)

    if (this.heap[leftIndex] < this.heap[index]) { this.swap(leftIndex, index) this.shiftDown(leftIndex) } if (this.heap[rightIndex] < this.heap[index]) { this.swap(rightIndex, index) this.shiftDown(rightIndex ) } }

    // 插入方法 insert(value) { this.heap.push(value) this.shiftUp(this.heap.length - 1) }

    // 删除方法 pop() { this.heap[0] = this.heap.pop() this.shiftDown(0) }

    // 获取堆顶 top() { return this.heap[0] }

    // 获取堆大小 size() { return this.heap.length } }

/**

  • @param {number[]} nums
  • @param {number} k
  • @return {number} */ var findKthLargest = function(nums, k) { // 使用 sort // return nums.sort((a, b) => a - b)[nums.length - k]

    // 使用最小堆 const h = new MinHeap() nums.forEach(v => { h.insert(v) if (h.size() > k) { h.pop() } }) return h.top() }; ```

翻转二叉树 - 226

leetcode地址
标签:二叉树、分而治之
解题思路

  • 先翻转左右子树,再将子树换个位置
  • 符合“分、解、合”,即分而治之的特性

解题步骤

  1. 分:获取左右子树
  2. 解:递归地翻转左右子树
  3. 合:将翻转后的左右子树,换个位置放到根节点上
    1. /**
    2. * Definition for a binary tree node.
    3. * function TreeNode(val, left, right) {
    4. * this.val = (val===undefined ? 0 : val)
    5. * this.left = (left===undefined ? null : left)
    6. * this.right = (right===undefined ? null : right)
    7. * }
    8. */
    9. /**
    10. * @param {TreeNode} root
    11. * @return {TreeNode}
    12. */
    13. var invertTree = function(root) {
    14. if (!root) return null
    15. return {
    16. val: root.val,
    17. left: invertTree(root.right),
    18. right: invertTree(root.left)
    19. }
    20. };

    删除链表中的节点 - 237

    leetcode地址
    标签:链表
    解题思路
    解题步骤
    1. /**
    2. * Definition for singly-linked list.
    3. * function ListNode(val) {
    4. * this.val = val;
    5. * this.next = null;
    6. * }
    7. */
    8. /**
    9. * @param {ListNode} node
    10. * @return {void} Do not return anything, modify node in-place instead.
    11. */
    12. var deleteNode = function(node) {
    13. node.val = node.next.val
    14. node.next = node.next.next
    15. };

反转字符串 - 344

leetcode地址
标签:字符串、双指针
解题思路

  • 通过左右两个指针 left、right 分别从数组首尾向中间遍历,并交换

    1. /**
    2. * @param {character[]} s
    3. * @return {void} Do not return anything, modify s in-place instead.
    4. */
    5. var reverseString = function(s) {
    6. const len = s.length
    7. if (len <= 1) return s
    8. let left = 0
    9. let right = len - 1
    10. while (left < right) {
    11. const temp = s[left]
    12. s[left] = s[right]
    13. s[right] = temp
    14. left += 1
    15. right -= 1
    16. }
    17. return s
    18. };

前 K 个高频元素 - 347

leetcode地址
标签:堆、最小堆
解题思路

  1. 看到 “前 K 个元素”
  2. 考虑使用最小堆

解题步骤

  1. 构建一个最小堆,依次把数组的值插入堆中
  2. 当堆的容量超过 K,删除堆顶
  3. 插入结束后,当前的堆即是前 K 个高频元素 ```javascript class MinHeap { constructor() { this.heap = [] }

    swap(i1, i2) { const temp = this.heap[i1] this.heap[i1] = this.heap[i2] this.heap[i2] = temp }

    getParentIndex(i) { return (i - 1) >> 1 }

    getLeftIndex(i) { return i * 2 + 1 }

    getRightIndex(i) { return i * 2 + 2 }

    shiftUp(index) { if (index === 0) return const parentIndex = this.getParentIndex(index)

    if (this.heap[parentIndex] && this.heap[parentIndex].value > this.heap[index].value) { this.swap(parentIndex, index) this.shiftUp(parentIndex) } }

    shiftDown(index) { const leftIndex = this.getLeftIndex(index) const rightIndex = this.getRightIndex(index)

    if (this.heap[leftIndex] && this.heap[leftIndex].value < this.heap[index].value) { this.swap(leftIndex, index) this.shiftDown(leftIndex) } if (this.heap[rightIndex] && this.heap[rightIndex].value < this.heap[index].value) { this.swap(rightIndex, index) this.shiftDown(rightIndex) } }

    // 插入方法 insert(value) { this.heap.push(value) this.shiftUp(this.heap.length - 1) }

    // 删除方法 pop() { this.heap[0] = this.heap.pop() this.shiftDown(0) }

    // 获取堆顶 top() { return this.heap[0] }

    // 获取堆大小 size() { return this.heap.length } }

/**

  • @param {number[]} nums
  • @param {number} k
  • @return {number[]} */ var topKFrequent = function(nums, k) { const map = new Map() nums.forEach(v => { map.set(v, map.has(v) ? map.get(v) + 1 : 1) }) // 使用 sort // const list = […map].sort((a, b) => b[1] - a[1]) // return list.slice(0, k).map(v => v[0])

    // 使用最小堆 const h = new MinHeap() map.forEach((value, key) => { h.insert({ value, key }) if (h.size() > k) { h.pop() } }) return h.heap.map(v => v.key) }; ```

两个数组的交集 - 349

  1. /**
  2. * @param {number[]} nums1
  3. * @param {number[]} nums2
  4. * @return {number[]}
  5. */
  6. var intersection = function(nums1, nums2) {
  7. // 使用集合
  8. // return [...new Set(nums1)].filter(v => nums2.includes(v))
  9. // 使用字典
  10. const map = new Map()
  11. nums1.forEach(v => {
  12. map.set(v, true)
  13. })
  14. const res = []
  15. nums2.forEach(v => {
  16. if (map.get(v)) {
  17. res.push(v)
  18. map.delete(v)
  19. }
  20. })
  21. return res
  22. };

猜数字大小 - 374

leetcode地址
标签:搜索、二分搜索

  1. /**
  2. * Forward declaration of guess API.
  3. * @param {number} num your guess
  4. * @return -1 if num is lower than the guess number
  5. * 1 if num is higher than the guess number
  6. * otherwise return 0
  7. * var guess = function(num) {}
  8. */
  9. /**
  10. * @param {number} n
  11. * @return {number}
  12. */
  13. var guessNumber = function(n) {
  14. let begin = 1
  15. let end = n
  16. while (begin <= end) {
  17. const mid = Math.floor((begin + end) / 2)
  18. if (guess(mid) === 0) {
  19. return mid
  20. } else if (guess(mid) === -1) {
  21. end = mid - 1
  22. } else if (guess(mid) === 1) {
  23. begin = mid + 1
  24. }
  25. }
  26. };


太平洋大西洋水流问题 - 417

标签:图、邻接矩阵、深度优先遍历
太平洋大西洋水流问题.png
解题思路

  1. 把矩阵相像成图
  2. 从海岸线逆流而上遍历图,所到之处就是可以留到某个大洋的坐标

解题步骤

  1. 新建两个矩阵,分别存可流向太平洋和大西洋的坐标
  2. 分别对4条海岸线上节点进行深度优先遍历,过程填充以上两个矩阵
  3. 遍历两个矩阵,找到同时可流向太平洋和大西洋的坐标
  1. /**
  2. * @param {number[][]} matrix
  3. * @return {number[][]}
  4. */
  5. var pacificAtlantic = function(matrix) {
  6. if (!matrix || !matrix[0]) return []
  7. const m = matrix.length
  8. const n = matrix[0].length
  9. const flow1 = Array.from({ length: m }, () => new Array(n).fill(false)) // 太平洋
  10. const flow2 = Array.from({ length: m }, () => new Array(n).fill(false)) // 大西洋
  11. const dfs = (r, c, flow) => {
  12. flow[r][c] = true;
  13. [[r - 1, c], [r + 1, c], [r, c -1], [r, c + 1]].forEach(([nr, nc]) => {
  14. if (
  15. // 保证在矩阵中
  16. nr >= 0 && nr < m &&
  17. nc >= 0 && nc < n &&
  18. // 防止死循环
  19. !flow[nr][nc] &&
  20. // 保证逆流而上
  21. matrix[nr][nc] >= matrix[r][c]
  22. ) {
  23. dfs(nr, nc, flow)
  24. }
  25. })
  26. }
  27. // 从海岸线往上遍历
  28. for (let r = 0; r < m; r += 1) {
  29. dfs(r, 0, flow1)
  30. dfs(r, n - 1, flow2)
  31. }
  32. for (let c = 0; c < n; c += 1) {
  33. dfs(0, c, flow1)
  34. dfs(m - 1, c, flow2)
  35. }
  36. // 找出俩矩阵同为 true 的值
  37. const res = []
  38. for (let r = 0; r < m; r += 1) {
  39. for (let c = 0; c < n; c += 1) {
  40. if (flow1[r][c] && flow2[r][c]) res.push([r, c])
  41. }
  42. }
  43. return res
  44. };

分饼干 - 455

leetcode地址
标签:贪心算法
解题思路

  • 局部最优:既能满足孩子,还能消耗最少
  • 先将 “较小的饼干” 分给 “胃口最小” 的孩子

解题步骤

  1. 对饼干数组和胃口数组升序排序
  2. 遍历饼干数组,找到能满足第一个孩子的饼干
  3. 然后继续遍历饼干数组,找到满足第二、三、……、n 个孩子的饼干

    1. /**
    2. * @param {number[]} g
    3. * @param {number[]} s
    4. * @return {number}
    5. */
    6. var findContentChildren = function(g, s) {
    7. g.sort((a, b) => a - b)
    8. s.sort((a, b) => a - b)
    9. let i = 0
    10. s.forEach(v => {
    11. if (v >= g[i]) {
    12. i += 1
    13. }
    14. })
    15. return i
    16. };

    斐波那契数 - 509

    leetcode地址
    标签:数组、动态规划
    解题思路
    解题步骤

    1. /**
    2. * @param {number} n
    3. * @return {number}
    4. */
    5. var fib = function(n) {
    6. if (n < 2) return n
    7. let dp0 = 0
    8. let dp1 = 1
    9. for (let i = 2; i <= n; i += 1) {
    10. const temp = dp1
    11. dp1 = dp1 + dp0
    12. dp0 = temp
    13. }
    14. return dp1
    15. // 便于理解写法
    16. // let dp = [0, 1]
    17. // for (let i = 2; i <= n; i += 1) {
    18. // dp[i] = dp[i - 1] + dp[i - 2]
    19. // }
    20. // return dp[n]
    21. };

有效的括号字符串 - 678

leetcode地址
标签:栈、双栈
解题思路

  • 左括号、星号各建立一个栈**
  • 遇到左括号和星号入栈,遇到右括号依次和左括号、星号做比较
  • 最后再判断是否星号都在左括号右侧

**

  1. /**
  2. * @param {string} s
  3. * @return {boolean}
  4. */
  5. var checkValidString = function(s) {
  6. const stackLeft = []
  7. const stackStar = []
  8. for (let i = 0; i < s.length; i += 1) {
  9. if (s[i] === '(') {
  10. stackLeft.push(i) // 存储索引
  11. } else if (s[i] === '*') {
  12. stackStar.push(i) // 存储索引
  13. } else {
  14. if (stackLeft.length) {
  15. stackLeft.pop()
  16. continue
  17. } else {}
  18. if (stackStar.length) {
  19. stackStar.pop()
  20. continue
  21. }
  22. return false
  23. }
  24. }
  25. if (stackLeft.length > stackStar.length) return false
  26. // 判断是否 * 都在 ( 的右边
  27. while (stackLeft.length) {
  28. const lTop = stackLeft.pop()
  29. const sTop = stackStar.pop()
  30. if (lTop > sTop) return false
  31. }
  32. return true
  33. };

计算最近请求次数 - 933

leetcode地址
标签:队列
解题思路
解题步骤

  1. var RecentCounter = function() {
  2. this.q = []
  3. };
  4. /**
  5. * @param {number} t
  6. * @return {number}
  7. */
  8. RecentCounter.prototype.ping = function(t) {
  9. this.q.push(t)
  10. while (this.q[0] < t - 3000) {
  11. this.q.shift()
  12. }
  13. return this.q.length
  14. };
  15. /**
  16. * Your RecentCounter object will be instantiated and called as such:
  17. * var obj = new RecentCounter()
  18. * var param_1 = obj.ping(t)
  19. */