记录 6 道算法题
二叉树前序遍历
简单的递归就能实现,顺序是 根左右
function preorderTraversal(root, result = []) {
if (!root) return result
result.push(root.val)
preorderTraversal(root.left, result)
preorderTraversal(root.right, result)
return result
}
N叉树前序遍历
有两种办法,一种是递归,一种是迭代。
递归比较简单,和二叉树前序遍历没有区别,只是二叉树可以直接传 root.left 和 root.right。而N叉树的子节点个数是不确定的,需要对 children 进行循环。
function preorder(root, result = []) {
if (!root) return result
result.push(root.val)
// 遍历收集
if (root.children) {
root.children.forEach(child => preorder(child, result))
}
return result
}
迭代和二叉树的区别也是子节点的不确定。递归是用函数调用栈,隐藏了栈的使用,迭代也可以用一个栈来实现。把节点推入数组里,用的时候弹出来,要保持栈顶是当前遍历到的某个节点。可以倒着将节点推入,即先推children的最后一个节点。这样在栈顶的就是children最左边的节点。
function preorder(root) {
if (!root) return []
const stack = [root]
const result = []
while (stack.length) {
let node = stack.pop()
result.push(node.val)
if ((node = node.children)) {
// 倒着推入
for (let i = node.length - 1; i >= 0; i--) {
stack.push(node[i])
}
}
}
return result
}
翻转二叉树
226. 翻转二叉树 - 力扣(LeetCode) (leetcode-cn.com)
只要遍历每一个节点把 left 和 right 进行调转就行
function invertTree(root) {
if (!root) return root
const temp = root.left
root.left = root.right
root.right = temp
invertTree(root.left)
invertTree(root.right)
return root
}
从上到下打印二叉树 ii
要求是一层层打印二叉树,所以肯定是用到了层序遍历。但是遍历到的每个节点都将他们的left right 推入同一个数组,重点是每一层的分界在哪。
function levelOrder(root) {
if (!root) return []
const result = []
let stack = [root]
while (stack.length) {
const temp = []
// 重点是怎么让每一层有一个区分。这里比较巧妙的地方是,
// 每一次 while 都是一层,把这时候的stack 计个数,
// 就区分出了每一层
for (let i = stack.length; i > 0; i--) {
const node = stack.shift()
temp.push(node.val)
if (node.left) {
stack.push(node.left)
}
if (node.right) {
stack.push(node.right)
}
}
result.push(temp)
}
return result
}
二叉树的层序遍历 ii
这道题要求是从底层向上一层层记录,其实只要正常的层序遍历,然后把每一层结果都推入到数组的开头就可以了。
在这之前先讲讲层序遍历吧。层序遍历是广度优先,递归是深度优先。意味着广度是从上面的节点,扫描完,然后再扫描下面的节点。
通常的做法是 将节点推入数组,然后每次都从数组的开头取值,像队列一样。因为每次都会推入节点的 left 和 right。所以在某一层的最后一个时,它是数组的开头。它的下一个就是这一层第一个节点的 left。大概是下面这种感觉
a
b c
d e f
第一次 [a], shift() 拿到 a,将 a.left a.right 推入数组,现在数组里是 [b, c]
第二次 [b, c], shift(), 拿到 b,将 b.left b.right 推入数组,现在数组是 [c. d, e]
第三次 [c, d, e], shift(),拿到 c,将 c.left, c.right 推入数组。现在数组是 [d, e, f]
然后一种重复,直到数组长度为0。 这时候所有节点都读了一遍
可以看到,当第三次的时候,已经是第二层的节点了。
然后每一层的边界怎么区分出来呢。就要用到一个临时数组。将 left right 推入到临时的数组。等 队列数组为0时,代表已经收集完下一层的节点。就将 临时数组赋值给队列数组就可以了。
function levelOrderBottom(root) {
if (!root) return []
let stack = [root]
const result = []
while (stack.length) {
const temp = []
// 和上一题的思路一样,将当前的长度作为边界的标记。
for (let i = stack.length; i > 0; i--) {
const node = stack.shift()
temp.push(node.val)
node.left && stack.push(node.left)
node.right && stack.push(node.right)
}
// 插入到开头,最后得到的就是从底层到第一层的结果了
result.unshift(temp)
}
return result
}
二叉树的锯齿形层序遍历
要求像蛇形一样,每一层的方向相反。其实层序遍历的方法不会改变,还是一层层的推入数组,但是可以在输出的结果数组里面做文章。
function zigzagLevelOrder(root) {
if (!root) return []
const stack = [root]
const result = []
// 这里进行了改造
let a = 'push'
while (stack.length) {
const temp = []
for (let i = stack.length; i > 0; i--) {
const node = stack.shift()
// 通过切换 push 和 unshift,改变方向,实现蛇形
temp[a](node.val)
node.left && stack.push(node.left)
node.right && stack.push(node.right)
}
// 每经过一层变一次
a = a === 'push' ? 'unshift' : 'push'
result.push(temp)
}
return result
}
结束