给定一个二叉树的根节点
root,返回 它的 中序 遍历 。
解法1 递归
思路
首先要搞清楚什么是中序遍历,其实什么序就是根节点在哪里,中序就是根节点在中间,按照左中右的顺序来遍历整颗树。
所以递归的时候按照左中右的顺序即可。
代码
function inorderTraversal(root: TreeNode | null): number[] {
if (!root) {
return [];
}
const result = [];
const dfs = (node) => {
if (!node) return;
dfs(node.left);
result.push(node.val);
dfs(node.right);
}
dfs(root);
return result;
};
时空复杂度
时间复杂度:遍历所有节点 O(n)
空间复杂度:递归会有栈开销,最差是只有左节点,所以是 O(n)
解法2 迭代
思路
迭代的话需要一个当前指针,和保存前序节点的栈。
一路走到最左边并将当前节点入栈,达到最左边之后弹出栈顶,将该节点的值放入结果集合当中。接下来转向该节点的右子树,直到循环结束。结束条件是当前节点为 null 或者 栈为空。
代码
function inorderTraversal(root: TreeNode | null): number[] {
if (!root) {
return [];
}
const result = [];
const stack = [];
let current = root;
while (current || stack.length > 0) {
while (current) {
stack.push(current);
current = current.left;
}
current = stack.pop();
result.push(current.val);
current = current.right;
}
return result;
};
时空复杂度
时间复杂度:遍历所有节点 O(n)
空间复杂度:显示栈开销 O(n)
解法3 最优解法Morris中序遍历
代码
function inorderTraversal(root: TreeNode | null): number[] {
const result: number[] = [];
let current = root;
while (current !== null) {
if (current.left === null) {
// 当前节点没有左子树,直接访问它
result.push(current.val);
// 然后往右边走
current = current.right;
} else {
// 找左子树中最右的节点(中序前驱)
let predecessor = current.left;
while (predecessor.right !== null && predecessor.right !== current) {
// 一直往右,直到找到最右边的那个节点
predecessor = predecessor.right;
}
if (predecessor.right === null) {
// 第一次到达当前节点,在前驱节点建立一个指向当前节点的临时链接(穿线)
predecessor.right = current;
// 然后继续往左子树走
current = current.left;
} else {
// 第二次到达当前节点,说明左子树已经访问过了
// 把之前建立的链接断掉(恢复树结构)
predecessor.right = null;
// 访问当前节点
result.push(current.val);
// 转向右子树
current = current.right;
}
}
}
return result;
};
时间复杂度:遍历所有节点 O(n)
空间复杂度:没有额外开销 O(1)