目录
题目
给定一个二叉树,返回它的 前序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3输出: [1,2,3]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
关于二叉树的遍历,有两种主流方式:
- 深度优先搜索(DFS)
在这个策略中,我们采用深度作为优先级,以便从跟开始一直到达某个确定的叶子,然后再返回根到达另一个分支。
深度优先搜索策略又可以根据根节点、左孩子和右孩子的相对顺序被细分为前序遍历,中序遍历和后序遍历。
- 宽度优先搜索(BFS)
我们按照高度顺序一层一层的访问整棵树,高层次的节点将会比低层次的节点先被访问到。
前序遍历代码
-
迭代 + 栈
使用栈遍历数,然后弹出栈顶元素用栈保存结果序列。
/**
* 迭代:
* 使用栈保存数。从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压右孩子再压左孩子。
*/
public List<Integer> preorderTraversal(TreeNode root){
LinkedList<TreeNode> stack = new LinkedList<TreeNode>();
LinkedList<Integer> res = new LinkedList<Integer>();
if (root == null) {
return res;
}
stack.add(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pollLast();
res.add(node.val);
if (node.right != null) {
stack.add(node.right);// 栈:先进后出 ,所以出来的res为(left,right)
}
if (node.left != null) {
stack.add(node.left);
}
}
return res;
}
算法复杂度
时间复杂度:访问每个节点恰好一次,时间复杂度为 O(N)O(N) ,其中 NN 是节点的个数,也就是树的大小。
空间复杂度:取决于树的结构,最坏情况存储整棵树,因此空间复杂度是 O(N)O(N)。
-
递归实现前序遍历(最基础)
/**
* 递归实现前序遍历
*/
public static List<Integer> preorderTraversalByRecursive(TreeNode root) {
List<Integer> res = new LinkedList<Integer>();
if (root == null) return res;
dfs(root, res);
return res;
}
private static void dfs(TreeNode tree , List<Integer> res) {
// 根
res.add(tree.val);
// 左
if (tree.left != null ) {
dfs(tree.left, res);
}
// 右
if (tree.right != null){
dfs(tree.right, res);
}
}
-
莫里斯遍历(了解)
方法基于 莫里斯原理,可以优化空间复杂度。算法不会使用额外空间,只需要保存最终的输出结果。如果实时输出结果,那么空间复杂度是 O(1)O(1)。
算法的思路是从当前节点向下访问先序遍历的前驱节点,每个前驱节点都恰好被访问两次。
首先从当前节点开始,向左孩子走一步然后沿着右孩子一直向下访问,直到到达一个叶子节点(当前节点的中序遍历前驱节点),所以我们更新输出并建立一条伪边 predecessor.right = root 更新这个前驱的下一个点。如果我们第二次访问到前驱节点,由于已经指向了当前节点,我们移除伪边并移动到下一个顶点。
如果第一步向左的移动不存在,就直接更新输出并向右移动。
/**
* Morris
*/
public static List<Integer> preorderTraversalByMorris(TreeNode root) {
LinkedList<Integer> output = new LinkedList<Integer>();
TreeNode node = root;
while (node != null) {
if (node.left == null) {
output.add(node.val);
node = node.right;
} else {
TreeNode predecessor = node.left;
while ((predecessor.right != null) && (predecessor.right != node)) {
predecessor = predecessor.right;
}
if (predecessor.right == null) {
output.add(node.val);
predecessor.right = node;
node = node.left;
} else {
predecessor.right = null;
node = node.right;
}
}
}
return output;
}
总结
1、前序遍历的三种常见写法;
2、二叉树的遍历总结,见连接。
完整测试代码及结果
import java.util.LinkedList;
import java.util.List;
/**
* 前序遍历
*/
public class PreorderTraversal {
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.setLeft(new TreeNode(0));
root.setRight(new TreeNode(2));
root.getRight().setLeft(new TreeNode(3));
root.getRight().setRight(new TreeNode(0));
// 递归实现
List<Integer> res = preorderTraversalByRecursive(root);
// 迭代实现
List<Integer> res2 = preorderTraversal(root);
// Morris实现
List<Integer> res3 = preorderTraversalByMorris(root);
System.out.println("递归实现:");
for (Integer item : res) {
System.out.print(item + " ");
}
System.out.println(" ");
System.out.println("迭代实现:");
for (Integer item : res2) {
System.out.print(item + " ");
}
System.out.println(" ");
System.out.println("Morris实现:");
for (Integer item : res2) {
System.out.print(item + " ");
}
}
/**
* 迭代:
* 使用栈保存数。从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压右孩子再压左孩子。
*/
public static List<Integer> preorderTraversal(TreeNode root) {
LinkedList<TreeNode> stack = new LinkedList<TreeNode>();
LinkedList<Integer> res = new LinkedList<Integer>();
if (root == null) {
return res;
}
stack.add(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pollLast();
res.add(node.val);
if (node.right != null) {
stack.add(node.right);// 栈:先进后出 ,所以出来的res为(left,right)
}
if (node.left != null) {
stack.add(node.left);
}
}
return res;
}
/**
* 递归实现前序遍历
*/
public static List<Integer> preorderTraversalByRecursive(TreeNode root) {
List<Integer> res = new LinkedList<Integer>();
if (root == null) return res;
dfs(root, res);
return res;
}
private static void dfs(TreeNode tree, List<Integer> res) {
// 根
res.add(tree.val);
// 左
if (tree.left != null) {
dfs(tree.left, res);
}
// 右
if (tree.right != null) {
dfs(tree.right, res);
}
}
/**
* Morris
*/
public static List<Integer> preorderTraversalByMorris(TreeNode root) {
LinkedList<Integer> output = new LinkedList<Integer>();
TreeNode node = root;
while (node != null) {
if (node.left == null) {
output.add(node.val);
node = node.right;
} else {
TreeNode predecessor = node.left;
while ((predecessor.right != null) && (predecessor.right != node)) {
predecessor = predecessor.right;
}
if (predecessor.right == null) {
output.add(node.val);
predecessor.right = node;
node = node.left;
} else {
predecessor.right = null;
node = node.right;
}
}
}
return output;
}
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val) {
this.val = val;
}
public void setVal(int val) {
this.val = val;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public void setRight(TreeNode right) {
this.right = right;
}
public int getVal() {
return val;
}
public TreeNode getLeft() {
return left;
}
public TreeNode getRight() {
return right;
}
}
测试结果
输入结构如题,null用0代替。
递归实现:
1 0 2 3 0
迭代实现:
1 0 2 3 0
Morris实现:
1 0 2 3 0
Process finished with exit code 0