树的合集(二)->求二叉树的属性
- 对称二叉树
对称二叉树
思路
首先要清楚什么是对称二叉树:以根节点为轴,右子树翻转可以和左子树重合。
那我们怎么判断一课树是对称二叉树?我们肯定要做两个节点的比较,但是绝对不是比较左右节点。我们要比较的是两个树—>根节点的左右子树。
那么我们在递归遍历的时候,要同时的遍历两棵树。比较这两棵树最外侧的节点是否相同,比较这两颗树最内侧的点是否相同,这样才能保证翻转后重合。
在这里采用递归的方式,要用后序遍历。从树的最底下的节点开始向上比较。
代码
public boolean isSymmetric(TreeNode root) {
return sy(root.left,root.right);
}
public boolean sy(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;
boolean out=sy(left.left,right.right);
boolean in=sy(left.right,right.left);
return in&&out;
}
}
我们递归两课树,根节点的左子树和根节点的右子树。然后分别看它的外部节点和内部节点是否相同。外部就是左子树的左孩子。右子树的有孩子。内部就是左子树的右孩子,右子树的左孩子。我们会遇到几种情况。左边遍历到的节点为空,且右边遍历的节点为空,此时说明是对称的。返回true。如果左边的不为空,右边的为空。或者反过来,都说明不对称。如果两边都不为空,但是值不一样,也说明不对称。返回false。
104.二叉树的最大深度
二叉树的最大深度
思路
二叉树的最大深度时从根节点到叶子节点最长的距离。
求深度问题想到用后序遍历,树的深度是Max(左子树的深度,右子树的深度)+1。
代码
class Solution {
public int maxDepth(TreeNode root) {
if(root==null)
return 0;
int left=maxDepth(root.left);
int right=maxDepth(root.right);
return Math.max(left,right)+1;
}
}
- 二叉树的最小深度
二叉树的最小深度
思路
这道题乍一看和上道题最大深度的题是一样的。只需要把max换成min即可。但是这样是不对的,因为这样忽略了一个问题。如果该节点的左子树为空,右子树深度为2;那么此时这颗子树的深度为3,而不是为1。那么这种情况单独判断就行。
代码
class Solution {
public int minDepth(TreeNode root) {
if(root==null)
return 0;
int left=minDepth(root.left);
int right=minDepth(root.right);
if (root.left == null) {
return right + 1;
}
if (root.right == null) {
return left + 1;
}
return Math.min(left,right)+1;
}
}
222.完全二叉树的节点个数
思路
如果是普通二叉树的话,那么这道题和求深度思路是一样的。
代码
class Solution {
public int countNodes(TreeNode root) {
if(root==null)
return 0;
int left=countNodes(root.left);
int right=countNodes(root.right);
return left+right+1;
}
}
但是如果是完全二叉树的话,完全二叉树有一个特点,就是如果是满二叉树的话。它的节点个数=2^树深度 - 1 。但是如果不是满二叉树的话就直接用左子树的节点个数+右节点的节点个数+1。怎么判断是满二叉树?就是左边的深度和右边的深度一样的。
代码
class Solution {
public int countNodes(TreeNode root) {
if (root == null)
return 0;
TreeNode left = root.left;
TreeNode right = root.right;
int leftDepth = 0, rightDepth = 0;
while (left != null) {
left = left.left;
leftDepth++;
}
while (right != null) {
right = right.right;
rightDepth++;
}
if (leftDepth == rightDepth) {
return (2 << leftDepth) - 1;
}
//后序遍历
int leftTreeNum = countNodes(root->left); // 左
int rightTreeNum = countNodes(root->right); // 右
int result = leftTreeNum + rightTreeNum + 1; // 中
return result;
}
}
110.平衡二叉树
平衡二叉树
思路
一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
这道题也是用后序遍历迭代。如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。
分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。
代码
class Solution {
public boolean isBalanced(TreeNode root) {
if(isBalance(root)!=-1)
return true;
else
return false;
}
public int isBalance(TreeNode root)
{
if(root==null)
return 0;
int left=isBalance(root.left);
if(left==-1)
return -1;
int right=isBalance(root.right);
if(right==-1)
return -1;
if(Math.abs(left-right)>1)
{
return -1;
}
else{
return Math.max(right,left)+1;
}
}
}
404.左叶子之和
左叶子之和
思路
题目给定了二叉树的根节点 root ,返回所有左叶子之和。
左叶子的定义就是,首先它是它父节点的左孩子。且它没有任何孩子节点。
在这里,也使用后序遍历。但是当遍历左孩子的时候,孩子本身它不知道它就是左孩子,所以得在它的父节点处判断该节点不是左叶子节点。如果是就把这个节点的值加到sum中。
代码
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if(root==null)
return 0;
int left=sumOfLeftLeaves(root.left);
int right=sumOfLeftLeaves(root.right);
int mid=0;
if(root.left!=null&&root.left.left==null&&root.left.right==null)
mid=root.left.val;
int sum=mid+left+right;
return sum;
}
}
- 二叉树的所有路径
二叉树的所有路径
思路
返回所有从根节点到叶子节点的路径。这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
递归的终止条件不再是root==null。我们的终止条件是该节点的左右节点都为空的时候,这就相当于遍历完一条路径,把这条路径存下来。
那么同时我们在前序遍历遍历左右子节点的时候要先判断当下节点是否为空,不为空的时候才继续遍历。
这里我们需要一个数组记录当前走过的节点。同时我们这里涉及到回溯。如果遍历到叶子节点的时候,说明我们已经走完了一条路径。我们下一次要换一个路径走,这时候就要把叶子节点从当前路径清除。
代码
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> result=new ArrayList<>();
List<Integer> path=new ArrayList<>();
travel(root,result,path);
return result;
}
public void travel(TreeNode root,List<String> result, List<Integer> path){//先序遍历
path.add(root.val);
if(root.left==null&&root.right==null){
StringBuilder sb=new StringBuilder();
for(int i=0;i<path.size()-1;i++)
{
sb.append(path.get(i)+"->");
}
sb.append(path.get(path.size()-1));
result.add(sb.toString());
return;
}
if(root.left!=null)
{
travel(root.left,result,path);//一条路径完事
path.remove(path.size()-1);//回溯
}
if(root.right!=null)
{
travel(root.right,result,path);
path.remove(path.size()-1);
}
}
}
- 找树左下角的值
如果是使用迭代法,可以想到用层序遍历,只需要返回最后一层第一个值就行。
代码
class Solution {
List<List<Integer>> resList = new ArrayList<List<Integer>>();
public int findBottomLeftValue(TreeNode root) {
checkFun02(root);
return resList.get(resList.size()-1).get(0);
}
public void checkFun02(TreeNode node) {
if (node == null) return;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(node);
while (!que.isEmpty()) {
List<Integer> itemList = new ArrayList<Integer>();
int len = que.size();
while (len > 0) {
TreeNode tmpNode = que.poll();
itemList.add(tmpNode.val);
if (tmpNode.left != null) que.offer(tmpNode.left);
if (tmpNode.right != null) que.offer(tmpNode.right);
len--;
}
resList.add(itemList);
}
}
}
如果采用递归的方法,如何判断是最后一行呢,其实就是深度最大的叶子节点一定是最后一行。
遍历顺序应该是都可以的,但是我用的是前序遍历。终止条件就是:当遇到叶子节点的时候,就需要统计一下最大的深度,所以需要遇到叶子节点来更新最大深度。
class Solution {
//递归
private int Deep = -1;
private int value = 0;
public int findBottomLeftValue(TreeNode root) {
int deep=0;
value = root.val;
find(root,deep);
return value;
}
public void find(TreeNode root,int deep){
if(root.left==null&&root.right==null){
if(deep>Deep)
{ Deep=deep;
value=root.val;
}
}
if(root.left!=null)
find(root.left,deep+1);
if(root.right!=null)
find(root.right,deep+1);
}
}
- 路径总和
路径总和
思路
我们需要累加每次访问节点的值,如果相加等于targetSum那么就符合题意。
同样我们也可以用targetSum减去每次访问的节点的值。最后到叶子节点的时候,判断是否为0即可。
用先序迭代遍历。每次先用targetSum减去该节点的数组。并且判断该节点是否是叶子节点,是否当前targetSum为0.如果返回true,则说明满足题意。否则,遍历左右节点。
代码
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root==null)
eturn false;
return path( root, targetSum);
}
public boolean path(TreeNode root,int targetSum){
targetSum-=root.val;
if(root.left==null&&root.right==null){//返回条件
if(targetSum==0)
return true;
else
return false;
}
if(root.left!=null)
{
boolean left=path(root.left,targetSum);
if(left)
return true;
}
if(root.right!=null)
{
boolean right=path(root.right,targetSum);
if(right)
return true;
}
return false;
}
}