「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
题目
给你二叉树的根节点 root ,返回它节点值的 前序 **遍历。
示例 1:
递归解法:
先传入根节点root和初始化结果数组res=[],若root不为空,则res.push(root.val),然后递归调用自身并传入root.left,处理完左节点后,递归调用处理右节点root.right。
递归解法代码:
/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function(root,res=[]) {
if(root == null){
return res;
}
res.push(root.val)
preorderTraversal(root.left,res)
preorderTraversal(root.right,res)
return res
};
迭代解法:
递归的方法,相当于系统维护了一个栈,每次递归调用的时候,就把剩下的代码逻辑存到栈中,等递归到最后一层后,开始往外弹,再一层一层去执行存在栈中的剩下逻辑代码。
那么迭代的解法,就需要自己去维护一个栈,二叉树的前序遍历,是根左右的遍历顺序,那么我们每次遍历根节点后,依次将右节点、左节点压入栈,然后再从栈中取出左节点,当作新的“根节点”,再将新的右节点、左节点压入栈中以此类推。
迭代解法代码:
/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function(root,res=[]) {
if(root == null){
return res;
}
let stack = [];
stack.push(root);
while(stack.length>0){
let node = stack[stack.length - 1];
stack.pop();
res.push(node.val);
if(node.right){
stack.push(node.right);
}
if(node.left){
stack.push(node.left);
}
}
return res
};
morris解法:
思路:
设立两个指针,p1和p2,p1指向当前遍历节点,p2指向p1左叶的最右子节点。正常来说,未经处理的p2节点,应该p2 != null && p2.right == null,或者直接是p2 ==null
p2 的状态有以下三种:
-
如果
p2 != null && p2.right==null,则说明该节点第一次来,还没有构建返回指针,那么让p2.right=p1,并且打印p1的值。循环该操作,将左侧所有右侧末尾节点都构建上返回指针。 -
如果
p2 == null,则说明p1已经走到自身左叶左节点最末尾,那么就打印p1的值,并让p1往右走,即p1 = p1.right,由于p1目前的节点在上一步就已经构建了返回路径指针,所以可以正常回到自身所处的树的根节点的根节点。 -
如果
p2 !== null && p2.right !== nul,那么说明p2曾已经已经来过,并且构建过返回指针,并且这时候p1肯定已经从这个节点走过了,那么就让p2.right == null,而且此时的p1正处在目前p2所在的树的根节点的根节点,这个节点在一开始就打印过,所以这次不用打印,让p1继续往右走,p1 = right。
重复循环上述操作,即可完成遍历。
总的来说,p2就这三种状态,整个遍历也是围绕的p2的状态来走的。
这里回头我会补一个动画分解图。
代码实现如下:
/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function(root,res=[]) {
if(root == null){
return res;
}
let p1 = root;
let p2 = null;
while(p1 != null){
p2 = p1.left;
if(p2 != null){
while(p2.right != null && p2.right != p1){
p2 = p2.right;
}
if(p2.right == null){
res.push(p1.val);
p2.right = p1;
p1 = p1.left;
continue;
}
p2.right = null;
}else{
res.push(p1.val);
}
p1 = p1.right;
}
return res;
};