二叉树的前序, 中序, 后序, 层序遍历方法

 * Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }

解题思路

1.语法:

与二叉树相关的问题, 不要想得太复杂,数据结构抽象如下

            根节点
/ \
左子树 右子树

而左子树和右子树又可以看成和上面相同的数据结构,

即树具有递归性质的数据结构, 解决树的问题时,递归首选。

2.非递归解题思路:

二叉树除了根节点,所有的叶子节点都可以看成左节点或者右节点

关于子树根节点的处理很微妙,因为子树的根节点同样是上级子树的左儿子或者右儿子, 所以不用单独处理,只需要处理左右儿子节点就能搞定一棵树。如果根节点需要单独处理,只有一个也很容易处理。

先序遍历

先序遍历: 根-左-右, p.val 处理要在 p.left 和 p.right 处理之前

前序遍历中, 每个节点都是在它的子树之前处理. 然而, 尽管每个节点在其子树之前进行了处理, 但在向下移动的过程中还是需要保留一些信息. 当左子树遍历完成之后, 必须返回其右子树来继续遍历. 为了能够在左子树遍历完成后能移动到右子树, 必须保留根节点信息. 能够实现该信息存储的显然是栈, 它能以逆序获取信息并返回到右子树.

迭代解法

解法1

不推荐下面的解法, 以后都要使用解法二的思想.

class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
TreeNode p = root;
List<Integer> ans = new LinkedList<>(); if (p == null) return ans;
Stack<TreeNode> stack = new Stack<>(); // 由于在下面区分树的左右儿子节点的方式处理所有节点, 所以根节点我们额外处理, 很麻烦
stack.push(p);
ans.add(p.val); // 这种写法, 是区分树的左右儿子节点的方式处理.
// 如果能以对待根节点的方式对待所有节点, 很简洁, 见解法2
while (!stack.isEmpty()) {
// 所操作的 p 已入栈
if (p.left != null) {
p = p.left;
ans.add(p.val);
stack.push(p);
} else {
// 这样写真的很啰嗦, 因为我们使用 stack.peek 和 p 存储了当前需要处理节点的信息
// 我们只要能获取当前节点的信息就好, 何必要双重, 很麻烦
stack.pop(); // 当前节点入栈就处理了, 出栈, 然后处理它的右节点
p = p.right;
if (p != null) { // 由于区分树的左右儿子节点的方式处理, 这里还需要对右节点进行处理
ans.add(p.val);
stack.push(p);
} else {
//由于是先序遍历, 最后处理右孩子, 根节点和左孩子处理过了,
// 我们不知道现在栈中是否还有元素, 所以要判断栈是否为空
if (!stack.isEmpty()) {
p = stack.peek();
p.left = null; // 关键一步
}
}
}
}
return ans;
}
}

解法2

class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
TreeNode p = root;
List<Integer> ans = new LinkedList<>();
Stack<TreeNode> stack = new Stack<>(); // p != null 有双重效果:
// 第一, 第一次循环栈为空, 我们不需要先将 p 压栈
// 第二, 由于是先序遍历, 最后处理右孩子, 根节点和左孩子处理过了,
// 所以在处理右节点时, 栈可能是空的
while (!stack.isEmpty() || p != null) {
if (p != null) {
ans.add(p.val);
stack.push(p);
p = p.left;
} else {
// 这里不用判断栈是否为空是由于, 循环条件二选一, p == null, 则栈就不为空
p = stack.pop().right;
}
}
return ans; }
}

递归解法

class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ans = new LinkedList<>();
helper(root, ans);
return ans;
}
public void helper(TreeNode root, List<Integer> ans) {
// 函数返回值为 void, return 就好, 使用 return null 是错误的
if (root == null) return;
ans.add(root.val);
helper(root.left, ans);
helper(root.right, ans);
}
}

中序遍历

中序遍历: 左-根-右, p.val 处理要在 p.left 和 p.right 处理之中

迭代解法

以下说根节点都是广义上的根节点

class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
TreeNode p = root;
List<Integer> ans = new LinkedList<>();
Stack<TreeNode> stack = new Stack<>(); while (!stack.isEmpty() || p != null) {
if (p != null) { // 先处理根节点的右子树
stack.push(p);
p = p.left;
} else { // 该分支含义为根节点的左子树已经搞定
p = stack.pop();
ans.add(p.val); // 处理根节点
p = p.right; // 处理根节点的右子树
}
}
return ans;
}
}

