Leetcode 102.二叉树的层序遍历

题目: 102.二叉树的层序遍历 讲解:https://www.bilibili.com/video/BV1GY4y1u7b2

初始思路

类似于广度优先搜索

代码

  1. var levelOrder = function (root) {
  2. if (!root) return []
  3. let queue = []
  4. let res = []
  5. queue.push(root)
  6. while (queue.length) {
  7. // 记录当前层有多少个节点
  8. let length = queue.length
  9. // 存放当前层的节点
  10. let curLevel = []
  11. for (let i = 0; i < length; i++){
  12. // 将这个节点弹出
  13. let node = queue.shift()
  14. curLevel.push(node.val)
  15. // 存放当这个节点的左右节点
  16. if (node.left) {
  17. queue.push(node.left)
  18. }
  19. if (node.right) {
  20. queue.push(node.right)
  21. }
  22. }
  23. // 把每一层结果放到结果数组中
  24. res.push(curLevel)
  25. }
  26. return res
  27. };

感想

  1. 看完讲解之后看代码更清晰了。
  2. curLevel存放着每一层的节点,queue中是原始队列。

Leetcode 107.二叉树的层序遍历 II

题目:107.二叉树的层序遍历 II

初始思路

这不就是把上一题结果翻转一下吗

代码

  1. var levelOrderBottom = function (root) {
  2. if (!root) return []
  3. let queue = []
  4. let res = []
  5. queue.push(root)
  6. while (queue.length) {
  7. // 记录当前层有多少个节点
  8. let length = queue.length
  9. // 存放当前层的节点
  10. let curLevel = []
  11. for (let i = 0; i < length; i++){
  12. // 将这个节点弹出
  13. let node = queue.shift()
  14. curLevel.push(node.val)
  15. // 存放当这个节点的左右节点
  16. if (node.left) {
  17. queue.push(node.left)
  18. }
  19. if (node.right) {
  20. queue.push(node.right)
  21. }
  22. }
  23. // 把每一层结果放到结果数组中
  24. res.push(curLevel)
  25. }
  26. return res.reverse()
  27. };

感想

  1. 还真是!

Leetcode 199.二叉树的右视图

题目:199.二叉树的右视图

初始思路

我觉得也可以用深度优先搜索,从右子树开始递归遍历

代码

  1. var rightSideView = function (root) {
  2. if (!root) return []
  3. let queue = []
  4. let res = []
  5. queue.push(root)
  6. while (queue.length) {
  7. // 记录当前层有多少个节点
  8. let length = queue.length
  9. for (let i = 0; i < length; i++){
  10. // 将这个节点弹出
  11. let node = queue.shift()
  12. if (i === length - 1) {
  13. // 不影响层序遍历
  14. // 只有当走到了这层的最后一个节点的时候再加入结果中
  15. res.push(node.val)
  16. }
  17. // 存放当这个节点的左右节点
  18. if (node.left) {
  19. queue.push(node.left)
  20. }
  21. if (node.right) {
  22. queue.push(node.right)
  23. }
  24. }
  25. }
  26. return res
  27. };

感想

  1. 深度优先搜索的题解:https://leetcode.cn/problems/binary-tree-right-side-view/solution/javascriptban-jie-ti-si-lu-by-ityou-o-lkl1/
  2. 用广度优先搜索的话,相比前两题:结果res保存的是节点,不是数组了,所以不用curLevel了。在遍历的时候判断到了最后一个节点的时候加入res

Leetcode 637.二叉树的层平均值

题目:637.二叉树的层平均值

初始思路

OK,层序遍历的时候顺便计算一下平均值。

代码

  1. var averageOfLevels = function (root) {
  2. if (!root) return []
  3. let queue = []
  4. let res = []
  5. queue.push(root)
  6. while (queue.length) {
  7. // 记录当前层有多少个节点
  8. let length = queue.length
  9. // 存放当前层的节点
  10. let curLevel = []
  11. for (let i = 0; i < length; i++) {
  12. // 将这个节点弹出
  13. let node = queue.shift()
  14. curLevel.push(node.val)
  15. // 存放当这个节点的左右节点
  16. if (node.left) {
  17. queue.push(node.left)
  18. }
  19. if (node.right) {
  20. queue.push(node.right)
  21. }
  22. }
  23. // 求平均值
  24. let s = curLevel.reduce((sum, curVal) => {
  25. return sum + curVal
  26. }, 0)
  27. // 把每一层结果放到结果数组中
  28. res.push(s / curLevel.length)
  29. }
  30. return res
  31. };

