剑指Offer面试题:17.树的子结构
一、题目:树的子结构
题目:输入两棵二叉树A和B,判断B是不是A的子结构。例如下图中的两棵二叉树,由于A中有一部分子树的结构和B是一样的,因此B是A的子结构。
该二叉树的节点定义如下,这里使用C#语言描述:
public class BinaryTreeNode
{
public int Data { get; set; }
public BinaryTreeNode leftChild { get; set; }
public BinaryTreeNode rightChild { get; set; } public BinaryTreeNode(int data)
{
this.Data = data;
} public BinaryTreeNode(int data, BinaryTreeNode left, BinaryTreeNode right)
{
this.Data = data;
this.leftChild = left;
this.rightChild = right;
}
}
二、解题思路
2.1 核心步骤
要查找树A中是否存在和树B结构一样的子树,我们可以分成两步:
Step1.在树A中找到和B的根结点的值一样的结点R;
Step2.判断树A中以R为根结点的子树是不是包含和树B一样的结构。
很明显,这是一个递归的过程。
2.2 代码实现
public static bool HasSubTree(BinaryTreeNode root1, BinaryTreeNode root2)
{
bool result = false; if (root1 != null && root2 != null)
{
if (root1.Data == root2.Data)
{
result = DoesTree1HasTree2(root1, root2);
}
// 从根节点的左子树开始匹配Tree2
if (!result)
{
result = HasSubTree(root1.leftChild, root2);
}
// 如果左子树没有匹配成功则继续在右子树中继续匹配Tree2
if (!result)
{
result = HasSubTree(root1.rightChild, root2);
}
} return result;
} private static bool DoesTree1HasTree2(BinaryTreeNode root1, BinaryTreeNode root2)
{
if (root2 == null)
{
// 证明Tree2已经遍历结束,匹配成功
return true;
} if (root1 == null)
{
// 证明Tree1已经遍历结束,匹配失败
return false;
} if (root1.Data != root2.Data)
{
return false;
}
// 递归验证左子树和右子树是否包含Tree2
return DoesTree1HasTree2(root1.leftChild, root2.leftChild) && DoesTree1HasTree2(root1.rightChild, root2.rightChild);
}
三、单元测试
为了方便测试,这里封装了一个设置指定根节点的左孩子和右孩子节点的方法:SetSubTreeNode
public void SetSubTreeNode(BinaryTreeNode root, BinaryTreeNode lChild, BinaryTreeNode rChild)
{
if (root == null)
{
return;
} root.leftChild = lChild;
root.rightChild = rChild;
}
3.1 功能测试
// 01.树中结点含有分叉,树B是树A的子结构
// 8 8
// / \ / \
// 8 7 9 2
// / \
// 9 2
// / \
// 4 7
[TestMethod]
public void HasSubTreeTest1()
{
BinaryTreeNode nodeA1 = new BinaryTreeNode();
BinaryTreeNode nodeA2 = new BinaryTreeNode();
BinaryTreeNode nodeA3 = new BinaryTreeNode();
BinaryTreeNode nodeA4 = new BinaryTreeNode();
BinaryTreeNode nodeA5 = new BinaryTreeNode();
BinaryTreeNode nodeA6 = new BinaryTreeNode();
BinaryTreeNode nodeA7 = new BinaryTreeNode(); SetSubTreeNode(nodeA1, nodeA2, nodeA3);
SetSubTreeNode(nodeA2, nodeA4, nodeA5);
SetSubTreeNode(nodeA5, nodeA6, nodeA7); BinaryTreeNode nodeB1 = new BinaryTreeNode();
BinaryTreeNode nodeB2 = new BinaryTreeNode();
BinaryTreeNode nodeB3 = new BinaryTreeNode(); SetSubTreeNode(nodeB1, nodeB2, nodeB3); Assert.AreEqual(SubTreeHelper.HasSubTree(nodeA1, nodeB1), true);
} // 02.树中结点含有分叉,树B不是树A的子结构
// 8 8
// / \ / \
// 8 7 9 2
// / \
// 9 3
// / \
// 4 7
[TestMethod]
public void HasSubTreeTest2()
{
BinaryTreeNode nodeA1 = new BinaryTreeNode();
BinaryTreeNode nodeA2 = new BinaryTreeNode();
BinaryTreeNode nodeA3 = new BinaryTreeNode();
BinaryTreeNode nodeA4 = new BinaryTreeNode();
BinaryTreeNode nodeA5 = new BinaryTreeNode();
BinaryTreeNode nodeA6 = new BinaryTreeNode();
BinaryTreeNode nodeA7 = new BinaryTreeNode(); SetSubTreeNode(nodeA1, nodeA2, nodeA3);
SetSubTreeNode(nodeA2, nodeA4, nodeA5);
SetSubTreeNode(nodeA5, nodeA6, nodeA7); BinaryTreeNode nodeB1 = new BinaryTreeNode();
BinaryTreeNode nodeB2 = new BinaryTreeNode();
BinaryTreeNode nodeB3 = new BinaryTreeNode(); SetSubTreeNode(nodeB1, nodeB2, nodeB3); Assert.AreEqual(SubTreeHelper.HasSubTree(nodeA1, nodeB1), false);
}
3.2 特殊输入测试
// 03.树中结点只有左子结点,树B是树A的子结构
// 8 8
// / /
// 8 9
// / /
// 9 2
// /
// 2
// /
//
[TestMethod]
public void HasSubTreeTest3()
{
BinaryTreeNode nodeA1 = new BinaryTreeNode();
BinaryTreeNode nodeA2 = new BinaryTreeNode();
BinaryTreeNode nodeA3 = new BinaryTreeNode();
BinaryTreeNode nodeA4 = new BinaryTreeNode();
BinaryTreeNode nodeA5 = new BinaryTreeNode(); nodeA1.leftChild = nodeA2;
nodeA2.leftChild = nodeA3;
nodeA3.leftChild = nodeA4;
nodeA4.leftChild = nodeA5; BinaryTreeNode nodeB1 = new BinaryTreeNode();
BinaryTreeNode nodeB2 = new BinaryTreeNode();
BinaryTreeNode nodeB3 = new BinaryTreeNode(); nodeB1.leftChild = nodeB2;
nodeB2.leftChild = nodeB3; Assert.AreEqual(SubTreeHelper.HasSubTree(nodeA1, nodeB1), true);
} // 04.树中结点只有左子结点,树B不是树A的子结构
// 8 8
// / /
// 8 9
// / /
// 9 3
// /
// 2
// /
//
[TestMethod]
public void HasSubTreeTest4()
{
BinaryTreeNode nodeA1 = new BinaryTreeNode();
BinaryTreeNode nodeA2 = new BinaryTreeNode();
BinaryTreeNode nodeA3 = new BinaryTreeNode();
BinaryTreeNode nodeA4 = new BinaryTreeNode();
BinaryTreeNode nodeA5 = new BinaryTreeNode(); nodeA1.leftChild = nodeA2;
nodeA2.leftChild = nodeA3;
nodeA3.leftChild = nodeA4;
nodeA4.leftChild = nodeA5; BinaryTreeNode nodeB1 = new BinaryTreeNode();
BinaryTreeNode nodeB2 = new BinaryTreeNode();
BinaryTreeNode nodeB3 = new BinaryTreeNode(); nodeB1.leftChild = nodeB2;
nodeB2.leftChild = nodeB3; Assert.AreEqual(SubTreeHelper.HasSubTree(nodeA1, nodeB1), false);
} // 05.树中结点只有右子结点,树B是树A的子结构
// 8 8
// \ \
// 8 9
// \ \
// 9 2
// \
// 2
// \
// 5
[TestMethod]
public void HasSubTreeTest5()
{
BinaryTreeNode nodeA1 = new BinaryTreeNode();
BinaryTreeNode nodeA2 = new BinaryTreeNode();
BinaryTreeNode nodeA3 = new BinaryTreeNode();
BinaryTreeNode nodeA4 = new BinaryTreeNode();
BinaryTreeNode nodeA5 = new BinaryTreeNode(); nodeA1.rightChild = nodeA2;
nodeA2.rightChild = nodeA3;
nodeA3.rightChild = nodeA4;
nodeA4.rightChild = nodeA5; BinaryTreeNode nodeB1 = new BinaryTreeNode();
BinaryTreeNode nodeB2 = new BinaryTreeNode();
BinaryTreeNode nodeB3 = new BinaryTreeNode(); nodeB1.rightChild = nodeB2;
nodeB2.rightChild = nodeB3; Assert.AreEqual(SubTreeHelper.HasSubTree(nodeA1, nodeB1), true);
} // 06.树中结点只有右子结点,树B不是树A的子结构
// 8 8
// \ \
// 8 9
// \ / \
// 9 3 2
// \
// 2
// \
// 5
[TestMethod]
public void HasSubTreeTest6()
{
BinaryTreeNode nodeA1 = new BinaryTreeNode();
BinaryTreeNode nodeA2 = new BinaryTreeNode();
BinaryTreeNode nodeA3 = new BinaryTreeNode();
BinaryTreeNode nodeA4 = new BinaryTreeNode();
BinaryTreeNode nodeA5 = new BinaryTreeNode(); nodeA1.rightChild = nodeA2;
nodeA2.rightChild = nodeA3;
nodeA3.rightChild = nodeA4;
nodeA4.rightChild = nodeA5; BinaryTreeNode nodeB1 = new BinaryTreeNode();
BinaryTreeNode nodeB2 = new BinaryTreeNode();
BinaryTreeNode nodeB3 = new BinaryTreeNode();
BinaryTreeNode nodeB4 = new BinaryTreeNode(); nodeB1.rightChild = nodeB2;
SetSubTreeNode(nodeB2, nodeB3, nodeB4); Assert.AreEqual(SubTreeHelper.HasSubTree(nodeA1, nodeB1), false);
} // 07.树A为空树
[TestMethod]
public void HasSubTreeTest7()
{
BinaryTreeNode nodeB1 = new BinaryTreeNode();
BinaryTreeNode nodeB2 = new BinaryTreeNode();
BinaryTreeNode nodeB3 = new BinaryTreeNode();
BinaryTreeNode nodeB4 = new BinaryTreeNode(); nodeB1.rightChild = nodeB2;
SetSubTreeNode(nodeB2, nodeB3, nodeB4); Assert.AreEqual(SubTreeHelper.HasSubTree(null, nodeB1), false);
} // 08.树B为空树
[TestMethod]
public void HasSubTreeTest8()
{
BinaryTreeNode nodeA1 = new BinaryTreeNode();
BinaryTreeNode nodeA2 = new BinaryTreeNode();
BinaryTreeNode nodeA3 = new BinaryTreeNode();
BinaryTreeNode nodeA4 = new BinaryTreeNode();
BinaryTreeNode nodeA5 = new BinaryTreeNode(); nodeA1.rightChild = nodeA2;
nodeA2.rightChild = nodeA3;
nodeA3.rightChild = nodeA4;
nodeA4.rightChild = nodeA5; Assert.AreEqual(SubTreeHelper.HasSubTree(nodeA1, null), false);
} // 09.树A和树B都为空树
[TestMethod]
public void HasSubTreeTest9()
{
Assert.AreEqual(SubTreeHelper.HasSubTree(null, null), false);
}
3.3 测试结果
(1)测试通过情况