递归解法

class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
TreeNode p = root;
List<Integer> ans = new LinkedList<>();
helper(p, ans);
return ans;
} private void helper(TreeNode root, List<Integer> ans) {
if (root == null) return;
helper(root.left, ans);
ans.add(root.val);
helper(root.right, ans);
}
}

后序遍历

后序遍历: (左-右-根), p.val 处理要在 p.left 和 p.right 处理之后

在后序遍历中, 每个节点需要访问 2 次, 这意味着遍历完左子树之后要访问当前节点, 遍历完右子树后还需要访问当前节点, 但是 \(\color{red}{只有在第二次访问才会处理当前节点}\). 问题就是如何区分两次访问, 是遍历完左子树返回还是遍历完右子树返回?

解决方法: 当栈中出栈一个元素, 检查这个元素是否与栈顶元素右节点相同, 如果是, 则说明完成了左右子树遍历. 此时只需将栈顶元素出栈并输出该节点数据就好.

迭代解法

解法 1

左右根 ==> 根右左 ->stack-> 左右根

/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<int> element;
TreeNode* p = root;
stack<TreeNode*> st;
vector<int> ans; // 使用循环代替递归,while 循环中要用到 栈是否为空的条件
while (p || !st.empty()) {
// 先处理根节点, 在处理右节点, 在处理左节点, 用的是栈的结构, 所以变成 左-右-根
if (p) {
st.push(p);
// 处理根节点
element.push(p->val);
// 处理根节点右节点
p = p->right;
} else {
p = st.top()->left;
st.pop();
}
}
while (!element.empty()) {
ans.push_back(element.top());
element.pop();
}
return ans;
}
};

解法2

class Solution {
public List<Integer> postorderTraversal(TreeNode root) { TreeNode p = root;
List<Integer> ans = new LinkedList<>();
Stack<TreeNode> stack = new Stack<>(); while (!stack.isEmpty() || p != null) {
if (p != null) {
stack.push(p);
p = p.left; // 处理根节点左节点
} else {
// 当前栈顶元素左子树处理完成, 然后处理右子树
// 当右子树为空的时候, 需要判断是不是 如下结构
/*
/ \
/ \
\
*/ \
if (stack.peek().right == null) {
// 如果左右节点皆为空, 处理根节点
p = stack.pop();
ans.add(p.val); // 处理完根节点之后看看根节点的位置, 看它是在父节点的左节点还是右节点位置
// 如果是右节点的位置, 那么就一直回溯(由于左节点已经处理完, 右节点也处理完,
// 只需处理根), 直到栈为空或者节点为父节点的左子树.
while (!stack.isEmpty() && p == stack.peek().right) {
p = stack.pop();
ans.add(p.val);
}
}
if (!stack.isEmpty()) { // 后续遍历, 栈为空, 则遍历结束
p = stack.peek().right;
} else {
break;
}
}
}
return ans;
}
}

递归解法


class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> ans = new LinkedList<>();
helper(root, ans);
return ans;
}
public void helper(TreeNode root, List<Integer> ans) {
// 函数返回值为 void, return 就好, 使用 return null 是错误的
if (root == null) return;
helper(root.left, ans);
helper(root.right, ans);
ans.add(root.val);
}
}

层次遍历

class Solution {
public List<List<Integer>> levelOrder(TreeNode root) { TreeNode p = root;
List<List<Integer>> ans = new LinkedList<>();
List<Integer> res = new LinkedList<>();
if (p == null) return ans; Queue<TreeNode> queue = new LinkedList();
queue.add(root);
while (!queue.isEmpty()) {
TreeNode temp = queue.remove();
res.add(temp.val);
if (temp.left != null) queue.add(temp.left);
if (temp.right != null) queue.add(temp.right);
}
ans.add(res); return ans;
}
}

