层次遍历
使用BFS遍历二叉树时,不需要使用两个队列来存储当前层节点和下一层节点,因为开始遍历的时候,当前层的节点数量就是队列的长度,只需要用这个来控制遍历当前层的节点即可,队列用来存储下一层的节点。
637. 二叉树的层平均值
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组.
示例 1:
输入:
3
/ \
9 20
/ \
15 7
输出: [3, 14.5, 11]
解释:
第0层的平均值是 3, 第1层是 14.5, 第2层是 11. 因此返回 [3, 14.5, 11].
注意:
- 节点值的范围在32位有符号整数范围内。
题解:这道题很明显是一道用BFS来解决的题目,用一个队列来存储下一层的节点,将每层的节点值累加起来,除于个数即可
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> result = new ArrayList<>();
if (root == null) {
return result;
}
//准备一个队列
Queue<TreeNode> queue = new LinkedList<>();
//先将根节点添加进去
((LinkedList<TreeNode>) queue).add(root);
//BFS
while (!queue.isEmpty()) {
//用队列长度来控制每一层的个数
int cnt = queue.size();
double sum = 0;
for (int i = 0; i < cnt; i++) {
TreeNode node = queue.poll();
sum += node.val;
//将下一层节点添加进队列
if (node.left != null) {
((LinkedList<TreeNode>) queue).add(node.left);
}
if (node.right != null) {
((LinkedList<TreeNode>) queue).add(node.right);
}
}
result.add(sum / cnt);
}
return result;
}
}
513. 找树左下角的值(Medium)
给定一个二叉树,在树的最后一行找到最左边的值。
示例 1:
输入:
2
/ \
1 3
输出:
1
示例 2:
输入:
1
/ \
2 3
/ / \
4 5 6
/
7
输出:
7
注意: 您可以假设树(即给定的根节点)不为 NULL。
解法一:这道题也是用BFS解决,使用队列从左至右存储下一层节点,保证每一层都是先遇到左边第一个节点,用变量res来存储这个节点,当遍历完队列时,返回res即可
class Solution {
public int findBottomLeftValue(TreeNode root) {
//准备一个队列
Queue<TreeNode> queue = new LinkedList<>();
//先将根节点添加进去
((LinkedList<TreeNode>) queue).add(root);
int res = 0;
//BFS
while (!queue.isEmpty()) {
//用队列长度来控制每一层的个数
int cnt = queue.size();
for (int i = 0; i < cnt; i++) {
TreeNode node = queue.poll();
//将每一层的左边第一个节点传给res
if (i == 0) {
res = node.val;
}
//将下一层节点添加进队列
if (node.left != null) {
((LinkedList<TreeNode>) queue).add(node.left);
}
if (node.right != null) {
((LinkedList<TreeNode>) queue).add(node.right);
}
}
}
return res;
}
}
解法二:这道题也可以使用“先序遍历”来解答
- 先序遍历的顺序是:根->左->右,每一层的最左节点是首先遍历到的,
- 当遍历到新一行的时候,当前深度肯定比之前的最大深度大,所以我们可以更新最大深度为当前深度,结点值res为当前结点值,这样在遍历到该行其他结点时就不会更新结果res了
class Solution {
public int findBottomLeftValue(TreeNode root) {
int res = root.val;
int maxDepth = 1;
helper(root, 1, maxDepth, res);
return res;
}
private void helper(TreeNode root, int depth, int maxDepth, int res) {
//base case
if (root == null) {
return;
}
//遍历到新一层的左边第一个节点
if (depth > maxDepth) {
maxDepth = depth;
res = root.val;
}
//遍历左右节点
helper(root.left, depth + 1, maxDepth, res);
helper(root.right, depth + 1, maxDepth, res);
}
}
前中后序遍历
层次遍历利用的是BFS,而前中后序利用的是DFS,用递归实现这三种遍历比较简单,而非递归比较难一点
递归实现前中后序遍历
- 先序遍历
public void dfs(TreeNode root) { //base case if (root == null) { return; } //先访问根节点 System.out.println(root.val); //遍历左节点和右节点 dfs(root.left); dfs(root.right); }
- 中序遍历
public void dfs(TreeNode root) { //base case if (root == null) { return; } //先遍历左节点 dfs(root.left); //中间访问根节点 System.out.println(root.val); //再遍历右节点 dfs(root.right); }
- 后续遍历
public void dfs(TreeNode root) { //base case if (root == null) { return; } //先遍历左节点 dfs(root.left); //再遍历右节点 dfs(root.right); //最后访问根节点 System.out.println(root.val); }
非递归实现前中后序遍历
144. 二叉树的前序遍历(Medium)
题解:
- 先序遍历的特点是:对访问的每个节点,先访问根节点,再访问左节点,最后访问右节点。
- 递归实现我们借助的是“栈”,只不过这个“栈”不是我们自己实现的,现在就需要我们自己实现
- 准备一个栈,压入节点的时候要注意“先压右节点”,因为弹出来的时候是相反的顺序
class Solution {
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();
//遇到空节点,跳过
if (node == null) {
continue;
}
result.add(node.val);
//如果有左右节点,就压入
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return result;
}
}
145. 二叉树的后序遍历(Hard)
题解:前序遍历为 root -> left -> right,后序遍历为 left -> right -> root。可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
//利用先序遍历遍历节点
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
if (node == null) continue;
ret.add(node.val);
stack.push(node.left);
stack.push(node.right);
}
//最后将结果反转即可
Collections.reverse(ret);
return ret;
}
}
94. 二叉树的中序遍历(Medium)
题解:中序遍历也需要用到栈,不同的是遍历条件多了个cur != null,因为中序遍历是先一直将左节点压入栈,遍历到为空之后就弹出,加入list,然后往右遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
if (root == null) {
return ret;
}
//准备一个栈
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (!stack.isEmpty() || cur != null) {
//将左节点全部压入(左)
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
//遇到空,弹出节点(根),然后往右走
TreeNode node = stack.pop();
ret.add(node.val);
//右
cur = node.right;
}
return ret;
}
}