【刷题】第三弹——二叉树篇(上)
文章目录
- 一.相同的树
- 二. 另一棵树的子树
- 三. 翻转二叉树
- 四. 对称二叉树
- 五. 平衡二叉树
- 六. 二叉搜索树
- 七. 二叉树的遍历
一.相同的树
检查两棵树是否相同
思路:
1.先比较结构 结构不一样直接false
2.结构一样,在比较值
public boolean isSameTree(TreeNode p, TreeNode q) {if(p==null && q==null){ //两个都是空一样return true;}if(p!=null && q==null || p==null && q!=null){//结构不一样return false;}if(p.val!=q.val){//到这里结构一样 开始判断值return false;}return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);//递归 判断左子树和右子树}
二. 另一棵树的子树
另一颗树的子树
关于检查root和subRoot是否具有相同的结构和节点值,这个思路和我们上道题是一样的
我们可以直接使用isSameTree()函数
不过后面的递归要注意,不能再用isSameTree(),后面的递归不知是需要isSameTree(),需要的是整个subTree()
首先:判断子树和根节点是否一样? 一开始就是14 和 4 比较就没必要继续向下比较了
再者:判断子树是否和当前root的左子树一样?
最后:判断子树是否和当前root的右子树一样?
public boolean isSubtree(TreeNode root, TreeNode subRoot) {if(root==null){return false;}if(isSameTree(root,subRoot)) return true;if(isSubtree(root.left,subRoot)) return true;if(isSubtree(root.right,subRoot)) return true;return false;}public boolean isSameTree(TreeNode p, TreeNode q) {if(p==null && q==null){ //两个都是空一样return true;}if(p!=null && q==null || p==null && q!=null){//结构不一样return false;}if(p.val!=q.val){//到这里结构一样 开始判断值return false;}return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);//递归 判断左子树和右子树}
满足条件就返回true 确实是子树 所有条件都不满足,就不是子树
分析复杂度
时间复杂度:O(m*n),这里的m和n分别是主树root和子树subRoot的节点数量 最坏的情况root的每个节点都要判断才能确定是不是子树
空间复杂度:O(max(m,n)),递归的深度取决于树的高度,最坏情况下树退化为链表,递归深度为树的节点数
三. 翻转二叉树
翻转二叉树
思路:前序遍历使每一个root的左右节点都交换,临时变量tmp帮助完成交换 最后返回整颗树
正确代码:
public TreeNode invertTree(TreeNode root) {if(root==null){return null;}if(root.left==null && root.right==null){return root;//空了不用向后互换了 提升一点点效率}TreeNode tmp=root.left;root.left=root.right;root.right=tmp;invertTree(root.left);invertTree(root.right);return root;}
四. 对称二叉树
对称二叉树
结构不一样 肯定不是轴对称
要判断整棵树是不是轴对称 就要判断root.left 和 root,right 是不是轴对称
判断左子树和右子树结构一不一样,对应的值一不一样
1.再判断左子树的左树和右子树的右树,是否轴对称
2.左子树的右树和右子树的左树,是否轴对称
题目中给的函数只有一个参数,我们要左右子树一起对比才能知道对不对称,所以我们添加一个子函数,传入两个参数解决问题
//子函数
public boolean isSymmetricChild(TreeNode leftTree,TreeNode rightTree) {if(leftTree == null && rightTree == null) {return true;}if(leftTree != null && rightTree == null || leftTree == null && rightTree != null) {return false;}if(leftTree.val != rightTree.val) {return false;}return isSymmetricChild(leftTree.left,rightTree.right)&& isSymmetricChild(leftTree.right,rightTree.left);}
完整代码:
public boolean isSymmetric(TreeNode root) {if(root==null){return true;}return isSymmetricChild(root.left,root.right);//利用子函数比较左子树右子树}public boolean isSymmetricChild(TreeNode leftTree,TreeNode rightTree) {if(leftTree == null && rightTree == null) {return true;}if(leftTree != null && rightTree == null || leftTree == null && rightTree != null) {return false;}if(leftTree.val != rightTree.val) {return false;}return isSymmetricChild(leftTree.left,rightTree.right)&& isSymmetricChild(leftTree.right,rightTree.left);}
五. 平衡二叉树
平衡二叉树
平衡二叉树就是每一个节点的左右子树高度差不能超过1
思路:使用前序遍历 遍历树的所有节点,求每个节点的左右树的高度差,h>=2,返回false &&root的左树 && root的右树 也都要是平衡的
从这里我们可以想到一个改进的点 我们这里说的是 root的左树 和root的右树也要是平衡的 我们遍历的过程中 是不是发现不平衡可以直接返回false ,这样可以大大提高时间复杂度 ,这也是字节的面试原题
我们先用第一种方法:
public boolean isBalanced(TreeNode root) {if(root==null){return true;}//我们需要求绝对值 先判断左树的高度不为负 int leftH=getHeight(root.left);int rightH=getHeight(root.right);int h=Math.abs(leftH-rightH);// if(h>=2){// return false;// }return Math.abs(leftH-rightH)<2 && isBalanced(root.left)&&isBalanced(root.right);}//求高度public int getHeight(TreeNode root){if(root==null){return 0;}int leftHeight=getHeight(root.left);int rightHeight=getHeight(root.right);return Math.max(leftHeight,rightHeight)+1;}
这就是我们思路中说的,利用前序遍历,求每个节点的左树右树的高度差其中会有很多的重复计算高度,h<=2,&&左树平衡,&&右树平衡
分析一下时间复杂度:已经达到了O(n^2)
如果我就规定必须在时间复杂度为O(n),完成判断是否是平衡二叉树
该怎么办,其实在**&&左树平衡,&&右树平衡 的过程中,就会发生不平衡**,没必要进行到计算高度差,也就是一旦不平衡我们高度上就返回 -1 标记一下 ,后面再加上一些判断就可以不用进行后面的递归了
public boolean isBalanced(TreeNode root) {if(root==null){return true;}//我们需要求绝对值 先判断左树的高度不为负 int leftH=getHeight(root.left);int rightH=getHeight(root.right);int h=Math.abs(leftH-rightH);// if(h>=2){// return false;// }if(leftH<0){return false;}//左树算高度时就已经不平衡了return Math.abs(leftH-rightH)<2 && isBalanced(root.left)&&isBalanced(root.right);}//求高度public int getHeight(TreeNode root){if(root==null){return 0;}int leftHeight=getHeight(root.left);int rightHeight=getHeight(root.right);if(leftHeight<0){return -1;}if(rightHeight>=0 && Math.abs(leftHeight-rightHeight) <= 1){//保证了不会因为右树的左子树返回-1而造成的 符合高度差return Math.max(leftHeight,rightHeight)+1;}else{return -1;}}
以上就是时间复杂度为O(n)的写法
我们放到IDEA中详细解释
完整代码:
public boolean isBalanced(TreeNode root) {if(root==null){return true;}//我们需要求绝对值 先判断左树的高度不为负 int leftH=getHeight(root.left);int rightH=getHeight(root.right);int h=Math.abs(leftH-rightH);// if(h>=2){// return false;// }if(leftH<0){return false;}//左树算高度时就已经不平衡了return Math.abs(leftH-rightH)<2 && isBalanced(root.left)&&isBalanced(root.right);}//求高度public int getHeight(TreeNode root){if(root==null){return 0;}int leftHeight=getHeight(root.left);int rightHeight=getHeight(root.right);if(leftHeight<0){return -1; //}if(rightHeight>=0 && Math.abs(leftHeight-rightHeight) <= 1){ //保证了不会因为右树的左子树返回-1而造成的 符合高度差return Math.max(leftHeight,rightHeight)+1;}else{return -1;}}
六. 二叉搜索树
二叉搜索树与双向链表
二叉搜索树:二叉树的左子树每个节点都比根小, 右子树每个节点都比根大
所以转化成双向链表要使用中序遍历
思路:要中序遍历 改变每个节点的前驱后继 一直遍历到头结点的前驱为空(左树),那就是链表的头
public TreeNode Convert(TreeNode pRootOfTree) {if(pRootOfTree==null){return null;}ConvertChild(pRootOfTree);TreeNode head=pRootOfTree;while(head.left!=null){head=head.left;}return head;}TreeNode prev=null;public void ConvertChild(TreeNode root){if(root==null){return ;}ConvertChild(root.left);root.left=prev;if(prev!=null){prev.right=root;}prev=root;ConvertChild(root.right);}
七. 二叉树的遍历
二叉树的遍历
首先创建一个二叉树,只用前序遍历能创建吗?–不可以
必须使用两种遍历才能创建一个二叉树
前序遍历和后序遍历可以确定一颗二叉树吗?–不可以
前序和后序都是确定的根,光知道根不可能确定一颗二叉树
所以确定二叉树时必须有中序遍历来确定左子树和右子树
但是这道题,只需要前序遍历是可以确定一颗二叉树的
为什么,不是说要两种遍历一起吗?
题目中已经告诉了我们他一定是前序遍历,并且说明了空树的位置,就能确定唯一的二叉树了
charAt()返回值为char字符类型
一个文件中只能有一个public修饰的类
静态类中不能使用非静态变量
class TreeNode{ //一个文件只能有一个public修饰的类public char val;public TreeNode left;public TreeNode right;public TreeNode(char val){this.val=val;}
}// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseString str=in.nextLine();//接收用户输入//利用前序遍历 创建树TreeNode root=creatTree(str);inorderTree(root);}}
public static int i=0;public static TreeNode creatTree(String str){TreeNode root=null;if(str.charAt(i)!='#'){root=new TreeNode(str.charAt(i));i++;root.left=creatTree(str);root.right=creatTree(str);}else{i++;}return root;}//中序遍历public static void inorderTree(TreeNode root){if(root==null){return ;}inorderTree(root.left);System.out.print(root.val+" ");inorderTree(root.right);}