一、判断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;
}

  

  

OptimalSolution(2)--二叉树问题(4)子树与拓扑结构的更多相关文章

  1. OptimalSolution(2)--二叉树问题(3)Path路径问题

    一.在二叉树中找到累加和为指定值的最长路径长度 给定一棵二叉树和一个32位整数sum,求累加和为sum的最长路径长度.路径是指从某个节点往下,每次最多选择一个孩子节点或者不选所形成的节点链 -3 / ...

  2. OptimalSolution(2)--二叉树问题(2)BST、BBT、BSBT

    一.判断二叉树是否为平衡二叉树(时间复杂度O(N)) 平衡二叉树就是:要么是一棵空树,要么任何一个节点的左右子树高度差的绝对值不超过1. 解法:整个过程为二叉树的后序遍历.对任何一个节点node来说, ...

  3. OptimalSolution(2)--二叉树问题(1)遍历与查找问题

    一.二叉树的按层打印与ZigZag打印 1.按层打印: 1 Level 1 : 1 / \ 2 3 Level 2 : 2 3 / / \ 4 5 6 Level 3 : 4 5 6 / \ 7 8 ...

  4. ZOJ3805Machine(二叉树左右子树变换)

    /* 题意:建立一棵二叉树,左子树和父节点占一个宽度,右子树另外占一个宽度! 使任意左右子树交换顺序,使得整个树的宽度最小! 思路:递归交换左右子树 ! 开始写的代码复杂了,其实左右子树不用真的交换, ...

  5. 99 Lisp Problems 二叉树(P54~P69)

    P54A (*) Check whether a given term represents a binary tree Write a predicate istree which returns ...

  6. 记忆化搜索 codevs 2241 排序二叉树

    codevs 2241 排序二叉树 ★   输入文件:bstree.in   输出文件:bstree.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] 一个边长为n的正三 ...

  7. 二叉树-你必须要懂!(二叉树相关算法实现-iOS)

    这几天详细了解了下二叉树的相关算法,原因是看了唐boy的一篇博客(你会翻转二叉树吗?),还有一篇关于百度的校园招聘面试经历,深刻体会到二叉树的重要性.于是乎,从网上收集并整理了一些关于二叉树的资料,及 ...

  8. POJ2255二叉树

    题目大意就是给出你一个二叉树的前序和中序,要你求后序. 思路:二叉树的排序就是根据根节点的位置来定义的.所以找到二叉树的根节点是最重要的,二叉树的左子树和右子树也可以看成是二叉树,以此递归: #inc ...

  9. 【数据结构】之二叉树的java实现

    转自:http://blog.csdn.net/wuwenxiang91322/article/details/12231657 二叉树的定义: 二叉树是树形结构的一个重要类型.许多实际问题抽象出来的 ...

随机推荐

  1. Java查询判断素数实验报告

    实验源代码: package sushu; import java.util.Scanner; public class First { int size=2; int data[]=new int[ ...

  2. Guava的常用方法示例

    Guava Maven Dependency <dependency> <groupId>com.google.guava</groupId> <artifa ...

  3. 11.Django基础九之中间件

    一 前戏 我们在前面的课程中已经学会了给视图函数加装饰器来判断是用户是否登录,把没有登录的用户请求跳转到登录页面.我们通过给几个特定视图函数加装饰器实现了这个需求.但是以后添加的视图函数可能也需要加上 ...

  4. python爬虫添加请求头和请求主体

    添加头部信息有两种方法 1.通过添加urllib.request.Request中的headers参数 #先把要用到的信息放到一个字典中 headers = {} headers['User-Agen ...

  5. API文档注释 Javadoc

    阅读API文档 JDK包结构 JDK包是由sun开发的一组已经实现的类库,.JDK根据提供的功能不同,将类库划分为若干个包,比如用于操作输入输出的  java.io包,java程序语言设计基础类的   ...

  6. MongoDB 学习笔记之 GridFS

    GridFS: GridFS 是 MongoDB 的一个用来存储/获取大型数据(图像.音频.视频等类型的文件)的规范.它相当于一个存储文件的文件系统,但它的数据存储在 MongoDB 的集合中.Gri ...

  7. 排坑日记之批量从库IO进程停止

    早上刚睁眼,看到了一堆数据库告警的短信,其中一个内容如下: Problem started at 05:02:58 on 2019.10.12 Problem name: Slave is stopp ...

  8. Spring入门(十五):使用Spring JDBC操作数据库

    在本系列的之前博客中,我们从没有讲解过操作数据库的方法,但是在实际的工作中,几乎所有的系统都离不开数据的持久化,所以掌握操作数据库的使用方法就非常重要. 在Spring中,操作数据库有很多种方法,我们 ...

  9. 程序员修神之路--设计一套RPC框架并非易事

    菜菜哥,我最近终于把Socket通信调通了 这么底层的东西你现在都会了,恭喜你离涨薪又进一步呀 http协议不也是利用的Socket吗 可以这么说,http协议是基于TCP协议的,底层的数据传输可以说 ...

  10. html、css以及javascript的注释方式

    HTML:<!--注释内容  -->; CSS:/* 注释内容*/ JS: //注 释内容; 或者块/*   注释内容 */, sublime中注释方法:选中注释内容+ctrl+/  , ...