Binary Tree Xorder Traversal
二叉树的前序, 中序, 后序, 层序遍历方法
* 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的更多相关文章
- [LeetCode] Binary Tree Postorder Traversal 二叉树的后序遍历
Given a binary tree, return the postorder traversal of its nodes' values. For example: Given binary ...
- [LeetCode] Binary Tree Preorder Traversal 二叉树的先序遍历
Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tr ...
- [LeetCode] Binary Tree Inorder Traversal 二叉树的中序遍历
Given a binary tree, return the inorder traversal of its nodes' values. For example:Given binary tre ...
- 【LeetCode】Binary Tree Preorder Traversal
Binary Tree Preorder Traversal Given a binary tree, return the preorder traversal of its nodes' valu ...
- LintCode Binary Tree Inorder Traversal
Binary Tree Inorder Traversal Given a binary tree, return the inorder traversal of its nodes' values ...
- 12. Binary Tree Postorder Traversal && Binary Tree Preorder Traversal
详见:剑指 Offer 题目汇总索引:第6题 Binary Tree Postorder Traversal Given a binary tree, return the po ...
- 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 ...
- 3月3日(4) Binary Tree Inorder Traversal
原题: Binary Tree Inorder Traversal 和 3月3日(2) Binary Tree Preorder Traversal 类似,只不过变成中序遍历,把前序遍历的代码拿出来, ...
- 3月3日(3) Binary Tree Preorder Traversal
原题 Binary Tree Preorder Traversal 没什么好说的... 二叉树的前序遍历,当然如果我一样忘记了什么是前序遍历的.. 啊啊.. 总之,前序.中序.后序,是按照根的位置来 ...
随机推荐
- Bzoj2337:[HNOI2011]XOR和路径
题面 bzoj Sol 设\(f[i]\)表示\(i到n\)的路径权值某一位为\(1\)的期望 枚举每一位,高斯消元即可 不要问我为什么是\(i\ - \ n\)而不可以是\(1\ - \ i\) # ...
- Linux系统默认权限之umask
默认情况下,目录权限值为755, 普通文件权限值为644, 那么这个值是由谁规定的,追究其原因是 umask [root@adminx]# vim /etc/profile 1.假设umask值为:0 ...
- eclipse热部署web项目
一.选中JavaEE视图 因为在普通的Java视图下,窗口下方没有server选项卡 二.双击Tomcat 注意:可能很多人当然包括我一开始的时候,都是喜欢右键Tomcat然后Add and remo ...
- 关于win8/win8.1系统不能调节亮度的解决办法
有时候我们重装了win8系统之后开始可以调节亮度,但是再重新安装了显卡驱动之后发现不能调节亮度了,解决办法就是添加一项注册表. 下载地址:http://7xnarc.com1.z0.glb.cloud ...
- 设计模式——建造者模式/生成器模式(C++实现)
#include <iostream> #include <string> using namespace std; class STProduct { public: voi ...
- 【.NetCore】基于jenkins以及gitlab的持续编译及发布
前沿 其实本来是想把标题叫做持续集成的,只是后来看看研究出的内容,就只有发布这一个动作,自动化测试等内容也未涉及到,所以改名叫持续编译及发布应该更加贴切吧? 问题背景 其实目前我们传统方式上的发布方式 ...
- Linux压缩命令总结
2018-02-28 10:43:18 linux压缩和解压缩命令大全 tar命令:tar本身仅是一个打包的命令,不具有压缩的功能.打包后源文件仍然存在,具有将多个文件归档成一个文件的功能[root ...
- canvas小球
小球碰撞效果是采用面向对象的方式写的,在小球的构造器里包含了小球的属性值,大小,移动速度,半径大小以及颜色. 在小球的原型方法里,添加了小球运动的方法,当小球碰撞到屏幕边界的时候进行反弹. 小球是 ...
- [译]Android view 测量布局和绘制的流程
原文链接 创造优秀的用户体验是我们开发者的主要目标之一.为此, 我们首先要了解系统是如何工作的, 这样我们才可以更好的与系统配合, 从它的优点中获益, 规避它的缺陷. 之前关于Android渲染过程的 ...
- npm包使用语义化版本号
npm 采用语义版本管理软件包.所谓语义版本,就是指版本号为a.b.c的形式,其中a是大版本号,b是小版本号,c是补丁号. 一个软件发布的时候,默认就是1.0.0版.如果以后发布补丁,就增加最后一位数 ...