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;
    }

 

 

  

  

posted @ 2018-09-12 20:35  BigJunOba  阅读(317)  评论(0编辑  收藏  举报