树的遍历 leetCode
前序遍历
根节点最先访问 先访问根节点 --> 再遍历左子树 --> 最后遍历右子树
前序遍历顺序是: F B A D C E G I H
- 递归法
时间复杂度:O(n),其中 n 是二叉树的节点数,每个节点恰好被遍历一次
空间复杂度:O(n),为递归过程中栈的开销,平均情况下为O(Logn),最坏情况数呈现链状结构,为O(n)
function preorderTraversal(root: TreeNode | null): number[] {
const values: number[] = [];
//先构造前序遍历: 递归结构
const preDfs = (node: TreeNode) => {
if (node === null) {
return;
}
//1. 先访问根节点
values.push(node.val);
//2. 再访问左子树
if (node.left !== null) {
preDfs(node.left);
}
//3. 再访问右子树
if (node.right !== null) {
preDfs(node.right);
}
}
//递归调用
preDfs(root);
//返回最终值
return values;
};
- 迭代法 时间复杂度O(n):每个节点都被遍历了一次
空间复杂度O(n)
function preorderTraversal(root: TreeNode | null): number[] {
if (root === null) { return []; }
//1.定义一个栈,使用数组来模拟一个栈,将rootNode作为初始值,压入栈
const stack: TreeNode[] = [root];
//2.迭代,当栈中有元素时,就出栈。然后将该元素的两个节点压入栈中,
//注意,因为栈是先进后出,所以先压入右节点,再压入左节点。
const res = [];
while (stack.length > 0) {
const node = stack.pop();
res.push(node.val);
if (node.right !== null) {
stack.push(node.right);
}
if (node.left !== null) {
stack.push(node.left);
}
}
//返回最终值
return res;
};
中序遍历
根节点中间访问 先遍历左子树 --> 再访问根节点 --> 最后遍历右子树
中序遍历顺序是: A B C D E F G H I
- dfs 递归法
时间复杂度:
O(n),其中n是二叉树的节点数,每个节点恰好被遍历一次
空间复杂度:O(n),为递归过程中栈的开销,平均情况下为O(Logn),最坏情况数呈现链状结构,为O(n)
function inorderTraversal(root: TreeNode | null): number[] {
const res: number[] = [];
const midDfs = (node: TreeNode) => {
if (node === null) {
return;
}
if (node.left !== null) {
dfs(node.left);
}
res.push(node.val);
if (node.right !== null) {
midDfs(node.right);
}
}
midDfs(root);
return res;
};
- 迭代法 时间复杂度O(n):每个节点都被遍历了一次
空间复杂度O(n)
function inorderTraversal(root: TreeNode | null): number[] {
if (root === null) {return [];}
const res: number[] = [];
const stack: TreeNode[] = [];
let p = root;
while (stack.length > 0 || p) {
//先判断是否有节点
while (p) {
//有的话先压入栈,因为是中序
stack.push(p);
//p 指向 p.left
p = p.left;
}
//取出栈顶的节点
const node = stack.pop();
res.push(node.val);
//将p 指向 p = node.right
p = node.right;
}
return res;
};
后序遍历
根节点最后访问 先遍历左子树 --> 再遍历右子树 --> 最后访问根节点
后续序遍历顺序是: A C E D B H I G F
时间复杂度:O(n),其中 n 是二叉树的节点数,每个节点恰好被遍历一次
空间复杂度:O(n),为递归过程中栈的开销,平均情况下为O(Logn),最坏情况数呈现链状结构,为O(n)
function postorderTraversal(root: TreeNode | null): number[] {
if (root === null) { return []; }
const res: number[] = [];
const sufDfs = (node: TreeNode) => {
if (node.left !== null) {
sufDfs(node.left);
}
if (node.right !== null) {
sufDfs(node.right);
}
res.push(node.val);
}
sufDfs(root);
return res;
};
- 迭代法 时间复杂度O(n):每个节点都被遍历了一次
空间复杂度O(n)
function postorderTraversal(root: TreeNode | null): number[] {
if (!root) return [];
const res = [];
const stack = [root];
while (stack.length) {
const n = stack.pop();
res.push(n.val); // 根->右->左
n.left && stack.push(n.left);
n.right && stack.push(n.right);
}
return res.reverse(); // 左->右->根
};
注意 返回值是
res.reverse();
层序遍历
一层层的从左到右遍历 遍历顺序是: F B G A D I C E H
时间复杂度O(n)
空间复杂度O(n)
//层序遍历顺序就是广度优先遍历 bfs
//不过在遍历的时候需要记录当前节点所处的层级,方便将其添加到不同的数组中
function levelOrder(root: TreeNode | null): number[][] {
if (root === null) {
return [];
}
//最终返回的二维数组
const res: number[][] = [];
//使用数组模拟栈
let queue: TreeNode[] = [root];
while (queue.length > 0) {
//保存当前这一层的值
const level: number[] = [];
//拿到当前queue的长度
const queueSize = queue.length;
for (let i = 0; i < queueSize; i++) {
//把队列最前的元素推出出来
const node = queue.shift();
//将当前节点的值保存到这一级数组中
level.push(node.val);
//将左右孩子压入到queue中,再下一次遍历的时候拿出来
if (node.left) {
queue.push(node.left);
}
if (node.right) {
queue.push(node.right);
}
}
//将当前层级的数组push到返回值的数组中
res.push(level);
}
return res;
};