感想

  1. 还复习了一下reduce的使用
  2. 本质就是第一题的改造

Leetcode 429.N 叉树的层序遍历

题目:429.N 叉树的层序遍历

初始思路

本质还是第一题的改造,但是看Node的定义可以知道,没有left和right了,只有children

代码

  1. var levelOrder = function (root) {
  2. if (!root) return []
  3. let queue = []
  4. let res = []
  5. queue.push(root)
  6. while (queue.length) {
  7. // 记录当前层有多少个节点
  8. let length = queue.length
  9. // 存放当前层的节点
  10. let curLevel = []
  11. for (let i = 0; i < length; i++){
  12. // 将这个节点弹出
  13. let node = queue.shift()
  14. curLevel.push(node.val)
  15. // 这里做出改造,遍历children节点
  16. for (let item of node.children) {
  17. if (item) queue.push(item)
  18. }
  19. }
  20. // 把每一层结果放到结果数组中
  21. res.push(curLevel)
  22. }
  23. return res
  24. };

感想

  1. 把遍历左右节点改成遍历children节点即可。

Leetcode 515.在每个树行中找最大值

题目:515.在每个树行中找最大值

初始思路

套路题

代码

  1. var largestValues = function (root) {
  2. if (!root) return []
  3. let queue = []
  4. let res = []
  5. queue.push(root)
  6. while (queue.length) {
  7. // 假设初始值是第一个元素
  8. let max = queue[0].val
  9. // 记录当前层有多少个节点
  10. let length = queue.length
  11. // 存放当前层的节点
  12. let curLevel = []
  13. for (let i = 0; i < length; i++) {
  14. // 将这个节点弹出
  15. let node = queue.shift()
  16. max = max > node.val ? max : node.val
  17. // 存放当这个节点的左右节点
  18. if (node.left) {
  19. queue.push(node.left)
  20. }
  21. if (node.right) {
  22. queue.push(node.right)
  23. }
  24. }
  25. // 把每一层结果放到结果数组中
  26. res.push(max)
  27. }
  28. return res
  29. };

感想

  1. 本来用的是Math.max()对每一行的curLevel求最大值,但是应该是有null的原因,求出来是NaN
  2. 还是在遍历的时候直接求吧

Leetcode 116.填充每个节点的下一个右侧节点指针

题目:116.填充每个节点的下一个右侧节点指针

初始思路

不是纯改模板了

代码

  1. var connect = function (root) {
  2. if (!root) return root
  3. let queue = []
  4. queue.push(root)
  5. while (queue.length) {
  6. // 记录当前层有多少节点
  7. let length = queue.length
  8. for (let i = 0; i < length; i++) {
  9. // 将这个节点弹出
  10. let node = queue.shift()
  11. if (i < length - 1) {
  12. // 当节点不是这层最后一个的时候,弹出的时候next指向下一个节点即可
  13. // 因为next默认为null,不需要对最后一个节点做特殊处理
  14. node.next = queue[0]
  15. }
  16. // 存放当这个节点的左右节点
  17. if (node.left) {
  18. queue.push(node.left)
  19. }
  20. if (node.right) {
  21. queue.push(node.right)
  22. }
  23. }
  24. }
  25. return root
  26. };

感想

  1. 当节点不是这层最后一个的时候,弹出的时候next指向下一个节点即可。因为next默认为null,不需要对最后一个节点做特殊处理
  2. 被root为空坑了一把,不能返回空数组,要返回root。因为返回值类型是Node!

Leetcode 117.填充每个节点的下一个右侧节点指针 II

题目:117.填充每个节点的下一个右侧节点指针 II

初始思路

恩?上一题是满二叉树,这题不是满二叉树而已,没有任何区别吧。。。

