反转二叉树

image.png
image.png

  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 root;
  15. let tmp = root.left
  16. root.left = root.right
  17. root.right = tmp
  18. invertTree(root.left)
  19. invertTree(root.right)
  20. return root
  21. };

解析url

image.png
image.png

上述12-16行发现问题:需要完善代码
1.有些没有值
2.a&b&c 连 = 号都没有
3.url 有可能有[]以西是想解析成对象
4.可能有需要解码的时候
5.可能有数组

  1. function parse (str) {
  2. return str.split('&').reduce((o, kv) => {
  3. const [key, value] = kv.split('=')
  4. if (!value) {
  5. return o
  6. }
  7. console.log( key.split(/[\[\]]/g).filter(x => x));
  8. // o[key] = value
  9. deep_set(o, key.split(/[\[\]]/g).filter(x => x), value)
  10. return o
  11. }, {})
  12. }
  13. function deep_set (o, path, value) {
  14. let i = 0
  15. for (; i < path.length - 1; i++) {
  16. if (!o[path[i]]) {
  17. if (path[i + 1].match(/^\d+$/)) {
  18. o[path[i]] = []
  19. } else {
  20. o[path[i]] = {}
  21. }
  22. }
  23. o = o[path[i]]
  24. }
  25. o[path[i]] = decodeURIComponent(value)
  26. }
  27. console.log(parse('p=4&spm_id_from=pageDriver'));
  28. console.log(parse('a&b&c'));
  29. console.log(parse('a[name]=fox&a[b]=c&c=d'));
  30. console.log(parse('color=Deep%20Blue'));
  31. console.log(parse('a[0]=1&a[1]=2'));

includes

includes()方法返回的是布尔值,也就是true和false,这样上面的例子就可以简化一下了。
if(str.includes(‘测试’)){
console.log(true); // 包含
}else{
console.log(false) //不包含
}

N个数字和为M的问题

image.png
image.png

  1. /***
  2. * A 原数组
  3. * n 取几个数
  4. * m 要求的和
  5. * i 当前的层级数,也就是决策的数量
  6. * decisions 已经取出的数组
  7. */
  8. // 第一层级有一个看选不选,第二个层级就是2个,以指数倍数递增的
  9. function sum (A, n, m, i = 0, decisions = []) {
  10. console.log(A, n, m, i, decisions);
  11. // 经过前面的递归以后,和 等于0了,意思是刚好取好了,就直接输入数组
  12. if (m === 0) {
  13. return decisions
  14. }
  15. // 如果要当前层级数,已经和原来数组一致了,而且没有输出,就意思是没找到
  16. // 或者要取得个数没有了 ,已经无法再取值了,也没找到
  17. // 或者要取得数已经小于 0 了,也没找到, 这里是一个优化点
  18. // 剪枝,可以减少不必要的求解,虽然不影响结果,但是影响性能
  19. if (i === A.length || n === 0 || m < 0) {
  20. return null
  21. }
  22. // 转化成决策树,传入原数组,放入当前得数,这样要取得数,就少了一个,要求的结果也就少了一个,然后层数加1
  23. // 然后结果集做一个合并,如果有值,就在递归调用,如果返回了null ,就调用到了后面
  24. // 传入原数据,然后把层级 +1 ,输出结果集
  25. return sum(A, n - 1, m - A[i], i + 1, decisions.concat(A[i])) || sum(A, n, m, i + 1, decisions)
  26. }
  27. // console.log(sum([1, 9, 3, 7], 2, 10));
  28. // 一种优化 因为上述 ,有一个concat 是一个O(n)的操作
  29. // 谷歌指导书说,如果一直数据量不大的话,不要做微优化,除非是高性能场景,这样就需要优化到极致
  30. function sumN (A, n, m) {
  31. // 最终结果
  32. let r = null
  33. // 决策
  34. const decisions = []
  35. function inner (i = 0, n, m) {
  36. // 如果已经有解,就终止递归
  37. if (r) {
  38. return
  39. }
  40. // 如果已经找到就输出r
  41. // 把当前的数组拷贝一份给r,然后输出,因为后面还在复用,可能回改变值
  42. if (m === 0) {
  43. r = decisions.slice()
  44. return
  45. }
  46. if (i === A.length || n === 0) {
  47. return
  48. }
  49. decisions.push(A[i])
  50. inner(i + 1, n - 1, m - A[i])
  51. decisions.pop(A[i])
  52. inner(i + 1, n, m)
  53. }
  54. inner(0, n, m)
  55. return r
  56. }
  57. console.log(sumN([1, 9, 3, 7], 2, 10));

image.png
image.png

误区

image.png

树的轮廓

image.png
image.png

