树
二叉树的先序,中序,后序遍历
先序遍历表示先访问根节点,然后访问左节点,最后访问右节点。
中序遍历表示先访问左节点,然后访问根节点,最后访问右节点。
后序遍历表示先访问左节点,然后访问右节点,最后访问根节点。
递归实现
递归实现相当简单,代码如下
function TreeNode(val) {
this.val = val;
this.left = this.right = null;
}
var traversal = function(root) {
if (root) {
// 先序
console.log(root);
traversal(root.left);
// 中序
// console.log(root);
traversal(root.right);
// 后序
// console.log(root);
}
};
对于递归的实现来说,只需要理解每个节点都会被访问三次就明白为什么这样实现了。
非递归实现
非递归实现使用了栈的结构,通过栈的先进后出模拟递归实现。
以下是先序遍历代码实现
function pre(root) {
if (root) {
let stack = [];
// 先将根节点 push
stack.push(root);
// 判断栈中是否为空
while (stack.length > 0) {
// 弹出栈顶元素
root = stack.pop();
console.log(root);
// 因为先序遍历是先左后右,栈是先进后出结构
// 所以先 push 右边再 push 左边
if (root.right) {
stack.push(root.right);
}
if (root.left) {
stack.push(root.left);
}
}
}
}
以下是中序遍历代码实现
function mid(root) {
if (root) {
let stack = [];
// 中序遍历是先左再根最后右
// 所以首先应该先把最左边节点遍历到底依次 push 进栈
// 当左边没有节点时,就打印栈顶元素,然后寻找右节点
// 对于最左边的叶节点来说,可以把它看成是两个 null 节点的父节点
// 左边打印不出东西就把父节点拿出来打印,然后再看右节点
while (stack.length > 0 || root) {
if (root) {
stack.push(root);
root = root.left;
} else {
root = stack.pop();
console.log(root);
root = root.right;
}
}
}
}
以下是后序遍历代码实现,该代码使用了两个栈来实现遍历,相比一个栈的遍历来说要容易理解很多
function pos(root) {
if (root) {
let stack1 = [];
let stack2 = [];
// 后序遍历是先左再右最后根
// 所以对于一个栈来说,应该先 push 根节点
// 然后 push 右节点,最后 push 左节点
stack1.push(root);
while (stack1.length > 0) {
root = stack1.pop();
stack2.push(root);
if (root.left) {
stack1.push(root.left);
}
if (root.right) {
stack1.push(root.right);
}
}
while (stack2.length > 0) {
console.log(s2.pop());
}
}
}
中序遍历的前驱后继节点
实现这个算法的前提是节点有一个 parent
的指针指向父节点,根节点指向 null
。
如图所示,该树的中序遍历结果是 4, 2, 5, 1, 6, 3, 7
前驱节点
对于节点 2
来说,他的前驱节点就是 4
,按照中序遍历原则,可以得出以下结论
- 如果选取的节点的左节点不为空,就找该左节点最右的节点。对于节点
1
来说,他有左节点2
,那么节点2
的最右节点就是5
- 如果左节点为空,且目标节点是父节点的右节点,那么前驱节点为父节点。对于节点
5
来说,没有左节点,且是节点2
的右节点,所以节点2
是前驱节点 - 如果左节点为空,且目标节点是父节点的左节点,向上寻找到第一个是父节点的右节点的节点。对于节点
6
来说,没有左节点,且是节点3
的左节点,所以向上寻找到节点1
,发现节点3
是节点1
的右节点,所以节点1
是节点6
的前驱节点
以下是算法实现
function predecessor(node) {
if (!node) return
// 结论 1
if (node.left) {
return getRight(node.left)
} else {
let parent = node.parent
// 结论 2 3 的判断
while(parent && parent.right === node) {
node = parent
parent = node.parent
}
return parent
}
}
function getRight(node) {
if (!node) return
node = node.right
while(node) node = node.right
return node
}
后继节点
对于节点 2
来说,他的后继节点就是 5
,按照中序遍历原则,可以得出以下结论
- 如果有右节点,就找到该右节点的最左节点。对于节点
1
来说,他有右节点3
,那么节点3
的最左节点就是6
- 如果没有右节点,就向上遍历直到找到一个节点是父节点的左节点。对于节点
5
来说,没有右节点,就向上寻找到节点2
,该节点是父节点1
的左节点,所以节点1
是后继节点
以下是算法实现
function successor(node) {
if (!node) return
// 结论 1
if (node.right) {
return getLeft(node.right)
} else {
// 结论 2
let parent = node.parent
// 判断 parent 为空
while(parent && parent.left === node) {
node = parent
parent = node.parent
}
return parent
}
}
function getLeft(node) {
if (!node) return
node = node.left
while(node) node = node.left
return node
}
树的深度
树的最大深度:该题目来自 Leetcode,题目需要求出一颗二叉树的最大深度
以下是算法实现
var maxDepth = function(root) {
if (!root) return 0
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1
};
对于该递归函数可以这样理解:一旦没有找到节点就会返回 0,每弹出一次递归函数就会加一,树有三层就会得到3。
当前内容版权归 InterviewMap 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请访问 InterviewMap .