前言
本文讲解二叉树这种结构,并且结合力扣题目讲解使用两种方式去实现它的前、中、后序遍历
二叉树
二叉树是每个节点最多有两个子节点的树结构,这两个子节点分别被称为左子节点和右子节点
二叉树有以下几种常见的遍历方式:
-
前序遍历(Preorder Traversal):
- 先访问根节点,然后遍历左子树,最后遍历右子树。
- 例如对于一棵二叉树
[1,2,3,4,5],前序遍历的结果是[1,2,4,5,3]。
-
中序遍历(Inorder Traversal):
- 先遍历左子树,然后访问根节点,最后遍历右子树。
- 例如对于一棵二叉树
[1,2,3,4,5],中序遍历的结果是[4,2,5,1,3]。
-
后序遍历(Postorder Traversal):
- 先遍历左子树,然后遍历右子树,最后访问根节点。
- 例如对于一棵二叉树
[1,2,3,4,5],后序遍历的结果是[4,5,2,3,1]。
下面我们结合力扣上的一些题目来看如何使用递归和迭代两种方式实现这三种遍历:
力扣题直达:
递归方式实现
前序遍历
// 前序遍历
function preorderTraversal(root) {
const result = [];
function dfs(node) {
if (node) {
result.push(node.val);
dfs(node.left);
dfs(node.right);
}
}
dfs(root);
return result;
}
中序遍历
// 中序遍历
function inorderTraversal(root) {
const result = [];
function dfs(node) {
if (node) {
dfs(node.left);
result.push(node.val);
dfs(node.right);
}
}
dfs(root);
return result;
}
后序遍历
// 后序遍历
function postorderTraversal(root) {
const result = [];
function dfs(node) {
if (node) {
dfs(node.left);
dfs(node.right);
result.push(node.val);
}
}
dfs(root);
return result;
}
三段递归的主要区别就是在于输出数据的时机不同
下面我们解释一下递归
递归是一种在函数或算法中直接或间接地调用自身的方法
递归的基本思想是将一个复杂的问题分解为一个或多个与原问题相似但规模更小的子问题,通过不断地重复调用自身来解决这些子问题,直到达到某种基础情况(也称为终止条件),此时不再进行递归调用,而是直接返回结果
递归的优点在于它可以使某些问题的解决变得简洁和直观,特别是对于那些具有明显自相似性的问题
然而,递归也有一些缺点。如果递归深度过大,可能会导致栈溢出错误。而且在某些情况下,递归的效率可能不如迭代(使用循环)的方式
迭代器
前序遍历
// 前序遍历
function preorderTraversalIterative(root) {
if (!root) return [];
const stack = [root];
const result = [];
while (stack.length > 0) {
const node = stack.pop();
result.push(node.val);
if (node.right) {
stack.push(node.right);
}
if (node.left) {
stack.push(node.left);
}
}
return result;
}
-
首先判断根节点
root是否为空,如果为空则返回一个空数组。 -
创建一个栈
stack,并将根节点root推入栈中。 -
创建一个结果数组
result。 -
进入一个
while循环,只要栈不为空就继续循环。 -
在循环中:
- 弹出栈顶元素,即当前处理的节点
node。 - 将当前节点的值
node.val推入结果数组result。 - 如果当前节点有右子节点
node.right,则将右子节点推入栈中。 - 如果当前节点有左子节点
node.left,则将左子节点推入栈中。
- 弹出栈顶元素,即当前处理的节点
-
循环结束后,返回最终的结果数组
result。
中序遍历
// 中序遍历
function inorderTraversalIterative(root) {
if (!root) return [];
const stack = [];
const result = [];
let node = root;
while (node || stack.length > 0) {
while (node) {
stack.push(node);
node = node.left;
}
node = stack.pop();
result.push(node.val);
node = node.right;
}
return result;
}
-
首先判断根节点
root是否为空,如果为空则返回一个空数组。 -
创建一个栈
stack和一个结果数组result。 -
定义一个当前节点变量
node,初始化为根节点root。 -
进入一个
while循环,只要node不为空或者栈不为空就继续循环。 -
在循环中:
- 首先使用
while循环,将所有左子节点依次压入栈中。 - 然后弹出栈顶元素,即当前的中间节点
node。 - 将中间节点的值
node.val添加到结果数组result中。 - 最后将
node更新为其右子节点node.right。
- 首先使用
-
循环结束后,返回最终的结果数组
result
后序遍历
// 后序遍历
function postorderTraversalIterative(root) {
if (!root) return [];
const stack1 = [root];
const stack2 = [];
const result = [];
while (stack1.length > 0) {
const node = stack1.pop();
stack2.push(node);
if (node.left) {
stack1.push(node.left);
}
if (node.right) {
stack1.push(node.right);
}
}
while (stack2.length > 0) {
result.push(stack2.pop().val);
}
return result;
}
-
首先判断根节点
root是否为空,如果为空则返回一个空数组。 -
创建两个栈
stack1和stack2。stack1用于存储遍历顺序的节点,stack2用于存储最终的结果顺序。 -
创建一个结果数组
result。 -
进入一个
while循环,只要stack1不为空就继续循环。 -
在循环中:
- 弹出
stack1栈顶元素,即当前处理的节点node。 - 将该节点推入
stack2。 - 如果该节点有左子节点
node.left,则将左子节点推入stack1。 - 如果该节点有右子节点
node.right,则将右子节点推入stack1。
- 弹出
-
当
stack1为空时,说明已经遍历完所有节点。此时,将stack2中的所有节点依次弹出,并将其值添加到结果数组result中。 -
最后返回结果数组
result
总结
本文讲解二叉树这种结构,并且结合力扣题目讲解使用两种方式去实现它的前、中、后序遍历,相信看到这里的你一定会有所收获的!!!