目录
1.检查两棵树是否相同
2.另一棵树的子树
3.判断一棵二叉树是否是平衡二叉树
4.对称二叉树
5.二叉树的构建及遍历
6.给定一个二叉树,找到该树中两个指定结点的最近公共祖先
7.二叉树搜索树转换成排序双向链表
1.检查两棵树是否相同
-
题目描述:
-
代码
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p == null && q != null) return false;
if(p != null && q == null) return false;
//代码走到这里,说明两者都不为空,或者都为空
if(p == null) return true;
if(p.val != q.val) return false;;
//左右子树结构和值都相等才算是一棵相同的树
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
-
思路
1.两个二叉树要是相同的树,那么它们的结构和值就必须都相同;
2.结构和值如何比较:先比较根,看是否都有,如果一个为空,另一个不为空,则结构不同,如果两个都为空,则结构和值都相同,如果都不为空,则比较根的值;然后递归判断分别以左右孩子作为根的两棵树是否相同,重复该操作,直到比较完每一个结点,,,,
2.另一棵树的子树
-
题目描述
-
代码
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p == null && q != null) return false;
if(p != null && q == null) return false;
if(p == null) return true;
if(p.val != q.val) return false;;
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root == null) return false;
//先判断根节点root这棵树是否包含subRoot这棵树,再分别以root的左子树和右子树作为根,来进行判断
if(isSameTree(root, subRoot)) return true;
if(isSubtree(root.left, subRoot)) return true;
if(isSubtree(root.right, subRoot)) return true;
return false;
}
-
思路
做这道题,我们需要在上一道题的基础上来完成,判断一棵二叉树是否是另一棵树的子树,也就是判断这棵树是否包含另一棵树,从函数的参数上来看,传过去的是两棵树的根,由此可知,我们先判断的是,两棵树是否相同,再分别以这棵树的左子树和右子树为根传过去进行判断,也就是判断这棵树的左子树或者右子树是否包含(>=)另一棵树,一直这样递归下去,直到在大树里面找到另一棵树的子树。
-
图解
3.判断一棵二叉树是否是平衡二叉树
-
题目描述
-
代码一
时间复杂度为O(n^2),待会写一个时间复杂度为O(n)的代码;
//求高度的代码
public int height(TreeNode root) {
if(root == null) {
return 0;
}
if(root.left == null && root.right == null) {
return 1;
}
int leftH = height(root.left);
int leftR = height(root.right);
return leftH > leftR ? leftH+1 : leftR+1;
}
public boolean isBalanced(TreeNode root) {
if(root == null) return true;
int leftH = height(root.left);//左树的高度
int rightH = height(root.right);//右树的高度
//求出左右子树的高度后,判断差值是否 <= 1 ,满足就继续往下递归,看看左右子树是否也是平衡树
return (Math.abs(leftH - rightH) <= 1) && (isBalanced(root.left) && isBalanced(root.right));
}
-
思路
这个题需要借助求高度的代码,求平衡二叉树其实思路很简单,只要计算根结点的这棵树的左子树的高度和右子树的高度,然后相减判断是否 <= 1 ,如果满足,则继续往下判断,以根的左右孩子作为根,继续重复操作。当然这不是最好的方法,时间复杂度太高了。
-
图解
-
代码二
时间复杂度为O(n),大大提高了效率;
public int height(TreeNode root) {
if(root == null) return 0;
int leftH = height(root.left);
int rightH = height(root.right);
//我在求高度的时候就检查下面的树是否平衡
if(leftH >= 0 && rightH >= 0 && Math.abs(leftH - rightH) <= 1) {
return Math.max(leftH,rightH) + 1;
} else {
return -1;
}
}
public boolean isBalanced(TreeNode root) {
if(root == null) return true;
return height(root) >= 0;
}
-
思路
如果按照第一种写法,在算最初的根节点那棵树时,就已经分别计算下面的左子树,或者右子树的左右子树的高度,如果按照那种方式继续递归下去,将会重复计算大量数据,效率非常慢;于是第二种方法,就是改造求高度的函数,我们在递归的过程中就直接判断左右子树高度的差值是否 <= 1,满足我就返回左右子树高度中较大的一个加一,不满足就返回-1,因为高度不可能是负数,这样将省略很多不必要的计算;还要注意的一点就是,判断条件还要满足求出的左右子树高度均大于0,否则负数也将在递归的过程中参与计算了。
-
对比图
4.对称二叉树
-
题目描述
-
代码
private boolean isSymmetricChild(TreeNode leftTree, TreeNode rightTree) {
if(leftTree != null && rightTree == null) return false;
if(leftTree == null && rightTree != null) return false;
if(leftTree == null) return true;
if(leftTree.val != rightTree.val) return false;
//左子树的左,右子树的右,左子树的右,右子树的左
return isSymmetricChild(leftTree.left,rightTree.right) && isSymmetricChild(leftTree.right, rightTree.left);
}
public boolean isSymmetric(TreeNode root) {
return isSymmetricChild(root.left, root.right);
}
-
思路
这道题与相同的树有着异曲同工之妙,唯一不同的地方,就是递归不同,这里也是先比较结构,再比较值的相同与否,在比较完根的结构和值后,我们需要递归比较的是左子树的左,和右子树的右,左子树的右和右子树的左,其他的操作都对应着。
5.二叉树的构建及遍历
-
题目描述
-
代码
import java.util.*;
class TreeNode {
char val;
TreeNode left;
TreeNode right;
public TreeNode(char val) {
this.val = val;
}
}
public class Main {
//主要代码
public static int i = 0;
//字符串是先序遍历的,所以创建这棵树就也是按照先序遍历来创建
public static TreeNode createTree(String str) {
TreeNode root = null;
if(str.charAt(i) != '#') {
root = new TreeNode(str.charAt(i));//创建根
i++;
root.left = createTree(str);//创建左树
root.right = createTree(str);//创建右数
} else {
i++;
}
return root;
}
//中序遍历
public static void inorder(TreeNode root) {
if(root == null) return;
inorder(root.left);
System.out.print(root.val + " ");
inorder(root.right);
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while(scan.hasNextLine()) {
String str = scan.nextLine();
TreeNode root = createTree(str);
inorder(root);
}
}
}
-
思路
1.这道题的关键代码就是创建二叉树这个函数,题目给的是一串先序遍历的字符串,所以我们创建二叉树的时候,也是用先序遍历来创建的,这里可能会有人问:只知道先序遍历也能创建一棵二叉树吗?平时,我们必须要知道中序遍历和前后序遍历的其中一种,就能创建二叉树,但是这里不一样,它给的字符串,虽然我们只知道要按前序遍历来创建,但是它规定了空树在哪,所以只有一种情况,就可以创建二叉树。
创建好的二叉树:
2.得到这棵树之后,我们就可以从字符串入手去创建这棵树了,我们需要拿到字符串的每一个字符,换做以前,我们肯定要弄一个循环,但是二叉树的真正创建方式是用递归来创建的,所以不能用循环,我们需要一个变量 i ,通过 charAt()方法不断获取字符串的每一个字符,如果 i 放在函数里面的话,每次递归,i 都会重新变为 0 ,所以我们需要将 i 定义在方法的外面,接下来,就是实例化一个根节点,然后不断以左右子树作为根节点,递归下去。
-
图解
6.给定一个二叉树,找到该树中两个指定结点的最近公共祖先
-
题目描述
-
代码一
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) {
return null;
}
//其中一个为root,则返回root
if(root == p || root == q) {
return root;
}
//找左右子树,是从下往上找的
TreeNode leftRet = lowestCommonAncestor(root.left, p, q);
TreeNode rightRet = lowestCommonAncestor(root.right, p, q);
//如果都不为空,那么说明p,q在两侧
if(leftRet != null && rightRet != null) {
return root;
} else if(leftRet != null) {
//如果左不为空,那么左为空,说明p,q都在左侧,且leftRet为root
return leftRet;
} else if(rightRet != null){
//如果右不为空,那么左为空,说明p,q都在右侧,且rightRet为root
return rightRet;
}
return null;
}
-
思路一
如果这棵树是一棵二叉搜索树,那怎么找公共祖先?
只讨论 p,无非就三种情况:
得出结论:
1. p 和 q 一个在一侧,此时的最近公共祖先是root;
2. p 或 q 其中一个为 root ,此时的最近公共祖先是 p 或者 q;
3. p 和 q 都在左侧,或者都在右侧,此时需要分别递归左侧或者右侧继续查找。
-
代码二
private boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {
if(root == null || node == null) {
return false;
}
stack.push(root);
//递归过程中,如果根等于 p 或 q , 说明路径找完了,依次返回
if(root == node) {
return true;
}
//往左递归找 p 或 q
boolean leftRet = getPath(root.left, node, stack);
if(leftRet == true) {
return true;
}
//往右递归找 p 或 q
boolean rightRet = getPath(root.right, node, stack);
if(rightRet == true) {
return true;
}
//因为getPath方法,一进来就入栈,如果没有返回true,说明入栈的就不是路劲上的结点,直接弹出,然后返回false
stack.pop();
return false;
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//1.分别获取 p,q 的路径,依次添加到栈中
Stack<TreeNode> stack1 = new Stack<>();
getPath(root, p, stack1);
Stack<TreeNode> stack2 = new Stack<>();
getPath(root, q, stack2);
//2.求出两个栈的大小,让大的先走差值步
int size1 = stack1.size();
int size2 = stack2.size();
if(size1 > size2) {
int size = size1 - size2;
while(size != 0) {
stack1.pop();
size--;
}
} else {
int size = size2 - size1;
while(size != 0) {
stack2.pop();
size--;
}
}
//再一起走,直到栈顶元素相等
while(!stack1.empty() && ! stack2.empty()) {
if(stack1.peek() == stack2.peek()) {
return stack1.pop();
} else {
stack1.pop();
stack2.pop();
}
}
return null;
}
-
思路二
如果二叉树的表示方式,是孩子双亲表示法。那么此时,这个最近公共祖先,可以被优化为,求链的交点;
解法:
1.使用两个栈,存储从根节点到指定结点的路径上的所有结点;
2.比较两个栈的大小,让 size 大的栈出差值个结点;
3.此时同时开始出栈,如果栈顶元素相同,那么这个值就是最近公共祖先
-
图解
7.二叉树搜索树转换成排序双向链表
-
题目描述
-
代码
//调整结构--中序遍历
//prev必须放在方法外面,不然每次递归,都变成null了
TreeNode prev = null;
public void ConvertChild(TreeNode pCur) {
//不用担心最后一个结点有无后继,当prev指向最后一个结点的时候,pCur已经为null了
if(pCur == null) return;
ConvertChild(pCur.left);
//绑前驱
pCur.left = prev;
//空结点只能作为头节点的前驱,没有后继
if(prev != null) {
prev.right = pCur;
}
//绑后继
prev = pCur;
ConvertChild(pCur.right);
}
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree == null) return null;
ConvertChild(pRootOfTree);
TreeNode head = pRootOfTree;
while(head.left != null) {
head = head.left;
}
return head;
}
-
思路
做这道题之前只需要把握两个点:
-
图解
谢谢观看!!!