Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
树的遍历
- 对于树的遍历有前、中、后序遍历三种方法。
- 前序遍历:先遍历节点自己,再遍历当前节点的left,最后是当前节点的right。
自己->left->right - 中序遍历:先遍历左子树left,再遍历自己,最后是节点的right。
left->自己->right - 后序遍历:先遍历left,然后right,最后遍历自己。
left->right->自己
下面看一个例子:
| 对右图进行遍历: | |
|---|---|
| 前序遍历结果 | 4 -> 2 -> 1 -> 3 -> 7 -> 6 -> 9 |
| 中序遍历结果 | 1 2 3 4 6 7 9 |
| 后序遍历结果 | 1 3 2 6 9 7 4 |
leetcode144前序遍历
递归方法
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function(root, arr=[]) {
// 前序遍历:自己->left->right
if (root) {
arr.push(root.val)
preorderTraversal(root.left,arr)
preorderTraversal(root.right,arr)
}
return arr
};
上面是递归方法,我们也可以利用栈用迭代法来遍历:
内层遍历:设当前节点cur指向根节点,从根节点开始遍历,将当前节点缓存到栈。然后cur指向当前节点的左节点(cur=cur.left);然后记录这个节点,一直循环到没有左节点。此时就将根节点的左节点都遍历过了。这些节点也存入栈了。
外层遍历:然后从栈顶取出一个节点,找到其右节点,将cur指向右节点,再将这个右节点重新进行上面一串遍历左节点的操作。然后再循环进行这一步骤的找右节点就,进行右节点中左节点的遍历。。。
迭代法代码:
var preorderTraversal = function(root, arr=[]) {
// 前序遍历:自己->left->right
// 开始遍历:有一个stack栈存储
// 节点出栈,右孩子为目标节点
let result = []
let stack = []
let cur = root
while(cur||stack.length>0){
// 内层循环:遍历当前节点的所有left节点
while(cur) {
// 内层遍历将当前节点的左子树全部遍历完毕,并存下每个节点以备找其右子树
result.push(cur.val)
stack.push(cur) // 要通过stack中的值找其右子树
cur = cur.left
}
// 外层遍历将栈顶出栈,将其右子树做当前节点,继续遍历该节点的左子树入栈出栈找右子树这一这个过程
cur = stack.pop()
cur = cur.right
}
return result
};
以上思路讲解比较抽象,以上图树结构和前序遍历为例,详解迭代法代码过程思路:
① 先遍历根节点4(cur),将4入栈 (stack=[4])
② 遍历其左子树并入栈操作 (内层遍历):找到left为2,将2入栈 (stack=[4, 2]);继续找到左节点1,将1入栈(stack=[4, 2, 1]);再往下没有左节点了,跳出内层循环。
③ (外层遍历) :就将栈顶的节点1出栈,找其右节点为null,curl为null,stack不为空,继续循环。cur为null跳过内层循环。再进行出栈找右节点(找到栈顶2的right3),cur为节点3。
④ 当前节点为节点3。对当前节点先内层循环,记录自己的val和入栈,再找3的左子树没有,跳出内层循环;再进行外层循环的出栈找右节点,此时出栈的为节点4,cur为其右节点的7。
⑤ 当前节点为7,对7进行内层遍历,一直找左节点并记录后,stack=[7, 6]。cur再找左节点为null。跳出内层循环。进行外层循环,出栈找右节点,cur为栈顶6的右节点null;再进行外层循环出栈节点7,cur为其右节点9。
⑥ 当前节点为9,记录自己的值,找其左子树为null跳出内层循环,其右子树为null,且栈为空,跳出外层循环。整个遍历结束。
循环过程总结来说: 找左节点一直到底并记录当前节点 -> 没有左节点后栈顶出栈找右节点 -> 右节点变成当前节点再找当前节点的所有左节点并记录 -> 直到没有左节点后再出栈变成当前节点找到右节点 .... -> 一直到没有左节点,没有右节点,栈为空,结束整个遍历
leetcode94中序遍历
var inorderTraversal = function(root, arr=[]) {
// // 递归
// if (root) {
// inorderTraversal(root.left,arr)
// arr.push(root.val)
// inorderTraversal(root.right,arr)
// }
// return arr;
// 迭代
let res=[], stack=[], cur=root;
while(cur||stack.length) {
while(cur) {
stack.push(cur)
cur = cur.left // 找到最左的值
}
cur = stack.pop()
// console.log(cur.val) push最左的值,变成cur
res.push(cur.val)
cur = cur.right
}
return res
};
leetcode145后序遍历
var postorderTraversal = function(root, arr=[]) {
// left->right->自己
// // 递归
// if (root) {
// postorderTraversal(root.left, arr)
// postorderTraversal(root.right, arr)
// arr.push(root.val)
// }
// return arr
// 迭代
let res = [], stack=[], cur = root, cache=null;
while(cur||stack.length) {
while(cur) {
stack.push(cur) // 入栈
cur = cur.left //找到最左的值
}
cur = stack[stack.length-1] // 读取栈顶找right
if(cur.right&&cur.right!=cache) {
cur = cur.right // 有right进行下一次循环right节点找left
} else {
console.log(cur.val)
res.push(cur.val) // 找完left和right之后再出栈记录自己
stack.pop() // 出栈
cache = cur
cur = null
}
}
return res
};