代码随想录算法训练营第13天 | 二叉树部分: 层序遍历(10), 226. 翻转二叉树,101. 对称二叉树
二叉树部分
层序遍历
广度遍历:使用队列的方式来实现,当某个节点出队的时候,让它的子节点入队
102. 二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
思路:使用一个变量保存每一层的数量,并且在节点出队的时候,其子节点入队
觉得这个代码没有问题,但是结果不对,感觉可能是由于队列方法或者队列的使用的问题,因为debug的时候哦,并不能满足先进先出的规则
错误原因:ArrayDeque中方法的使用问题:使用 pop() push()方法,它们表示对栈的操作,所以才不能满足先进先出的规则
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
Deque<TreeNode> deque=new LinkedList<>();
if(root==null){
return res;
}
deque.offer(root);//deque.push(root);
while(!deque.isEmpty()){
int size=deque.size();//每一层的大小
List<Integer> list=new ArrayList<>();
while(size>0){
TreeNode node=deque.poll();
list.add(node.val);
size--;
if(node.left!=null){
deque.offer(node.left);//deque.push(node.left);//对队列进行更新
}
if(node.right!=null){
deque.offer(node.right);//错误的 deque.push(node.right);
}
}
res.add(list);
}
return res;
}
补充:LinkedList ArrayDeque的区别:底层数据结构不一样(链表和数组)
Deque中的push poll pop等的区别
Deque双端队列,同样实现了栈的功能
107. 二叉树的层序遍历 II
给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
思路:先按照上面的题做,最后对结果进行翻转
答案:哈哈哈哈哈哈哈哈哈哈哈,就是这个思路,但是直接对res结果集采用reverse()函数出现错误,集合中不支持这个方法,使用循环来解决
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> res=new LinkedList<>();
Deque<TreeNode> deque=new LinkedList<>();
if(root==null){
return res;
}
deque.offer(root);//deque.push(root);
while(!deque.isEmpty()){
int size=deque.size();//每一层的大小
List<Integer> list=new ArrayList<>();
while(size>0){
TreeNode node=deque.poll();
list.add(node.val);
size--;
if(node.left!=null){
deque.offer(node.left);
}
if(node.right!=null){
deque.offer(node.right);
}
}
res.add(list);
}
//这边没写出来,需要注意
List<List<Integer>> resList=new LinkedList<>();
for(int i=res.size()-1;i>=0;i--){
resList.add(res.get(i));
}
return resList;
}
199. 二叉树的右视图
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
思路:还是采用层序遍历,维护一个size表示每一层的大小,当size==1的时候,把这个元素保存在结果集中
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res=new LinkedList<>();
Deque<TreeNode> deque=new ArrayDeque<>();
if(root==null){
return res;
}
deque.offer(root);
while(!deque.isEmpty()){
int size=deque.size();
while(size>0){
if(size==1){
res.add(deque.peek().val);//这边的一个判断比较重要
}
TreeNode node =deque.poll();
size--;
if(node.left!=null){
deque.offer(node.left);
}
if(node.right!=null){
deque.offer(node.right);
}
}
}
return res;
}
637. 二叉树的层平均值
给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。
思路:使用变量保存每一层的元素个数和元素和,这边的10-5 这个知识点不会,补充
public List<Double> averageOfLevels(TreeNode root) {
List<Double> res=new LinkedList<>();
Deque<TreeNode> deque=new ArrayDeque<>();
if(root==null){
return res;
}
deque.offer(root);
while(!deque.isEmpty()){
int size=deque.size();
int sizeOr=size;
double sum=0.0;
while(size>0){
TreeNode node=deque.poll();
size--;
sum +=node.val;
if(size==0){
res.add(sum/sizeOr);//这边存在一点问题,刚开始除以size,一直出现Nan的错误,但是其实此时size=0,所以才会报错,使用一个变量来保存队列容量的原始值就可以了
}
if(node.left!=null){
deque.offer(node.left);
}
if(node.right!=null){
deque.offer(node.right);
}
}
}
return res;
}
429. N 叉树的层序遍历
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
思路:n叉树的定义
public class NTreeNode{
public int val;
public List<NTreeNode> child;//多叉树的子节点的表示,使用列表来表示
public NTreeNode(){};
public NTreeNode(int val){
this.val=val;
}
public NTreeNode(int val, List<NTreeNode> child){
this.val=val;
this.child=child;
}
}
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> res=new LinkedList<>();
if(root==null){
return res;
}
Deque<Node> deque=new ArrayDeque<>();
deque.offer(root);
while(!deque.isEmpty()){
int size=deque.size();
List<Integer> list=new LinkedList<>();
while(size>0){
Node node=deque.poll();
list.add(node.val);
size--;//注意不要忘了
if(node.children!=null){
List<Node> nodeList=node.children;
//使用循环实现子节点的遍历
for(int i=0;i<nodeList.size();i++){
deque.offer(node.children.get(i));
}
}
}
res.add(list);
}
return res;
}
515. 在每个树行中找最大值
给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
使用一个变量保存每层中的最大值
public List<Integer> largestValues(TreeNode root) {
List<Integer> res=new LinkedList<>();
Deque<TreeNode> deque=new ArrayDeque<>();
if(root==null){
return res;
}
deque.offer(root);
while(!deque.isEmpty()){
int size=deque.size();
int max=Integer.MIN_VALUE;//保存每层中的最大值
while(size>0){
TreeNode node =deque.poll();
if(max<node.val){
max=node.val;//最大值的更新
}
size--;
if(node.left!=null){
deque.offer(node.left);
}
if(node.right!=null){
deque.offer(node.right);
}
}
res.add(max);
}
return res;
}
116. 填充每个节点的下一个右侧节点指针
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下,填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
思路:如果size=1的话,那么next=null, 否则next等于待出队的节点
public Node connect(Node root) {
Deque<Node> deque=new ArrayDeque<>();
if(root==null){
return root;
}
deque.offer(root);
while(!deque.isEmpty()){
int size=deque.size();
while(size>0){
Node node=deque.poll();
size--;
if(size==0){
node.next=null;//主要的逻辑在这边,如果此时队列中没有其他的节点,表示是最右边的节点,指向null
}else{
node.next=deque.peek();//否则的话指向队列中的对头元素
}
if(node.left!=null){
deque.offer(node.left);
}
if(node.right!=null){
deque.offer(node.right);
}
}
}
return root;
}
117. 填充每个节点的下一个右侧节点指针 II
给定一个二叉树,填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
思路,这个题和上一个题是一个思路,只不过这边的二叉树结构不确定是完全二叉树,就是普通二叉树,但是对结果是没有影响的,所以采用相同的代码
public Node connect(Node root) {
Deque<Node> deque=new ArrayDeque<>();
if(root==null){
return root;
}
deque.offer(root);
while(!deque.isEmpty()){
int size=deque.size();
while(size>0){
Node node=deque.poll();
size--;
if(size==0){
node.next=null;//主要的逻辑在这边,如果此时队列中没有其他的节点,表示是最右边的节点,指向null
}else{
node.next=deque.peek();//否则的话指向队列中的对头元素
}
if(node.left!=null){
deque.offer(node.left);
}
if(node.right!=null){
deque.offer(node.right);
}
}
}
return root;
}
104. 二叉树的最大深度
给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明: 叶子节点是指没有子节点的节点。
思路:二叉树的最大深度也就是二叉树层序遍历的层数,使用一个变量记录二叉树的层数
public int maxDepth(TreeNode root) {
int level=0;
Deque<TreeNode> deque=new ArrayDeque<>();
if(root==null){
return 0;
}
deque.offer(root);
while(!deque.isEmpty()){
int size=deque.size();
//while循环表示一层的遍历
while(size>0){
TreeNode node=deque.poll();
size--;
if(node.left!=null){
deque.offer(node.left);
}
if(node.right!=null){
deque.offer(node.right);
}
}
level++;
}
return level;
}
111. 二叉树的最小深度
给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。说明:叶子节点是指没有子节点的节点。
思路:层序遍历第一次遇到的叶子节点所在的层数即为二叉树的最小深度
public int minDepth(TreeNode root) {
int level=0;
Deque<TreeNode> deque=new ArrayDeque<>();
if(root==null){
return 0;
}
deque.offer(root);
while(!deque.isEmpty()){
int size=deque.size();
level ++;//这边提前更新深度
while(size>0){
TreeNode node=deque.poll();
size--;
if(node.left==null && node.right==null){
return level;//保证一但遇到叶子节点直接退出,此时的深度已经是更新的状态
}
if(node.left!=null){
deque.offer(node.left);
}
if(node.right!=null){
deque.offer(node.right);
}
}
}
return level;
}
226. 翻转二叉树
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
思路:正常的层序遍历的顺序是从左到右,既然要实现二叉树的翻转,试试先右再左的方法,暂时感觉就是仅仅改变了队列里面的顺序,实际上二叉树的结构并没有发生变化,感觉这个题目不适合使用层序遍历来做
public TreeNode invertTree(TreeNode root) {
Deque<TreeNode> deque=new ArrayDeque<>();
if(root==null){
return root;
}
deque.offer(root);
while(!deque.isEmpty()){
int size=deque.size();
while(size>0){
TreeNode node=deque.poll();
size--;
//这边我以为先右边入队,后左边入队会改变树的左右结构,但是实际上没有
//即只是改变了队列的顺序,实际上对于节点的左右儿子之间的关系并没有实现相应的改变
if(node.right!=null){
deque.offer(node.right);
}
if(node.left!=null){
deque.offer(node.left);
}
}
}
return root;
}
使用递归的方式来实现
public TreeNode invertTree(TreeNode root) {
invertT(root);
return root;
}
public void invertT(TreeNode root){
if(root==null){
return;
}
//左右子树交换
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
invertT(root.left);//左子树
invertT(root.right);//右子树
}
101. 对称二叉树
给你一个二叉树的根节点 root , 检查它是否轴对称。
思路: 使用递归的方法 递归写不出来,层序遍历的话,队列中是对称的, 感觉也实现不出来
答案:感觉这边是对于左外侧和右外侧比较 左内侧和右内测比较有印象,但是自己除了前三个if语句之外想得到的就是left.val=right.val, 但是这种情况肯定不能返回true,然后就想不出来了
public boolean isSymmetric(TreeNode root) {
return compare(root.left, root.right);
}
private boolean compare(TreeNode left, TreeNode right) {
if (left == null && right != null) {
return false;
}
if (left != null && right == null) {
return false;
}
if (left == null && right == null) {
return true;
}
//上面的几种条件都想到了,但是想着如果左节点和右节点值相等的话直接返回true不对,然后就想不出来了
//没想到会是这种实现
if (left.val != right.val) {
return false;
}
// 比较外侧
boolean compareOutside = compare(left.left, right.right);
// 比较内侧
boolean compareInside = compare(left.right, right.left);
return compareOutside && compareInside;
}
总结:对称二叉树里面的递归逻辑没想出来, Deque也实现了栈的功能,注意方法的使用