// 最左侧的数字
// 递归 + 决策树
/**
 * 
 * @param {*} node 树的结构
 * @param {*} d 当前层数
 * @param {*} outline 表示
 */
function leftoutLineTree (node, d = 0, outline = []) {
  if (!node) {
    return
  }
  // 如果当前这一层没有值,就赋值,也就是运用了树的遍历顺序,如果有值就不操作了
  if (!outline[d]) {
    outline[d] = node.value
  }
  leftoutLineTree(node.left, d + 1, outline)
  leftoutLineTree(node.right, d + 1, outline)
  return outline
}

// 每一行的最大值
/**
 * 
 * @param {*} node 树的结构
 * @param {*} d 当前层数
 * @param {*} outline 表示
 */
function maxOfLine (node, d = 0, outline = []) {
  if (!node) {
    return
  }
  // 如果当前这一层没有值,就赋值,也就是运用了树的遍历顺序,如果有值就不操作了
  outline[d] = Math.max(outline[d] || -1, node.value)
  maxOfLine(node.left, d + 1, outline)
  maxOfLine(node.right, d + 1, outline)
  return outline
}

火车车厢重排问题

image.png
image.png
image.png

function isTrans (o, t) {
  // 不匹配的队列
  const q = []
  // 遍历要排成的新数组
  for (let x of t) {
    // 队列中的最后一个是不是跟 重排车厢中的 一个相同 ,如果是就放到重排车厢
    if (q[q.length - 1] === x) {
      // 取出最后一个,意思是可以放到重排车厢,也就是匹配上了
      q.pop()
    }
    let y = null
    // 每次都从原始队列中取出第一个,如果 相同就跳过,不相同就进入
    while (o.length > 0 && (y = o.shift()) !== x) {
      q.unshift(y)
    }
    // 循环不变式: o中下一个要么和x匹配,要么o为空
  }
  // 如果最后不匹配的车厢还有值,就说明无法匹配
  return q.length === 0
}

console.log(isTrans([1, 2, 3, 4, 5], [1, 3, 2, 4, 5]));
console.log(isTrans([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]));

因为上面用的是数组模仿队列,shift 和 unshift 都是O(n)的操作,所以要用真队列来提高效率
image.png

image.png

网格路径

image.png
image.png

// 网格问题
function f (x, y) {
  if (x > 0 && y > 0) {
    return f(x - 1, y) + f(x, y - 1)
  }
  else if (x > 0) {
    return f(x - 1, y)
  }
  else if (y > 0) {
    return f(x, y - 1)
  }
  else {
    return 1
  }
}
// console.log(f(4, 3));

// 网格问题优化
// 数组中的横纵坐标是反着来的,y正好代表一行
function fy (x, y, dp = []) {
  // dp 是一个缓存数据,就不用每次都计算新值了
  if (!dp[y]) {
    dp[y] = []
  }
  if (dp[y][x] !== undefined) {
    return dp[y][x]
  }

  if (x > 0 && y > 0) {
    dp[y][x] = fy(x - 1, y, dp) + fy(x, y - 1, dp)
  }
  else if (x > 0) {
    dp[y][x] = f(x - 1, y, dp)
  }
  else if (y > 0) {
    dp[y][x] = f(x, y - 1, dp)
  }
  else {
    dp[y][x] = 1
  }
  return dp[y][x]
}
// console.log(fy(4, 3));
// 网格问题动态规划
function fd (w, h) {
  const dp = []
  for (let y = 0; y <= h; y++) {
    dp[y] = []
    for (let x = 0; x <= w; x++) {
      if (x === 0 && y === 0) {
        dp[y][x] = 1
      }
      else if (x === 0) {
        dp[y][x] = dp[y - 1][x]
      }
      else if (y === 0) {
        dp[y][x] = dp[y][x - 1]
      }
      else {
        dp[y][x] = dp[y][x - 1] + dp[y - 1][x]
      }
    }
  }
  return dp[h][w]
}
console.log(fd(4, 3));

两个栈实现一个队列

image.png

class Queue {
  constructor() {
    this.s1 = []
    this.s2 = []
  }
  // 入队
  enqueue (item) {
    this.s1.push(item)
  }
  // 出队
  // 看一下这里的复杂度,我们看13行好像每一次取值似乎都要遍历,像O(n)的操作
  // 但是其实如果现在没有新加入值得话,也就是第一次取值需要全部遍历一边,所以
  // 大体还是 O(1)得操作
  dequeue () {
    while (this.s1.length > 0) {
      this.s2.push(this.s1.pop())
    }
    if (this.s2.length > 0) {
      return this.s2.pop()
    }
  }
}
const queue = new Queue()
queue.enqueue(1)
queue.enqueue(2)
console.log(queue.dequeue());
console.log(queue.dequeue());