(2)代码覆盖率

剑指Offer面试题:17.树的子结构的更多相关文章
- 剑指Offer:面试题18——树的子结构(java实现)
问题描述: 输入两棵二叉树A和B,判断B是不是A的子结构.二叉树结点的定义如下: public class TreeNode { int val = 0; TreeNode left = null; ...
- 剑指Offer - 九度1520 - 树的子结构
剑指Offer - 九度1520 - 树的子结构2013-11-30 22:17 题目描述: 输入两颗二叉树A,B,判断B是不是A的子结构. 输入: 输入可能包含多个测试样例,输入以EOF结束.对于每 ...
- 剑指Offer:面试题17——合并两个排序的链表
题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 思路1: 分别用p1,p2两个指针扫描两个有序链表,p3指针去构建新链表h3. p1.val & ...
- 【剑指offer 面试题17】合并两个排序的链表
思路: 比较两个链表端点值的大小,通过递归的方式排列. #include <iostream> using namespace std; struct ListNode { int val ...
- 剑指offer面试题17:合并两个排序的链表
题目:输入两个递增排序的链表,合并这两个链表并使新链表中的节点人是按照递增排序的.解题思路:两个链表分别都已经是有序的了,遍历链表的时候只要比较两个链表当前位置大小,取出最小的添加到新链表中. 可以有 ...
- 剑指offer——面试题17:打印从1到最大的n位数
用字符串模拟加法: #include"iostream" #include"string.h" using namespace std; bool AddOne ...
- 【剑指offer】Q18:树的子结构
类似于字符串的匹配,我们总是找到第一个匹配的字符,在继续比較以后的字符是否所有同样,假设匹配串的第一个字符与模式串的第一个不同样,我们就去查看匹配串的下一个字符是否与模式串的第一个同样,相应到这里,就 ...
- C++版 - 剑指Offer 面试题39:二叉树的深度(高度)(二叉树深度优先遍历dfs的应用) 题解
剑指Offer 面试题39:二叉树的深度(高度) 题目:输入一棵二叉树的根结点,求该树的深度.从根结点到叶结点依次经过的结点(含根.叶结点)形成树的一条路径,最长路径的长度为树的深度.例如:输入二叉树 ...
- C++版 - 剑指offer 面试题24:二叉搜索树BST的后序遍历序列(的判断) 题解
剑指offer 面试题24:二叉搜索树的后序遍历序列(的判断) 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true.否则返回false.假设输入的数组的任意两个 ...
- C++版 - 剑指Offer 面试题45:圆圈中最后剩下的数字(约瑟夫环问题,ZOJ 1088:System Overload类似)题解
剑指Offer 面试题45:圆圈中最后剩下的数字(约瑟夫环问题) 原书题目:0, 1, - , n-1 这n个数字排成一个圈圈,从数字0开始每次从圆圏里删除第m个数字.求出这个圈圈里剩下的最后一个数字 ...
随机推荐
- HP-SOCKET TCP/UDP通信框架库解析
项目概述: HP-SOCKET是一套通用TCP/UDP通信框架,包括服务器.客户端.Agent组件:其目标是提供高性能.通用性.简易性.可扩展.可定制: 鉴于此,其仅实现基本的通用框架通信.数据收发功 ...
- SQL Server 系统数据库
Sql Server的系统数据库分为:master.model.msdb,resouce和tempdb,这五个数据库在SQL Server中各司其职,作为研发人员,很有必要了解这几个数据库的职责,下面 ...
- Officel常用操作
Excel: 1.隔行变色|菜单->条件格式->其它规则->使用公式->"=MOD(ROW(),2)=0" 2.查找包含特定字符的单元格,并替换整个单元格 ...
- C# 通过模拟http请求来调用soap、wsdl
C#调用webservice的方法很多,我说的这种通过http请求模拟来调用的方式是为了解决C#调用java的远程API出现各种不兼容问题. 由于远程API不在我们的控制下,我们只能修改本地的调用代码 ...
- F#之旅1 - Why use F#?为什么要用F#?
原文地址:http://fsharpforfunandprofit.com/why-use-fsharp/ Why use F#?Why you should consider using F# fo ...
- CozyRSS开发记录18-番外之Atom1.0的支持
CozyRSS开发记录18-番外之Atom1.0的支持 1.对CozyRSS.Syndication批判一番 由于我工作的主要开发语言是c++,所以会看到我的c#代码写得非常朴素,很多语法糖都没有用上 ...
- MMU工作原理
MMU的工作原理就是把虚拟地址转换成物理地址. 虚拟地址:由编译器和连接器在定位程序时分配. 物理地址:用来访问实际的主存硬件模块. 使用虚拟存储器的系统都使用一种称为分页(paging).虚拟地址空 ...
- 学习微信小程序之css7
盒模型 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8 ...
- Trigger和ViewStateManager的具体比较
ViewStateManager的好处 拥有 GeneratedDuration ,可以很方便的进行几个状态之间的切换过渡动画. 坏处是,在界面加载时只能显示默认效果,通过GoToStateActi ...
- UVA - 11604 General Sultan 题解
题目大意: 有若干模式串,将某些模式串拼接起来(一个可以使用多次)形成一个长模式串,判断能否有两种或更多种不同的拼法拼成相同的模式串. 思路: 神奇的构图,暴力的求解. 可以发现,若有不同的拼法,则一 ...
