前言:二叉树对于算法来说可谓是重中之重,而二叉树的遍历是二叉树的基础,是学好二叉树的前提,之前一直是一知半解,这次我来好好总结下,如果对你有帮助希望可以点个赞,十分感谢!
一、递归版
前序遍历
function preorder(root,res = []) {
if (!root) {
return res;
}
res.push(root.val);
root.left && preorder(root.left, res);
root.right && preorder(root.right, res);
return res;
}
中序遍历
function inorder(root,res = []) {
if (!root) {
return res;
}
root.left && inorder(root.left, res);
res.push(root.val);
root.right && inorder(root.right, res);
return res;
}
后序遍历
function postorder(root,res = []) {
if (!root) {
return res;
}
root.left && postorder(root.left, res);
root.right && postorder(root.right, res);
res.push(root.val);
return res;
}
二、迭代版-模拟出栈顺序
前序遍历
function preorder(root) {
if (!root) {
return [];
}
const stack = [root];
const res = [];
while(stack.length) {
// 迭代法模拟递归过程,后进先出,所以node.left要放在后面
let node = stack.pop();
res.push(node.val);
node.right && stack.push(node.right);
node.left && stack.push(node.left);
}
return res;
}
中序遍历
function inorder(root) {
const stack = [];
const res = [];
let cur = root;
while(stack.length || cur) {
// 通过指针的遍历访问到二叉树左侧的最底层
while(cur) {
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
res.push(cur.val);
cur = cur.right;
}
return res;
}
后序遍历
function postorder(root) {
if (!root) {
return [];
}
const stack = [root];
const res = [];
while(stack.length) {
let node = stack.pop();
res.unshift(node.val);
node.left && stack.push(node.left);
node.right && stack.push(node.right);
}
return res;
}
三、迭代版统一写法(标记法)
通过标记法控制值出栈的顺序
前序遍历
function preorder(root) {
if (!root) {
return [];
}
const stack = [root];
const res = [];
while(stack.length) {
let node = stack.pop();
while(node === null) {
res.push(stack.pop().val);
continue;
}
node.right && stack.push(node.right);
node.left && stack.push(node.left);
stack.push(node);
stack.push(null);
}
return res;
}
中序遍历
function inorder(root) {
if (!root) {
return [];
}
const stack = [root];
const res = [];
while(stack.length) {
let node = stack.pop();
while(node === null) {
res.push(stack.pop().val);
continue;
}
node.right && stack.push(node.right);
stack.push(node);
stack.push(null);
node.left && stack.push(node.left);
}
return res;
}
后序遍历
function postorder(root) {
if (!root) {
return [];
}
const stack = [root];
const res = [];
while(stack.length) {
let node = stack.pop();
while(node === null) {
res.push(stack.pop().val);
continue;
}
stack.push(node);
stack.push(null);
node.right && stack.push(node.right);
node.left && stack.push(node.left);
}
return res;
}
四、层序遍历
function levelOrder(root) {
if (!root) {
return [];
}
const stack = [root];
const res = [];
while (stack.length) {
const length = stack.length;
const arr = [];
for (let i = 0; i < length; i++){
// 采用先进先出策略,防止后面插入的数据影响取值
const node = stack.shift();
arr.push(node.val);
node.left && stack.push(node.left);
node.right && stack.push(node.right);
}
res.push(arr);
}
return res;
}
总结:
不管是先中后序遍历还是层序遍历,左节点都会先输出
- 若采用后进先出策略(push,pop),则left在right下面执行,如前序遍历
- 若采用先进先出策略(push,shift),则left在right上面执行,如层序遍历
- 特殊情况:后序遍历中由于通过unshift改变了输出结果,所以即使采用了后进先出策略,left也在right上面执行