二叉树
树的遍历
二叉树
是一种更为典型的树状结构。如它名字所描述的那样,二叉树是每个节点最多有两个子树
的树结构,通常子树被称作“左子树”和“右子树”。
树的遍历 - 介绍
前序遍历
前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树。
中序遍历
中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树。
通常来说,对于二叉搜索树,我们可以通过中序遍历得到一个递增的有序序列。
后序遍历
后序遍历是先遍历左子树,然后遍历右子树,最后访问树的根节点。
值得注意的是,当你删除树中的节点时,删除过程将按照后序遍历的顺序进行。 也就是说,当你删除一个节点时,你将首先删除它的左节点和它的右边的节点,然后再删除节点本身。
另外,后序在数学表达中被广泛使用。 编写程序来解析后缀表示法更为容易。 这里是一个例子:
您可以使用中序遍历轻松找出原始表达式。 但是程序处理这个表达式时并不容易,因为你必须检查操作的优先级。
如果你想对这棵树进行后序遍历,使用栈来处理表达式会变得更加容易。 每遇到一个操作符,就可以从栈中弹出栈顶的两个元素,计算并将结果返回到栈中。
递归和迭代
二叉树的前序遍历:
给定一个二叉树,返回它的 前序 遍历。
示例:
[1,null,2,3] 1 \ 2 / 3 [1,2,3]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
解法一:递归法
递归法很简单,按照根节点、左子树、右子树的顺序。JavaScript中数组的连接使用concat,且此函数并不改变数组本身。
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function(root) {
if (!root) return [];
var result = [];
result.push(root.val);
result = result.concat(preorderTraversal(root.left));
result = result.concat(preorderTraversal(root.right));
return result;
};
解法二:迭代法
迭代法意在通过循环解决问题。建立一个栈,将根节点加入栈中,当栈不为空时:
1、取出栈顶元素 curr,访问curr
2、若curr右子节点不为空,则将curr右子节点加入栈
3、若curr左子节点不为空,则将curr左子节点加入栈
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function(root) {
if (!root) return [];
var result = [],
stack = [root];
console.log(stack);
while (stack.length !== 0) {
var curr = stack.pop();
if (curr.right) stack.push(curr.right);
if (curr.left) stack.push(curr.left);
result.push(curr.val);
}
return result;
};
arrayObject.pop( ) 删除并返回数组的最后一个元素;
push( ) 向数组的末尾添加一个或更多元素,并返回新的长度。
难点:二叉树实际入栈后是这样的:
[ TreeNode {
val: 1,
right: TreeNode { val: 2, right: null, left: [TreeNode] },
left: null } ]
所以,栈顶元素实际是根节点。
中序遍历二叉树:
给定一个二叉树,返回它的中序 遍历。
示例:
[1,null,2,3] 1 \ 2 / 3 [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
解法一:递归法/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } *//** * @param {TreeNode} root * @return {number[]} */var inorderTraversal = function(root) { if (!root) return []; var result = []; result = result.concat(inorderTraversal(root.left)); result.push(root.val); result = result.concat(inorderTraversal(root.right)); return result;};
解法二:迭代法
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } *//** * @param {TreeNode} root * @return {number[]} */var inorderTraversal = function(root) { if (!root) return []; var res=[]; //栈 var s=[]; var p = root; while (p || s.length>0) { //直至左节点为空,即没有左节点为止 while (p) { s.push(p); p = p.left; console.log(s); } //出栈,存放根节点 p = s.pop(); res.push(p.val); console.log('res',res); p = p.right; } return res;};
代码执行结果:
我的输入:
[1,4,2,3,null,5,7] 二叉树
我的标准输出:
[ TreeNode {
val: 1,
right: TreeNode { val: 2, right: [TreeNode], left: [TreeNode] },
left: TreeNode { val: 4, right: null, left: [TreeNode] } } ]
[ TreeNode {
val: 1,
right: TreeNode { val: 2, right: [TreeNode], left: [TreeNode] },
left: TreeNode { val: 4, right: null, left: [TreeNode] } },
TreeNode {
val: 4,
right: null,
left: TreeNode { val: 3, right: null, left: null } } ]
[ TreeNode {
val: 1,
right: TreeNode { val: 2, right: [TreeNode], left: [TreeNode] },
left: TreeNode { val: 4, right: null, left: [TreeNode] } },
TreeNode {
val: 4,
right: null,
left: TreeNode { val: 3, right: null, left: null } },
TreeNode { val: 3, right: null, left: null } ]
res [ 3 ]
res [ 3, 4 ]
res [ 3, 4, 1 ]
[ TreeNode {
val: 2,
right: TreeNode { val: 7, right: null, left: null },
left: TreeNode { val: 5, right: null, left: null } } ]
[ TreeNode {
val: 2,
right: TreeNode { val: 7, right: null, left: null },
left: TreeNode { val: 5, right: null, left: null } },
TreeNode { val: 5, right: null, left: null } ]
res [ 3, 4, 1, 5 ]
res [ 3, 4, 1, 5, 2 ]
[ TreeNode { val: 7, right: null, left: null } ]
res [ 3, 4, 1, 5, 2, 7 ]
我的答案:
[3,4,1,5,2,7]
。
二叉树的后序遍历
解法二:迭代法
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } *//** * @param {TreeNode} root * @return {number[]} */var postorderTraversal = function(root) { if (!root) return []; var stack = [root]; var res = []; while (stack.length > 0) { var item = stack[stack.length - 1]; //满足这些就可以直接输出它了。它是叶子节点。或它的子节点都ok了。 if ((item.left == null && item.right == null) || (item.left && item.left.isOk && item.right && item.right.isOk) || (item.left && item.left.isOk && item.right == null) || (item.left == null && item.right && item.right.isOk)) { item.isOk = true; console.log(item.val); res.push(item.val); stack.pop(); } else if (item.left && !item.left.isOk) { //如果左边的没ok,就把左边的入栈 stack.push(item.left); } else if (item.right && !item.right.isOk) { //如果右边的没ok就把右边的入栈。 stack.push(item.right); } } return res;};
。
本章节更新完毕!!!