• OptimalSolution(2)--二叉树问题(4)子树与拓扑结构


      一、判断t1树是否包含t2树全部的拓扑结构

              1
            /    
           2      3                       2
         /       /                     /  
        4    5  6    7                  4    5
       /    /                         /
      8  9 10                         8                               返回:true

      解法(O(M×N)):如果t1中某棵子树头结点和t2头结点的值一样,则从这两个头结点开始匹配,匹配的每一步都是让t1上的节点跟着t2的先序遍历移动,每移动一步,都检查t1的当前节点和t2当前节点的值是否一样。如果匹配的过程中发现有不匹配的过程,直接返回false,那么再去寻找t1的下一棵树。

        public boolean contains(Node t1, Node t2) {
            return check(t1, t2) || contains(t1.left, t2) || contains(t1.right, t2);
        }
    
        private boolean check(Node h, Node t2) {
            if (t2 == null) return true;
            if (h == null || h.val != t2.val) return false;
            return check(h.left, t2.left) && check(h.right, t2.right);
        }

      二、判断t1树中是否有与t2树拓扑结构完全相同的子树

              1
            /    
           2      3                       2                          2
         /       /                     /                         / 
        4    5  6    7                  4    5                     4   5
           /                               /                       
         8 9                              8  9  返回:true            8     返回:false

      如果t1的节点数为N,t2的节点数为M  

      1.时间复杂度为O(N×M)的方法:对于t1的每棵子树,都去判断是否与t2树的拓扑结构完全一样,这个过程的复杂度为O(M),t1的子树一共有N棵,所以时间复杂度是O(N×M)

      做法:(知识扩展:判断两棵树是否相等)首先定义一个判断两棵树是否相等的方法,然后让s的每一个子树都和t作比较,看是否两棵树相等,如果存在相等,那么就返回true

        public boolean isSubtree(TreeNode s, TreeNode t) {
            if (s == null) return false;
            if (isSame(s, t)) return true;
            return isSubtree(s.left, t) || isSubtree(s.right, t);
        }
        
        private boolean isSame(TreeNode sub, TreeNode t) {
            if (sub == null && t == null) return true;
            if (sub != null && t != null) {
                if (sub.val == t.val) {
                    if (isSame(sub.left, t.left)) {
                        if (isSame(sub.right, t.right)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

      2.时间复杂度为O(N+M)的方法:先将t1树前序遍历序列化成字符串“1!2!4!#!8!#!#!5!9!#!#!#!#!3!6!#!#!7!#!#!”,而t2树前序遍历序列化为字符串“2!4!#!8!#!#!5!9!#!#!”(t3树前序遍历序列化为字符串“2!4!#!8!#!#!5!#!#!”),也就是验证t2str是否是t1str的子串即可,可以用KMP算法在线性时间内解决。所以t1序列化的时间复杂度为O(N),t2序列化的时间复杂度是O(M),KMP解决两个字符串的匹配问题O(M+N),所以时间复杂度是O(M+N)。

      

      三、判断一棵树是否为镜像

        1
       / 
      2   2
     /  / 
    3  4 4  3 true
        1
       / 
      2   2
          
       3    3  false

      思路很关键:将一棵树分成两棵树判断。

      解法:如果两棵树为镜像树,那么必须满足:(1)这两棵树的根节点的值相同(2)第一棵树的左子树是第二棵树的右子树(3)第一棵树的右子树是第一棵树的左子树

      1.递归

        public boolean isSymmetric(TreeNode root) {
            return isSymmetircTwoTrees(root, root);
        }
        
        private boolean isSymmetircTwoTrees(TreeNode t1, TreeNode t2) {
            if (t1 == null && t2 == null) return true;
            if (t1 == null || t2 == null) return false;
            return t1.val == t2.val && isSymmetircTwoTrees(t1.left, t2.right) && isSymmetircTwoTrees(t1.right, t2.left);
        }

      2.迭代(BFS)

        public boolean isSymmetricBFS(TreeNode root) {
            if (root == null) return true;
            Queue<TreeNode> queue = new LinkedList<>();
            queue.offer(root);
            queue.offer(root);
            while (!queue.isEmpty()) {
                TreeNode t1 = queue.poll();
                TreeNode t2 = queue.poll();
                if (t1 == null && t2 == null) continue;
                if (t1 == null || t2 == null) return false;
                if (t1.val != t2.val) return false;
                queue.offer(t1.left);
                queue.offer(t2.right);
                queue.offer(t1.right);
                queue.offer(t2.left);
            }
            return true;
        }

      四、二叉树的反转(考虑问题太过复杂,要从最基本的要求开始考虑

      1.仅仅适用于完全二叉树的反转(不满足某个节点有且仅有一个子树为空的情况)

         4
       /   
      2     7
     /    / 
    1   3 6   9
    Output:
         4
       /   
      7     2
     /    / 
    9   6 3   1

      代码实现:

        public TreeNode invertTree(TreeNode root) {
            if (root != null) {
                Queue<TreeNode> queue = new LinkedList<>();
                queue.offer(root.left);
                queue.offer(root.right);
                while (!queue.isEmpty()) {
                    TreeNode t1 = queue.poll();
                    TreeNode t2 = queue.poll();
                    if (t1 == null && t2 == null) continue;
                    if (t1 != null && t2 != null) {
                        int tmp1 = t1.val;
                        t1.val = t2.val;
                        t2.val = tmp1;
                    }
                    queue.offer(t1.left);
                    queue.offer(t2.right);
                    queue.offer(t1.right);
                    queue.offer(t2.left);
                }
            }
            return root;
        }

      2.适用于所有二叉树的反转

            1       1
           /         
          2           2 

      思路1:基于最简单的层序遍历, TreeNode tmp = root.left; root.left = root.right; root.right = tmp;如上面的测试用例,如果root=1,root.left=2,root.right=null,那么在交换之后,还是可以满足,即root.left=null,root.right=2

      还要注意的是:必须使用TreeNode cur = queue.poll();作为引用,不能直接使用root = queue.poll();,如果直接使用root,return的是最后一个遍历到的节点。

        public TreeNode invertTreeBFS(TreeNode root) {
            if (root != null) {
                Queue<TreeNode> queue = new LinkedList<>();
                queue.offer(root);
                while(!queue.isEmpty()) {
                    TreeNode cur = queue.poll();
                    TreeNode tmp = cur.left;
                    cur.left = cur.right;
                    cur.right = tmp;
                    if (cur.left != null) queue.offer(cur.left);
                    if (cur.right != null) queue.offer(cur.right);
                }
            }
            return root;
        }

      思路2:基于最简单的后序递归遍历,invertTreeDFS方法返回root的反转,而root的左子树是右子树的反转,并且root的右子树是左子树的反转。

        public TreeNode invertTreeDFS(TreeNode root) {
            if (root == null) return null;
            TreeNode right = invertTree(root.right);
            TreeNode left  = invertTree(root.left);
            root.left  = right;
            root.right = left;
            return root;
        }

      

      

  • 相关阅读:
    SpringMVC之数据处理及跳转
    SpringMVC之控制器Controller和RestFul风格
    第一个SpringMVC程序
    什么是SpringMVC
    Spring声明式事务
    【嘎】字符串-反转字符串
    【嘎】字符串-字符串中的单词数
    【嘎】二叉树-226. 翻转二叉树
    【嘎】数组-面试题 01.07. 旋转矩阵-看题解
    【嘎】字符串-统计位数为偶数的数字
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/9637185.html
Copyright © 2020-2023  润新知