层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。
需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。
使用队列实现二叉树广度优先遍历,动画如
/**
* 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 {
public List<List<Integer>> resList = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
DFS(root,0);
return resList;
}//递归
public void DFS(TreeNode root,Integer deep){
if(root==null)return; //终止条件
deep++;
if(resList.size()<deep){
//当层级增加时,list的Item也增加,利用list的索引值进行层级界定
// 每一层一个item储存对应层的节点元素
List<Integer> item = new ArrayList<>();
resList.add(item);
}
resList.get(deep-1).add(root.val);//根据对应的层数储存节点
DFS(root.left,deep);
DFS(root.right,deep);
}
}
先建立【储存每一层的result数组】
【建立队列】保存每一层的树节点,方便取值。
将root放入队列中,队列不为空时进行循环。
建立每一层的item数组,储存数值
题解
代码:
注意: queue的size一定要提前固定,因为queue的大小是动态变化的
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode>queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root!=null){
queue.add(root);//放入根节点
}
while(!queue.isEmpty()){
// 新建一个临时列表 tmp ,用于存储当前层打印结果
List<Integer> tmp=new ArrayList<>();
// 队列放入每一层节点的个数
// 循环次数为当前层节点数
// // 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
int len=queue.size();
for(int i=len;i>0;i--){
TreeNode node=queue.poll();
tmp.add(node.val);
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
res.add(tmp);
}
return res;
}
}
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
List<Integer> tmp = new ArrayList<>();
int len = queue.size();
for(int i=len;i>0;i--){
TreeNode node = queue.poll();
tmp.add(node.val);
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
res.add(tmp);
}
// return res;
List<List<Integer>> res1 = new ArrayList<>();
for(int i=res.size()-1;i>=0;i--){
res1.add(res.get(i));
}
return res1;
}
}
层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中,随后返回result就可以了。
/**
* 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 {
public List<Integer> rightSideView(TreeNode root) {
// List<List<Integer>> res = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
if(root!=null){
queue.add(root);
}
List<Integer>list=new ArrayList<>();//储存最终结果
while(!queue.isEmpty()){
int len=queue.size();
// List<Integer>temp=new ArrayList<>();
for(int i=len;i>0;i--){
TreeNode node=queue.poll();
if(i==1){//浏览到数组的最后一个,储存
list.add(node.val);
}
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
}
return list;
}
}
本题就是层序遍历的时候把一层求个总和在取一个均值。
/**
* 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 {
public List<Double> averageOfLevels(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<Double> list = new ArrayList<>();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
// List<Integer> list = new ArrayList<>();
int len = queue.size();
double temp=0;
for(int i=len;i>0;i--){
TreeNode node = queue.poll();
// list.add(node.val);
temp+=node.val;
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
list.add(temp/len);
}
return list;
}
}
注意:
// List<Node> children = node.children;
// if(children==null||children.size()==0){
// continue;
// }
// for(Node child:children){
或者 两种方法
for(Node child:node.children){
if(child!=null){
queue.add(child);
}
}
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> res=new ArrayList<>();
Queue<Node> queue =new LinkedList<>();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
List<Integer> item = new ArrayList<>();
int len=queue.size();
for(int i=0;i<len;i++){
Node node=queue.poll();
item.add(node.val);
// List<Node> children = node.children;
// if(children==null||children.size()==0){
// continue;
// }
for(Node child:node.children){
if(child!=null){
queue.add(child);
}
}
}
res.add(item);
}
return res;
}
}
/**
* 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 {
public List<Integer> largestValues(TreeNode root) {
List<Integer> res = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
int len=queue.size();
int num=Integer.MIN_VALUE;
for(int i=0;i<len;i++){
TreeNode node = queue.poll();
num=Math.max(num,node.val);
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
res.add(num);
}
return res;
}
}
本题依然是层序遍历,只不过在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了
class Solution {
public Node connect(Node root) {
Queue<Node>queue = new LinkedList<>();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
int len=queue.size();
Node pre=null;
Node node =null;
for(int i=0;i<len;i++){
if(i==0){//记录头部节点
pre=queue.poll();
node=pre;
}else{//让头部节点指向本节点
node=queue.poll();
pre.next=node;
pre=pre.next;
}
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
pre.next=null;
}
return root;
}
}
class Solution {
public int maxDepth(TreeNode root) {
Queue<TreeNode>queue = new LinkedList<>();
int deep=0;
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
deep++;
int len=queue.size();
for(int i=0;i<len;i++){
TreeNode node = queue.poll();
if(node.left!=null)queue.add(node.left);
if(node.right!=null)queue.add(node.right);
}
}
return deep;
}
}
相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。
需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点
class Solution {
public int minDepth(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
int deep=0;
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
int len=queue.size();
deep++;
for(int i=0;i<len;i++){
TreeNode node =queue.poll();
if(node.left!=null)queue.add(node.left);
if(node.right!=null)queue.add(node.right);
if(node.left==null&&node.right==null){
return deep;
}
}
}
return deep;
}
}
注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果
这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了
那么层序遍历可以不可以呢?依然可以的!只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!
把每一个节点的左右孩子都进行翻转
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null){
return root;
}
invertTree(root.left);//左
invertTree(root.right);//右
// 交换 //中
TreeNode temp=null;
temp=root.left;
root.left=root.right;
root.right=temp;
return root;
}
}
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null){
return root;
}
// 交换 //中
TreeNode temp=null;
temp=root.left;
root.left=root.right;
root.right=temp;
return root;
invertTree(root.left); //左
invertTree(root.right); //右
}
}
在添加左右节点前,进行左右节点的交换。
class Solution {
public TreeNode invertTree(TreeNode root) {
// if(root==null){
// return root;
// }
Queue<TreeNode> queue=new LinkedList<>();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
int len = queue.size();
for(int i=0;i<len;i++){
TreeNode node=queue.poll();
TreeNode tmp=null;
// 交换
tmp=node.right;
node.right=node.left;
node.left = tmp;
// 放入左右节点
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
}
return root;
}
}
首先想清楚,判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点!
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。
返回值自然是bool类型。
代码如下:
bool compare(TreeNode* left, TreeNode* right)
要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。
节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点)
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:
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; // 注意这里我没有使用else
注意上面最后一种情况,我没有使用else,而是else if, 因为我们把以上情况都排除之后,剩下的就是 左右节点都不为空,且数值相同的情况。
此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中(逻辑处理)
return isSame;
class Solution {
public boolean isSymmetric(TreeNode root) {
return Compare(root.left,root.right);
}
public boolean Compare(TreeNode left,TreeNode right){
if(left==null&&right==null){
return true;
}
// if(left==null&&right!=null){
// return false;
// }
// if(left!=null&&right==null){
// return false;
// }
// if(left.val!=right.val){
// return false;
// }
if (left==null||right==null||left.val != right.val) {
return false;
}
// 比较外侧
boolean outside = Compare(left.left,right.right);
// 比较内侧
boolean inside = Compare(left.right,right.left);
return outside&&inside;
}
}