Binary Tree Xorder Traversal的更多相关文章

  1. [LeetCode] Binary Tree Postorder Traversal 二叉树的后序遍历

    Given a binary tree, return the postorder traversal of its nodes' values. For example: Given binary ...

  2. [LeetCode] Binary Tree Preorder Traversal 二叉树的先序遍历

    Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tr ...

  3. [LeetCode] Binary Tree Inorder Traversal 二叉树的中序遍历

    Given a binary tree, return the inorder traversal of its nodes' values. For example:Given binary tre ...

  4. 【LeetCode】Binary Tree Preorder Traversal

    Binary Tree Preorder Traversal Given a binary tree, return the preorder traversal of its nodes' valu ...

  5. LintCode Binary Tree Inorder Traversal

    Binary Tree Inorder Traversal Given a binary tree, return the inorder traversal of its nodes' values ...

  6. 12. Binary Tree Postorder Traversal && Binary Tree Preorder Traversal

    详见:剑指 Offer 题目汇总索引:第6题 Binary Tree Postorder Traversal            Given a binary tree, return the po ...

  7. 37. Binary Tree Zigzag Level Order Traversal && Binary Tree Inorder Traversal

    Binary Tree Zigzag Level Order Traversal Given a binary tree, return the zigzag level order traversa ...

  8. 3月3日(4) Binary Tree Inorder Traversal

    原题: Binary Tree Inorder Traversal 和 3月3日(2) Binary Tree Preorder Traversal 类似,只不过变成中序遍历,把前序遍历的代码拿出来, ...

  9. 3月3日(3) Binary Tree Preorder Traversal

    原题 Binary Tree Preorder Traversal 没什么好说的... 二叉树的前序遍历,当然如果我一样忘记了什么是前序遍历的..  啊啊.. 总之,前序.中序.后序,是按照根的位置来 ...

随机推荐

  1. 【NOIP2016】换教室(动态规划)

    题目戳我 题解 其实感觉16年的难度不是很大???? 这道题去年考场上DP都想出来了... 只是因为不会数学期望...然后GG.... 这道题目只要把数学期望搞出来就可以啦 设f[i][j][0/1] ...

  2. USACO08MAR Land Acquisition

    斜率优化 # include <stdio.h> # include <stdlib.h> # include <iostream> # include <s ...

  3. css块级元素和内联元素

    左边的是块级元素block,右边内联inline. 修改方法 display: inline;

  4. wireshark抓包看ECN

    由于实验需要,要统计ECN信息.为了验证拓扑中是否真的有ECN信息,用了wireshark进行抓包查看. 网上找到的相关有用资料有:http://blog.csdn.net/u011414200/ar ...

  5. java web需要好好掌握的一些东西

    这是一些需要好好的复习的东西 本来存了个文档  怕整丢了  就在这里保存一下 java 基础 重点关注集合 如list hashmap等使用(有时间多看看hashmap的实现原理  问的比较多)多线程 ...

  6. 微信小程序基于腾讯云对象存储的图片上传

    在使用腾讯云对象存储之前,公司一直使用的是传统的FTP的上传模式,而随着用户量的不断增加,FTP所暴露出来的问题也越来越多,1.传输效率低,上传速度慢.2.时常有上传其他文件来攻击服务器,安全上得不到 ...

  7. PHP的日志记录-错误与异常记录

    PHP的日志记录-错误与异常记录 提到 Nginx + PHP 服务的错误日志,我们通常能想到的有 Nginx 的 access 日志.error 日志以及 PHP 的 error 日志.虽然看起来是 ...

  8. ASP.NET MVC上传图片的奇怪问题

    本文来源于博客园-钱智慧,转载请注明出处 表现:客户说就华为的手机有问题,而且是在QQ里打开有问题,如果在手机的浏览器上,则可以正常上传图片. 有问题的代码如下: private ResultMode ...

  9. 笔记:Spring Cloud Feign 其他配置

    请求压缩 Spring Cloud Feign 支持对请求与响应进行GZIP压缩,以减少通信过程中的性能损耗,我们只需要通过下面二个参数设置,就能开启请求与响应的压缩功能,yml配置格式如下: fei ...

  10. Webpack执行命令参数详解

    一.概述前面的章节我们讲解了webpack的安装.webpack.config.js的 基本配置.webpack执行命名以及require方法的使用,不 知道大家有没有发现,当我们每次修改或者新增一个 ...