代码

  1. var connect = function(root) {
  2. if (!root) return root
  3. let queue = []
  4. queue.push(root)
  5. while (queue.length) {
  6. // 记录当前层有多少节点
  7. let length = queue.length
  8. for (let i = 0; i < length; i++) {
  9. // 将这个节点弹出
  10. let node = queue.shift()
  11. if (i < length - 1) {
  12. // 当节点不是这层最后一个的时候,弹出的时候next指向下一个节点即可
  13. // 因为next默认为null,不需要对最后一个节点做特殊处理
  14. node.next = queue[0]
  15. }
  16. // 存放当这个节点的左右节点
  17. if (node.left) {
  18. queue.push(node.left)
  19. }
  20. if (node.right) {
  21. queue.push(node.right)
  22. }
  23. }
  24. }
  25. return root
  26. };

感想

  1. 跟上一道题一样的代码,但是LeetCode就是过不去。而且黏贴别人的代码也报一样的错。很难不怀疑是不是LeetCode的问题。
  2. 语言换成TypeScript通过了。好吧!

Leetcode 104.二叉树的最大深度

题目:104.二叉树的最大深度

初始思路

在遍历的时候同时记录层数。最大的深度就是二叉树的层数。

代码

  1. var maxDepth = function(root) {
  2. if (!root) return 0
  3. let queue = []
  4. let res = []
  5. queue.push(root)
  6. // 记录深度
  7. let depth = 0
  8. while (queue.length) {
  9. // 记录当前层有多少个节点
  10. let length = queue.length
  11. depth++
  12. for (let i = 0; i < length; i++){
  13. // 将这个节点弹出
  14. let node = queue.shift()
  15. // 存放当这个节点的左右节点
  16. if (node.left) {
  17. queue.push(node.left)
  18. }
  19. if (node.right) {
  20. queue.push(node.right)
  21. }
  22. }
  23. }
  24. return depth
  25. };

感想

  1. root判空记得返回0,不是空数组。

Leetcode 111.二叉树的最小深度

题目:111.二叉树的最小深度

初始思路

比上一题多一个判断什么时候结束

代码

  1. var minDepth = function (root) {
  2. if (!root) return 0
  3. let queue = []
  4. let res = []
  5. queue.push(root)
  6. // 记录深度
  7. let depth = 0
  8. while (queue.length) {
  9. // 记录当前层有多少个节点
  10. let length = queue.length
  11. depth++
  12. for (let i = 0; i < length; i++){
  13. // 将这个节点弹出
  14. let node = queue.shift()
  15. if (node.left == null && node.right == null) {
  16. // 如果左右节点都为空,则该节点深度最小
  17. return depth
  18. }
  19. // 存放当这个节点的左右节点
  20. if (node.left) {
  21. queue.push(node.left)
  22. }
  23. if (node.right) {
  24. queue.push(node.right)
  25. }
  26. }
  27. }
  28. return depth
  29. };

感想

  1. 如果左右节点都为空,则该节点深度最小。直接return,不用继续执行了。
  2. 我擦,十题终于做完了、

Leetcode 226.翻转二叉树

题目:226.翻转二叉树 讲解:https://www.bilibili.com/video/BV1sP4y1f7q7

初始思路

写了一天层序遍历,前序差点忘了。

代码

  1. // 递归前序遍历
  2. var invertTree = function (root) {
  3. if (!root) return null
  4. // 交换左右节点
  5. const rightNode = root.right
  6. root.right = invertTree(root.left)
  7. root.left = invertTree(rightNode)
  8. return root
  9. };
  1. // 迭代法前序遍历
  2. var invertTree = function (root) {
  3. // 交换节点
  4. const invertNode = function (root, left, right) {
  5. let temp = left
  6. left = right
  7. right = temp
  8. root.left = left
  9. root.right = right
  10. }
  11. if (!root) return null
  12. let stack = []
  13. stack.push(root)
  14. while (stack.length) {
  15. let current = stack.pop()
  16. if (!current) {
  17. // 以右左中的顺序压栈,以中左右的顺序弹栈
  18. if (current.right) stack.push(current.right)
  19. if (current.left) stack.push(current.left)
  20. stack.push(current)
  21. stack.push(null)
  22. } else {
  23. current = stack.pop()
  24. invertNode(current, current.left, current.right)
  25. }
  26. }
  27. return root
  28. }
  1. // 层序遍历法
  2. var invertTree = function (root) {
  3. // 交换节点
  4. const invertNode = function (root, left, right) {
  5. let temp = left
  6. left = right
  7. right = temp
  8. root.left = left
  9. root.right = right
  10. }
  11. if (!root) return null
  12. let queue = []
  13. queue.push(root)
  14. while (queue.length) {
  15. // 记录当前层有多少个节点
  16. let length = queue.length
  17. for (let i = 0; i < length; i++){
  18. // 将这个节点弹出
  19. let node = queue.shift()
  20. // 翻转节点
  21. invertNode(node, node.left, node.right)
  22. // 存放当这个节点的左右节点
  23. if (node.left) {
  24. queue.push(node.left)
  25. }
  26. if (node.right) {
  27. queue.push(node.right)
  28. }
  29. }
  30. }
  31. return root
  32. }

