难度标识:
⭐:简单,⭐⭐:中等,⭐⭐⭐:困难。
tips:这里的难度不是根据LeetCode难度定义的,而是根据我解题之后体验到题目的复杂度定义的。
前言
二叉树这节分两部分,首先要讲二叉树必须讲一下二叉树的遍历,就像数组链表一样,最基础的操作肯定是遍历,那么二叉树要怎样遍历呢?大家肯定知道使用递归和迭代,我下面的代码都是基于迭代的。前序、中序、后序遍历迭代的方式都是使用栈的方式模拟递归的过程,层序遍历迭代的方式是使用队列模拟递归的过程。前序、中序、后序遍历属于深度优先遍历,先遍历每个子节点的所有子节点,可以理解为从上到下,从左到右(或从右到左)的顺序遍历,先上下遍历完再往右遍历。层序遍历属于广度优先遍历,一层一层的遍历,优先兄弟节点遍历,一层一层的遍历。掌握了二叉树的这4种遍历方式才能继续做二叉树相关的题目,因为这4种遍历方式是二叉树的基础。
推荐学习资料
1.二叉树的前序遍历 ⭐
思路
迭代方法(使用栈):
-
使用一个栈来存储待访问的节点。
-
从根节点开始,每访问一个节点,将其值加入结果列表,并将其右子节点和左子节点依次压入栈(因为栈是后进先出的,所以要先压入右子节点)。
-
从栈中弹出节点并访问,直到栈为空。
代码
var preorderTraversal = function (root) {
if (!root) return []
// 前序:根 => 左 => 右
const stk = [root], res = []
while (stk.length) {
let node = stk.pop()
res.push(node.val)
if (node.right) stk.push(node.right)
if (node.left) stk.push(node.left)
}
return res
};
2.二叉树的中序遍历 ⭐
思路
迭代方法(使用栈):
-
从根节点开始: 从给定的二叉树的根节点开始。
-
深入左子树: 沿左子树一路深入,每经过一个节点,就把它存储起来(这里使用栈),直到到达树的最左边。
-
访问并转向右子树: 当无法继续深入左子树时(遇到一个叶子节点),从存储结构中取出最近存入的节点(即从栈顶取出一个节点),这个节点是最底下的左子树节点,将这个节点的值储存到结果数组中,接着,尝试对这个节点的右子树进行相同的操作。
-
返回: 如果某节点的右子树为空,再次从存储结构中取出节点(栈顶取出)并访问,这时其实取出的节点就是上面那个最底下的左子节点的根节点了,将这个节点值储存到结果数组中,然后再尝试其右子树。
-
结束条件: 当存储结构栈为空,且没有节点可以访问时,遍历结束。
代码
var inorderTraversal = function (root) {
// 中序:左 => 中 => 右
if (!root) return []
const stk = [], res = []
let node = root
while (node || stk.length) {
while (node) {
stk.push(node)
node = node.left
}
node = stk.pop()
res.push(node.val)
node = node.right
}
return res
};
3.二叉树的后序遍历 ⭐
思路
二叉树的前序遍历:根节点 => 左节点 => 右节点。而二叉树的后序遍历:左节点 => 右节点 => 根节点。你看出来什么没有,如果我将前序遍历变成 根节点 => 右节点 => 左节点,然后再将结果反转,是不是就是后序遍历的结果了,这样是不是就很简单了。
代码
var postorderTraversal = function (root) {
// 后序:左 => 右 => 中
if (!root) return []
const stk = [root], res = []
while (stk.length) {
let node = stk.pop()
res.push(node.val)
if (node.left) stk.push(node.left)
if (node.right) stk.push(node.right)
}
return res.reverse()
};
4.二叉树的层序遍历 ⭐
思路
-
初始化队列: 从根节点开始,首先将根节点放入一个队列中。
-
遍历每一层: 在开始每一层的遍历之前,首先记下当前队列的长度,这个长度就是当前层的节点数。
-
节点出队并记录: 对于当前层的每一个节点,执行以下操作:
- 将其从队列中移出(出队)。
- 记录或保存其值。
- 将它的左右子节点(如果存在的话)加入队列(入队)。
-
转到下一层: 当前层的所有节点都被处理完后,队列中现在存放的是下一层的所有节点。然后重复步骤2和3,直到队列为空,这意味着所有的层都被处理完了。
-
返回结果: 将每一层的节点值按照从上到下的顺序返回。
简单来说,层序遍历的思路是使用一个队列来帮助我们按层次顺序访问节点。当我们访问某个节点时,我们考虑它的子节点,并将它们放入队列中。这样,当我们完成当前层的遍历时,队列中就存放了下一层的所有节点,我们可以继续这个过程,直到所有的节点都被访问过。
代码
var levelOrder = function (root) {
if (!root) return []
const q = [root], res = []
while (q.length) {
const levelSize = q.length, levelRes = [];
for (let i = 0; i < levelSize; i++) {
const node = q.shift()
levelRes.push(node.val)
if (node.left) q.push(node.left)
if (node.right) q.push(node.right)
}
res.push(levelRes)
}
return res
};
这4种遍历方式是解二叉树相关题型的基础,所以一定要先掌握,当然除了上面迭代的方法你也可以去掌握递归的方法。