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