感想

  1. 原来有这么多写法
  2. 递归前序肯定是最简单最好想的。迭代法还是有点复杂。层序 写了一天,感觉挺熟的了。

Leetcode 101.对称二叉树

题目:101.对称二叉树 讲解:https://www.bilibili.com/video/BV1ue4y1Y7Mf

初始思路

真的是简单题?

代码

  1. // 递归
  2. var isSymmetric = function (root) {
  3. // 1 确定递归的参数 root.left root.right和返回值true false
  4. const compareNode = function (left, right) {
  5. // 2 确定终止情况
  6. if ((left == null && right != null) || (left != null && right == null)) {
  7. // 左右节点其中一个为空,另一个不是。不对称
  8. return false
  9. } else if (left == null && right == null) {
  10. // 左右都为空。对称
  11. return true
  12. } else if (left.val != right.val) {
  13. // 左右节点不为空,且数值也不相同。不对称
  14. return false
  15. }
  16. // 左右节点都不为空,数值相同,进入以下递归
  17. // 3 确定单层递归逻辑
  18. let outside = compareNode(left.left, right.right)
  19. let inside = compareNode(left.right, right.left)
  20. return outside && inside
  21. }
  22. if (!root) return true
  23. return compareNode(root.left, root.right)
  24. };
  1. // 队列实现迭代判断
  2. var isSymmetric = function (root) {
  3. if (!root) return true
  4. let queue = []
  5. queue.push(root.left)
  6. queue.push(root.right)
  7. while (queue.length) {
  8. let leftNode = queue.shift()
  9. let rightNode = queue.shift()
  10. if (leftNode == null && rightNode == null) {
  11. continue
  12. }
  13. if ((leftNode == null & rightNode != null) || (leftNode != null & rightNode == null) || (rightNode.val !== rightNode.val)) {
  14. return false
  15. }
  16. // 外侧
  17. queue.push(leftNode.left)
  18. queue.push(rightNode.right)
  19. // 里侧
  20. queue.push(leftNode.right)
  21. queue.push(rightNode.left)
  22. }
  23. return true
  24. }
  1. // 栈实现迭代判断
  2. var isSymmetric = function (root) {
  3. if (!root) return true
  4. let stack = []
  5. stack.push(root.left)
  6. stack.push(root.right)
  7. while (stack.length) {
  8. let rightNode = stack.pop()
  9. let leftNode = stack.pop()
  10. if (leftNode == null && rightNode == null) {
  11. continue
  12. }
  13. if ((leftNode == null & rightNode != null) || (leftNode != null & rightNode == null) || (rightNode.val !== rightNode.val)) {
  14. return false
  15. }
  16. // 外侧
  17. stack.push(leftNode.left)
  18. stack.push(rightNode.right)
  19. // 里侧
  20. stack.push(leftNode.right)
  21. stack.push(rightNode.left)
  22. }
  23. return true
  24. }

感想

  1. 要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。
    外侧:左子树的左节点、右子树的右节点
    里测:左子树的右节点、右子树的左节点
    image.png
  2. 后面两种方法的写法其实差不多,调换一下顺序,因为出栈的顺序不一样。
  3. 今天这十几道题写了两个半小时。感觉脑子里全是left和right。