【LeetCode题解】二叉树的遍历
我准备开始一个新系列【LeetCode题解】,用来记录刷题,顺便复习一下数据结构与算法。
1. 二叉树
二叉树(binary tree)是一种极为普遍的数据结构,树的每一个节点最多只有两个节点——左孩子结点与右孩子结点。C实现的二叉树:
struct TreeNode {
int val;
struct TreeNode *left; // left child
struct TreeNode *right; // right child
};
DFS
DFS的思想非常朴素:根据结点的连接关系,依次访问每一个节点,直至遍历完整棵树。根据根节点的访问次序的不同——前、中、后,可分为先序、中序、后序遍历。先序遍历是指先访问根节点,再依次访问左孩子节点、右孩子节点。下图给出一个二叉树的先序、中序、后序遍历示例:

递归实现:递归调用,打印根节点。C实现如下:
// preorder binary tree traversal
void preorder(struct TreeNode *root) {
if(root) {
printf("%d", root->val);
preorder(root -> left);
preorder(root -> right);
}
}
void inorder(struct TreeNode *root) {
if(root) {
inorder(root -> left);
printf("%d", root->val);
inorder(root -> right);
}
}
void postorder(struct TreeNode *root) {
if(root) {
postorder(root -> left);
postorder(root -> right);
printf("%d", root->val);
}
}
任何递归实现的程序都可以改用栈实现。比如,先序遍历时用栈维护待访问结点;先将根结点入栈,再将右孩子结点入栈、左孩子结点入栈。Java实现如下:
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
result.add(node.val);
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
}
return result;
}
中序遍历,栈维护已访问结点。一个结点只有当其左孩子结点已访问时,才能出栈,然后入栈右孩子结点;否则,则入栈左孩子结点。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.peek();
if (node.left != null) {
stack.push(node.left);
node.left = null; // its left child has been visited
} else {
result.add(node.val);
stack.pop();
if (node.right != null)
stack.push(node.right);
}
}
return result;
}
后序遍历,栈也维护已访问结点。一个结点只有当其左右孩子结点均已访问时,才能出栈;否则,则依次入栈右孩子结点、左孩子结点。
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.peek();
if (node.left == null && node.right == null) {
result.add(node.val);
stack.pop();
} else {
if (node.right != null) {
stack.push(node.right);
node.right = null; // its right child has been visited
}
if (node.left != null) {
stack.push(node.left);
node.left = null; // its left child has been visited
}
}
}
return result;
}
层次遍历
层次遍历为二叉树的BFS,是指按照结点的层级依次从左至右访问。实现层序遍历需要借助于队列,用以维护待访问的结点。
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new LinkedList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> levelList = new LinkedList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
levelList.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(levelList);
}
return result;
}
2. 题解
| LeetCode题目 | 归类 |
|---|---|
| 100. Same Tree | |
| 101. Symmetric Tree | |
| 104. Maximum Depth of Binary Tree | |
| 111. Minimum Depth of Binary Tree | |
| 110. Balanced Binary Tree | |
| 112. Path Sum | |
| 113. Path Sum II | |
| 129. Sum Root to Leaf Numbers | |
| 144. Binary Tree Preorder Traversal | 先序 |
| 94. Binary Tree Inorder Traversal | 中序 |
| 145. Binary Tree Postorder Traversal | 后序 |
| 102. Binary Tree Level Order Traversal | 层次 |
| 107. Binary Tree Level Order Traversal II | 层次 |
| 103. Binary Tree Zigzag Level Order Traversal | 层次 |
| 114. Flatten Binary Tree to Linked List |
100. Same Tree
判断两棵树是否一样,递归解决:
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) return true;
if (p == null || q == null) return false;
return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
101. Symmetric Tree
同上一题较为类似,判断一棵树是否中心对称:
public boolean isSymmetric(TreeNode root) {
return root == null || helper(root.left, root.right);
}
private boolean helper(TreeNode p, TreeNode q) {
if (p == null && q == null) return true;
if (p == null || q == null) return false;
return p.val == q.val && helper(p.left, q.right) && helper(p.right, q.left);
}
104. Maximum Depth of Binary Tree
二叉树的最大深度,递归实现之:
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
111. Minimum Depth of Binary Tree
二叉树的最小深度,解决思路与上类似,不过有个special case——根不能视作为叶子节点:
public int minDepth(TreeNode root) {
if (root == null) return 0;
if (root.left == null || root.right == null)
return 1 + Math.max(minDepth(root.left), minDepth(root.right));
return 1 + Math.min(minDepth(root.left), minDepth(root.right));
}
110. Balanced Binary Tree
判断一棵树是否平衡,即每一个节点的左右子树的深度不大于1,正好可用到上面求树深度的代码:
public boolean isBalanced(TreeNode root) {
return root == null || Math.abs(maxDepth(root.left) - maxDepth(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
}
112. Path Sum
判断root-to-leaf路径的结点之和是否存在,递归实现之:
public boolean hasPathSum(TreeNode root, int sum) {
if (root == null) return false;
if (root.val == sum && root.left == null && root.right == null) return true; // root is leaf node
return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}
113. Path Sum II
上一个问题的变种,要将所有满足sum的情况保存下来;解决办法采用两个list存储中间结果,要注意list适当remove:
public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> result = new LinkedList<>();
List<Integer> pathList = new LinkedList<>(); // store a path which meets required sum
helper(root, sum, result, pathList);
return result;
}
private void helper(TreeNode root, int sum, List<List<Integer>> result, List<Integer> pathList) {
if (root != null) {
pathList.add(root.val);
if (root.val == sum && root.left == null && root.right == null) {
result.add(new LinkedList<>(pathList)); // deep copy
} else {
helper(root.left, sum - root.val, result, pathList);
helper(root.right, sum - root.val, result, pathList);
}
pathList.remove(pathList.size() - 1); // remove the last element
}
}
129. Sum Root to Leaf Numbers
二叉树的root-to-leaf路径表示一个十进制数,求所有路径所代表的数之和,递归实现:
public int sumNumbers(TreeNode root) {
return helper(root, 0);
}
private int helper(TreeNode root, int sum) {
if (root == null) return 0; // special case
if (root.left == null && root.right == null) return sum * 10 + root.val;
return helper(root.left, sum * 10 + root.val) + helper(root.right, sum * 10 + root.val);
}
144. Binary Tree Preorder Traversal
先序遍历,参看前面的栈实现。
94. Binary Tree Inorder Traversal
中序遍历,同上。
145. Binary Tree Postorder Traversal
后序遍历,同上。
102. Binary Tree Level Order Traversal
层次遍历,同上。
107. Binary Tree Level Order Traversal II
层次遍历变种,不同的是从bottom开始往上遍历;只需在层次遍历的代码中修改从头入result链表而不是从尾入:
result.addFirst(levelList);
103. Binary Tree Zigzag Level Order Traversa
“之”字形层次遍历,在层次遍历的基础上,将层级分为奇数、偶数,分别按顺序、逆序出队。
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> result = new LinkedList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int level = 1;
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> levelList = new LinkedList<>();
while (levelSize-- > 0) { // to deal with level nodes
TreeNode node = queue.poll();
levelList.add(node.val);
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
if (level % 2 == 0) // be stack order when level is even
Collections.reverse(levelList);
result.add(levelList);
level++;
}
return result;
}
114. Flatten Binary Tree to Linked List
将二叉树打散成长链条树,从递归的角度来看,可分解为三个步骤:打散左子树,打散右子树,然后将右子树拼接到左子树尾部。
public void flatten(TreeNode root) {
helper(root, null);
}
// flatten sub-tree, return its root node
private TreeNode helper(TreeNode root, TreeNode last) {
if (root == null) return last;
root.right = helper(root.left, helper(root.right, last));
root.left = null;
return root;
}
【LeetCode题解】二叉树的遍历的更多相关文章
- 【Leetcode】二叉树层遍历算法
需求: 以层遍历一棵二叉树,二叉树的结点结构如下 struct tree_node{ struct tree_node *lc; struct tree_node *rc; int data; }; ...
- [Leetcode 144]二叉树前序遍历Binary Tree Preorder Traversal
[题目] Given a binary tree, return the preordertraversal of its nodes' values. Example: Input: [1,null ...
- 【LeetCode 144_二叉树_遍历】Binary Tree Preorder Traversal
解法一:非递归 vector<int> preorderTraversal(TreeNode* root) { vector<int> res; if (root == NUL ...
- 【LeetCode 100_二叉树_遍历】Same Tree
解法一:递归 bool isSameTree(TreeNode* p, TreeNode* q) { if (p == NULL && q == NULL) return true; ...
- 【LeetCode 104_二叉树_遍历】Maximum Depth of Binary Tree
解法一:递归 int maxDepth(TreeNode* root) { if (root == NULL) ; int max_left_Depth = maxDepth(root->lef ...
- 【LeetCode 110_二叉树_遍历】Balanced Binary Tree
解法一:From top to bottom int treeHeight(TreeNode *T) { if (T == NULL) ; ; } bool isBalanced(TreeNode* ...
- 【LeetCode 111_二叉树_遍历】Minimum Depth of Binary Tree
解法一:递归 int minDepth(TreeNode* root) { if (root == NULL) ; if (root->left == NULL) { ; } else if ( ...
- 【LeetCode题解】94_二叉树的中序遍历
目录 [LeetCode题解]94_二叉树的中序遍历 描述 方法一:递归 Java 代码 Python代码 方法二:非递归 Java 代码 Python 代码 [LeetCode题解]94_二叉树的中 ...
- 【LeetCode题解】144_二叉树的前序遍历
目录 [LeetCode题解]144_二叉树的前序遍历 描述 方法一:递归 Java 代码 Python 代码 方法二:非递归(使用栈) Java 代码 Python 代码 [LeetCode题解]1 ...
随机推荐
- nginx 红黑树详解
1 介绍 这部分终于整理完了,太耗时间了,留下来备忘吧! 之前看STL源码时,只是研究了红黑树的插入部分.在stl源码剖析的书中,也没有涉及到删除操作的分析,这次对删除操作也进行了详细的研究, 并且还 ...
- 深度学习 for java http://deeplearning4j.org/
http://deeplearning4j.org/ http://deeplearning4j.org/zh-neuralnet-overview
- ecos的app机制
底层->支持层->业务 底层base 数据库访问 service管理 app管理 kvstore存储 kvcache缓存 支持层 pam登录验证 dbeav数据库扩展功能 site前台 d ...
- ios 中在容器中移除单个控件的两个方法Subview
我们知道[parentView removeFromSuperView]; 会把全部的view都移除.以下我们可以通过给subView设一个tag,然后遍历所有的subView,找到目标subVie ...
- 无源RS232转RS485(转)
源:无源RS232转RS485 RS232 -485转换器主要包括了电源.232电平转换.485电路三部分.本电路的232电平转换电路采用了NIH232或者也可以直接使用MAX232集成电路,485 ...
- linux之scp
传输文件夹 scp -r -P 目标端口号 文件夹名 目标用户名@目标服务器地址:目标存放地址 传输文件夹 scp -P 目标端口号 文件名 目标用户名@目标服务器地址:目标存放地址
- STM8建立IAR工程
STM8是意法半导体公司出的增强型八位单片机,性能比51单片机强大,而且价格便宜,在商业应用中很受欢迎 在STM8的开发工程中主要有两种开发工具链.第一是使用IAR开发环境,第二十使用cosmic+s ...
- 基于libUSB的USB设备固件更新程序(下载数据)(转)
源:基于libUSB的USB设备固件更新程序(下载数据) 本文紧接上一篇日志:基于libUSB-Win32的USB设备固件更新程序(前言),相关背景以及起因等,此处不再赘述,如感兴趣请移步. libU ...
- Makefile — 基础
参考: 跟我一起写 Makefile GNU make <GNU+Make项目管理(第三版)> 1.Makefile用途 使用GNU Make工具来管理程序是每个Linux工程师必须掌握的 ...
- 4、安卓数据存储——sqlite
朋友圈里的每一个消息体里面的数据,当下拉刷新从服务器下载数据包后,存入sqlite:用户名.图片url.点赞.评论等等.上拉加载的时候,从数据库里取出最近的5条数据加载到朋友圈上. Android通过 ...