层序遍历
如何控制左右孩子入队很简单,有点像前序遍历入栈的判断,但是,如何返回结果集,就有点难度了,结果集是根据层数来保存的,所以,需要一个控制层数的方法,这里即,用一个变量len来记录每一层的节点数,然后循环加入每一层节点的孩子节点,加入完某个节点的孩子节点之后,让他出队,并让这一层的len自减和让这个出队的节点的值加入到这一层的集合中去,这样就完成了按层加入集合的实现。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
// 放在外面,为了与递归法参数一致,要封装方法
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null) {
return result;
}
BDFOrder(root);
return result;
}
public void BDFOrder(TreeNode node) {
Queue<TreeNode> queue = new LinkedList<>();
List<Integer> level = null;
int len = 0; // 用来记录每一层的节点数,一遍leve存入每一层的数据大小,根据每一层的节点数,循环入队起孩子
TreeNode temp = null;
queue.add(node);
while(!queue.isEmpty()) {
level = new ArrayList<>();
len = queue.size();
while(len > 0) { // 例如,此时队列中只有一个,那么将它的左右孩子入队(入队后队列中将有两个,这个交给下一个level处理),当有两个时,则循环把每一个的左右孩子入队。。。
temp = queue.poll();
if(temp.left != null) {
queue.add(temp.left);
}
if(temp.right != null) {
queue.add(temp.right);
}
level.add(temp.val);
len--; // 把孩子节点入队后别忘了控制这一层的循环变量
}
result.add(level);
}
}
}
下面是9道层序遍历题:
107.二叉树的层次遍历II
思路就是正常层次遍历,然后反转一下result集合就行。
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if(root == null) {
return result;
}
BOFOrder(root,result); // 先正常层序遍历,之后反转一下集合,即是答案
Collections.reverse(result);
return result;
}
public void BOFOrder(TreeNode root, List<List<Integer>> result) {
Queue<TreeNode> queue = new LinkedList<>();
List<Integer> level = null;
TreeNode temp = null;
int len = 0;
queue.add(root);
while(!queue.isEmpty()) {
len = queue.size();
level = new ArrayList<>();
while(len > 0) {
temp = queue.poll();
level.add(temp.val);
if(temp.left != null) {
queue.add(temp.left);
}
if(temp.right != null) {
queue.add(temp.right);
}
len--;
}
result.add(level);
}
}
199. 二叉树的右视图
这道题第一眼想,直接一路向右遍历就完事了呗,结果一做,发现事情并不简单,题目要求的是“右视图”,不是“右子树”,单单一路向右是不行的,因为当右边没有节点而左边有节点时,左孩子也算是右视图了,这样就不能用简单的遍历了,必须一层一层的看,并且从左往右遍历,遍历到最右边的即是右视图,所以还是层序遍历的思路,开干!
public List<Integer> rightSideView(TreeNode root) {
List<Integer> result = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
int len = 0;
TreeNode temp = null;
if(root == null) {
return result;
}
queue.add(root);
// 下面就是正常的层序遍历
while(!queue.isEmpty()) {
len = queue.size();
while(len > 0) {
temp = queue.poll();
if(temp.left != null) {
queue.add(temp.left);
}
if(temp.right !=null) {
queue.add(temp.right);
}
if(len == 1) { // 当遍历到这一层的最后一个(因为是从左往右遍历的,所以最右边即是最后一个,也就是右视图的节点)
result.add(temp.val);
}
len--;
}
}
return result;
}
637.二叉树的层平均值
还是层次遍历啦,就是简单求和然后求平均。
public List<Double> averageOfLevels(TreeNode root) {
List<Double> result = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
int len = 0; // 记录每一层节点个数,控制孩子节点入队
double sum = 0; // 每一层的和
double fenmu = 0; // 分母
TreeNode temp = null;
if(root == null) {
return result;
}
queue.add(root);
while(!queue.isEmpty()) {
len = queue.size();
sum = 0;
fenmu = 1.0 / len;
while(len > 0) {
temp = queue.poll();
if(temp.left != null) {
queue.add(temp.left);
}
if(temp.right !=null) {
queue.add(temp.right);
}
sum +=temp.val;
len--;
}
result.add(sum*fenmu);
}
return result;
}
429. N 叉树的层序遍历
这题就是把孩子节点看成集合就行,当要把孩子节点入队的时候,把孩子节点这个集合遍历一遍,然后入队即可,题目中意思null只是一个分割,不用特殊处理,看题图理解就行,下面直接上官方代码了。
class Solution {
public List<List<Integer>> levelOrder(Node root) {
if (root == null) {
return new ArrayList<List<Integer>>();
}
List<List<Integer>> ans = new ArrayList<List<Integer>>();
Queue<Node> queue = new ArrayDeque<Node>();
queue.offer(root);
while (!queue.isEmpty()) {
int cnt = queue.size();
List<Integer> level = new ArrayList<Integer>();
for (int i = 0; i < cnt; ++i) {
Node cur = queue.poll();
level.add(cur.val);
for (Node child : cur.children) {
queue.offer(child);
}
}
ans.add(level);
}
return ans;
}
}
还有5道题,都是差不多的代码了,就先不做了。
226.翻转二叉树
1. 层序遍历法(广度优先遍历法<BFS>)
public TreeNode invertTree(TreeNode root) {
// 1. BFS
if(root == null) {
return null;
}
TreeNode p = root;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(p);
while(!queue.isEmpty()) { // 直接开始层序遍历
int len = queue.size();
while(len > 0) {
TreeNode temp = queue.poll();
// 孩子节点入队之前,互相交换一下位置
TreeNode q = temp.left;
temp.left = temp.right;
temp.right = q;
if(temp.left != null) {
queue.add(temp.left);
}
if(temp.right != null) {
queue.add(temp.right);
}
len--;
}
}
return root;
}
2. 前序遍历法(深度优先遍历法<DFS>)
public TreeNode invertTree(TreeNode root) {
// 2. DFS(递归)法
// 先序遍历
if(root == null){
return null;
}
swpChildrean(root);
invertTree(root.left); // 这个函数就是反转函数,所以就把他写成递归形式就行
invertTree(root.right);
return root;
}
void swpChildrean(TreeNode root){
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
}
101.对称二叉树
1. 递归法:
对递归法还是不是特别懂,找个时间专门突击一下递归法吧,把我对代码的理解放在注释里面了。
public boolean isSymmetric(TreeNode root) {
/** 1. 递归法
递归法其实与之前的递归遍历不一样,只是能用递归去做,
因为每次都是两个两个比较,而且,两个两个比较完了之后,
又需要同样的方式比较他们的左右孩子,所以再递归调用一遍方法
*/
return compare(root.left, root.right);
}
// 递归三部曲
// 1. 参数和返回值:由于需要两个两个比较,那么肯定需要两个参数,也刚好对应两个子树的头节点,由于是比较,而且题目也要求返回boolean,所以是boolean
public boolean compare(TreeNode left, TreeNode right){
// 2. 终止条件:先想想不满足条件的时候,即,当一个为空,一个不为空的时候、两个都不为空但是数值不相等的时候,返回true的时候便是两个都不为空且数值相等的时候
if(left == null && right != null) {
return false;
}else if(left != null && right == null) {
return false;
}else if(left == null && right == null) {
return true;
}else if(left.val != right.val) {
return false;
}
// 注意!!这里是些循环终止条件!当他们值相等的时候,例如示例1的2,不用返回true,等到
// 3. 单层循环逻辑:接下来是要比较左子树的外侧(示例1的左3)和右子树的外侧(示例1的右3)是否相等,发现这不就和比较左右子树根节点思路一样嘛,所以直接递归即可,内侧也同理
boolean isOut = compare(left.left, right.right);
boolean isIn = compare(left.right, right.left);
return isOut && isIn; // 返回判断左右子树的结果,当一边不对称就返回false,所以是&&
}
2. 迭代法:
关键是在理解,两个两个比较,从外向里比较,当遇到空的时候continue,不再让它的左右孩子入队。
public boolean isSymmetric(TreeNode root) {
// 2. 迭代法
if(root == null){
return false;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root.left);
queue.add(root.right);
while(!queue.isEmpty()) {
TreeNode leftNode = queue.poll();
TreeNode rightNode = queue.poll();
if(leftNode == null && rightNode == null) {
continue;
}
// 因为已经判断都为空的情况,下面只需要判断某一个为空,则直接返回false
// 或者都不为空,但是值不相同,也返回false
if(leftNode == null || rightNode == null || (leftNode.val != rightNode.val)) {
return false;
}
queue.add(leftNode.left);
queue.add(rightNode.right);
queue.add(leftNode.right);
queue.add(rightNode.left);
}
return true;
}