function TreeNode(val) {
this.val = val;
this.left = null;
this.right = null;
}
function createBinaryTree(inputList) {
let node = null;
if (!inputList || inputList.length === 0) {
return null;
}
const data = inputList.shift();
if (data !== null) {
node = new TreeNode(data);
node.left = createBinaryTree(inputList);
node.right = createBinaryTree(inputList);
}
return node;
}
const inputList = [3, 2, 9, null, null,10,null,null,8,null, 4];
const root = createBinaryTree(inputList);
console.log(root);
/**
{
"val": 3,
"left": {
"val": 2,
"left": {
"val": 9,
"left": null,
"right": null
},
"right": {
"val": 10,
"left": null,
"right": null
}
},
"right": {
"val": 8,
"left": null,
"right": {
"val": 4,
"left": null,
"right": null
}
}
}
*/
我们现在已经能成功的构建出二叉树了,接下来我们来学习一下二叉树的遍历。显示递归的方式去写,这种通过递归栈的方式实现的遍历整体而言代码简单易于理解。具体代码如下。
function preOrderTraversal(node) {
if (node == null) {
return;
}
console.log(node.val);
preOrderTraversal(node.left);
preOrderTraversal(node.right);
}
console.log("前序遍历")
preOrderTraversal(root)
// 3 2 9 10 8 4
function inOrderTraversal(node) {
if (node == null) {
return;
}
inOrderTraversal(node.left);
console.log(node.val);
inOrderTraversal(node.right);
}
console.log("中序遍历")
inOrderTraversal(root)
// 9 2 10 3 8 4
function postOrderTraversal(node) {
if (node == null) {
return;
}
postOrderTraversal(node.left);
postOrderTraversal(node.right);
console.log(node.val);
}
console.log("后序遍历")
postOrderTraversal(root)
// 9 10 2 4 8 3
除了用递归的方式,我们也可以利用栈来实现二叉树非递归遍历,这里用到了回溯的思想,以前序遍历举例。
// 二叉树非递归前序遍历
function preOrderTraversalWithStack(root) {
const stack = [];
let treeNode = root;
while (treeNode || stack.length > 0) {
while (treeNode) {
console.log('打印', treeNode.val);
stack.push(treeNode);
// console.log('stack push', stack)
treeNode = treeNode.left;
// console.log('treeNode', treeNode ? treeNode.val : null)
}
if (stack.length > 0) {
treeNode = stack.pop();
// console.log('treeNode', treeNode ? treeNode.val : null)
// console.log('stack pop', stack)
treeNode = treeNode.right;
// console.log('treeNode', treeNode ? treeNode.val : null)
}
}
}
console.log("非递归前序遍历")
preOrderTraversalWithStack(root)
这段代码实现了二叉树的前序遍历算法,使用了栈的数据结构来辅助实现。 具体来说,代码中首先定义了一个空的栈和一个指向二叉树根节点的指针treeNode,然后通过while循环来遍历整个二叉树。 在while循环中,首先使用另一个while循环来将当前节点的所有左子节点都依次入栈,并将当前节点更新为其左子节点,直到当前节点的左子节点为空为止。这一步操作实现了先序遍历算法中访问节点的操作,即先访问根节点,然后依次访问其左子节点。 当当前节点的左子节点为空时,代码会进入if语句块中。在这里,代码会将栈顶的节点出栈并将其右子节点赋值给treeNode,以便继续遍历右子树。这一步操作实现了先序遍历算法中回溯的操作,即在左子树遍历完之后回到其父节点,然后再遍历其右子节点。 在while循环中,如果栈为空并且当前节点为空,那么整个遍历过程就结束了。 总体来说,这段代码使用了栈的数据结构来辅助实现二叉树的前序遍历算法,实现了依次访问节点、回溯到父节点并继续遍历右子树的操作。
我们以[1,2,null,null,3,null,null]这样一棵树来具体说明。
下面是对这段代码在输入为[1,2,null,null,3,null,null]的二叉树上的执行过程的解释:
1
/ \
2 3
/ \ / \
n n n n
- 首先,根据输入数组构造出二叉树。这个二叉树的根节点是值为1的节点,它有一个左子节点值为2,一个右子节点值为3。
- 然后,我们调用 preOrderTraversalWithStack 函数,并将根节点作为参数传入。此时 stack 数组为空,treeNode 指向根节点。
- 进入 while 循环,由于 treeNode 不为空,所以我们进入内层 while 循环。首先打印出 treeNode 的值,也就是1。然后将 treeNode 入栈,并将 treeNode 的左子节点赋值给 treeNode。
- treeNode 现在指向节点2。由于 treeNode 不为空,我们进入内层 while 循环。打印出 treeNode 的值,也就是2。然后将 treeNode 入栈,并将 treeNode 的左子节点赋值给 treeNode。
- treeNode 现在指向 null。因此,内层 while 循环结束。由于此时 stack 数组中有两个元素,我们从 stack 中弹出一个节点,并将 treeNode 指向该节点的右子节点null。这个节点是节点2。因此,此时 stack 数组中还有一个元素,它是节点1。
- treeNode 现在指向null, 但是stack中还有节点1,因此进入stack.length>0, 从栈里面pop出节点1,此时栈为空m=,并把treeNode指向节点1的right,也就是3
- treeNode 现在指向节点3。由于 treeNode 不为空,我们进入内层 while 循环。打印出 treeNode 的值,也就是3。接着将节点3入栈,此时栈的长度为1,然后将 treeNode 的左子节点赋值给 treeNode。由于节点3没有左子节点,treeNode 现在指向 null。
- 由于此时treeNode为null,因此跳出循环
- 由于 stack 数组中还有一个元素,也就是节点3,所以我们从 stack 中弹出该节点,并将 treeNode 指向该节点的右子节点null。此时 stack 数组为空。 10.由于 treeNode 现在指向 null,内层 while 循环结束。我们回到外层 while 循环的判断条件,发现 stack 数组为空且 treeNode 为 null,因此整个算法结束。