剑指offer面试题18

面试题18:树的子结构

题目:输入两棵二叉树A和B,判断B是不是B的子结构。二叉树结构的定义如下:

/*******二叉树结点定义*******/
struct BinaryTreeNode
{
	int				  m_nValue;
	BinaryTreeNode*   m_pLeft;
	BinaryTreeNode*   m_pRight;
};

预备知识:

二叉树的基本操作如下。

#include "stdafx.h"
#include "BinaryTree.h"

//创建树结点
BinaryTreeNode* CreateBinaryTreeNode(int value)
{
	BinaryTreeNode* pNode = new BinaryTreeNode();  //分配内存,返回头指针
	pNode->m_nValue  = value;  //结点值
	pNode->m_pLeft = NULL;   //指向左子结点
	pNode->m_pRight = NULL;  //指向右子结点

	return pNode;
}

//链接结点
void ConnectTreeNodes(BinaryTreeNode* pParent, BinaryTreeNode* pLeft, BinaryTreeNode* pRight)
{
	if(pParent != NULL)
	{
		pParent->m_pLeft = pLeft;     //左子结点
		pParent->m_pRight = pRight;   //右子结点
	}
}

//输出树的单个结点,同时也输出它的子结点
void PrintTreeNode(BinaryTreeNode* pNode)
{
	if(pNode != NULL)
	{
		printf("value of this node is: %d\n", pNode->m_nValue);

		if(pNode->m_pLeft != NULL)
			printf("value of its left child is: %d.\n", pNode->m_pLeft->m_nValue);
		else
			printf("Left child is null.\n");

		if(pNode->m_pRight !=NULL)
			printf("value is its right child is: %d.\n", pNode->m_pRight->m_nValue);
		else
			printf("right child is null.\n");
	}
	else
	{
		printf("this node is null.\n");
	}

	printf("\n");
}

//遍历树的所有结点,并输出(这里使用了前序遍历:先根结点-左子结点-右子结点)
void PrintTree(BinaryTreeNode* pRoot)
{
	PrintTreeNode(pRoot);  //先输出根结点
	
	if(pRoot !=NULL)
	{
		if(pRoot->m_pLeft != NULL)
			PrintTree(pRoot->m_pLeft);

		if(pRoot->m_pRight != NULL)
			PrintTree(pRoot->m_pRight);
	}
}

//销毁树的所有结点(这里使用了前序遍历,销毁所有的结点)
void DestroyTree(BinaryTreeNode* pRoot)
{
	if(pRoot != NULL)
	{
		BinaryTreeNode* pLeft = pRoot->m_pLeft;
		BinaryTreeNode* pRight = pRoot->m_pRight;

		delete pRoot;  //根结点
		pRoot = NULL;

		DestroyTree(pLeft);  //左子结点
		DestroyTree(pRight); //右子结点
	}
}

算法实现:

// 面试题18.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "..\BinaryTree.h"

//根结点相等的情况下执行下面这个函数
bool DoesTree1HaveTree2(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
	if(pRoot2 == NULL)
		return true;

	if(pRoot1 == NULL)
		return false;

	if(pRoot1->m_nValue != pRoot2->m_nValue)
		return false;
	
	//递归的终止条件是:达到了树A或树B的叶结点。
	return DoesTree1HaveTree2(pRoot1->m_pLeft, pRoot2->m_pLeft) && DoesTree1HaveTree2(pRoot1->m_pRight, pRoot2->m_pRight);
}

bool HasSubtree(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
	bool result = false;

	if(pRoot1 != NULL && pRoot2 != NULL)
	{
		if(pRoot1->m_nValue == pRoot2->m_nValue) //第一步:判断两棵树的根结点是否相等
			result = DoesTree1HaveTree2(pRoot1, pRoot2);

		if(!result)
			result = HasSubtree(pRoot1->m_pLeft, pRoot2);  //左子树
		if(!result)
			result = HasSubtree(pRoot1->m_pRight, pRoot2); //右子树
	}

	return result;
}

//****************测试代码************
void Test(char *testName, BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2, bool expected)
{
	if(HasSubtree(pRoot1, pRoot2) == expected)
		printf("%s passed.\n", testName);
	else
		printf("%s failed.\n", testName);
}
// 树中结点含有分叉,树B是树A的子结构
//                  8                8
//              /       \           / \
//             8         7         9   2
//           /   \
//          9     2
//               / \
//              4   7

void Test1()
{
	//创建树结点
	BinaryTreeNode* pNodeA1 = CreateBinaryTreeNode(8);
	BinaryTreeNode* pNodeA2 = CreateBinaryTreeNode(8);
	BinaryTreeNode* pNodeA3 = CreateBinaryTreeNode(7);
	BinaryTreeNode* pNodeA4 = CreateBinaryTreeNode(9);
	BinaryTreeNode* pNodeA5 = CreateBinaryTreeNode(2);
	BinaryTreeNode* pNodeA6 = CreateBinaryTreeNode(4);
	BinaryTreeNode* pNodeA7 = CreateBinaryTreeNode(7);

	//链接树结点,构成二叉树A
	ConnectTreeNodes(pNodeA1,pNodeA2, pNodeA3);
	ConnectTreeNodes(pNodeA2,pNodeA4, pNodeA5);
	ConnectTreeNodes(pNodeA5,pNodeA6, pNodeA7);

	//创建树结点
	BinaryTreeNode* pNodeB1 = CreateBinaryTreeNode(8);
	BinaryTreeNode* pNodeB2 = CreateBinaryTreeNode(9);
	BinaryTreeNode* pNodeB3 = CreateBinaryTreeNode(2);

	//链接树结点,构成二叉树B
	ConnectTreeNodes(pNodeB1, pNodeB2, pNodeB3);

	Test("Test1", pNodeA1, pNodeB1, true);

	DestroyTree(pNodeA1);
	DestroyTree(pNodeB1);	

}


int _tmain(int argc, _TCHAR* argv[])
{
	Test1();
	return 0;
}

注意事项:

(1)一定要注意边界条件的检查,即检查空指针,同时也需要设置递归调用的退出条件。在写遍历树代码的时候一定要高度警惕,每一处需要访问地址的时候都要问问自己这个地址有没有可能死NULL,如何是NULL该如何处理。

(2)在写代码前至少要用几个测试用例检测自己的程序:1.树A和树B的头结点只有一个或两个都是空指针;2.树A和树B中所有结点都只有左子结点或右子结点;3.树A和树B中含有分叉。

(3)总之我们应该采用防御性编程的方式,每次访问指针地址前都需要考虑这个指针是否可能为NULL。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值