程序员面试金典: 9.4树与图 4.8判断一棵树是否是另一棵树的子树

来源:互联网 发布:软件项目管理案例 编辑:程序博客网 时间:2024/05/28 11:29
#include <iostream>#include <stdio.h>#include <string>#include <vector>#include <sstream>using namespace std;/*问题:你有两颗非常大的二叉树:T1,有几百万个结点;T2,有几百个结点。设计一个算法,判断T2是否为T2的子树。      如果T1有这么一个结点n,其子树与T2一模一样,则T2位T1的子树。也就是说,从结点n处把树砍断,得到的树  与T2完全相同。分析:题目不断强调树的结点个数非常多,可能是想告诉我们只允许用一种O(N)的解法,那么对于暴力解法,就是遍历      每个结点,对每个结点判断是否为子树,时间复杂度为O(NM),其中N为T1的结点个数,M为T2的结点个数。  判断是否为子树,如果当前两个树的根节点不同,则直接返回false,否则判断两个树根节点的左右孩子结点是否  为子树。输入:2(树的数目)7(第一棵树的结点个数n)6 3 9 1 4 2 5(第一棵树的所有结点的值,接下来共有n行,每一行表示当前第i个结点对应孩子结点的下标)d(表示有两个孩子结点,后面跟两个孩子结点的下标) 2 3d 4 5 z(无孩子结点)r(只有右孩子结点,后跟右孩子结点下标) 6r 7zz1(第二棵树的结点个数)9z276 3 9 1 4 2 5d 2 3d 4 5 zr 6r 7zz43 1 4 2d 2 3r 4 zz276 3 9 1 4 2 5d 2 3d 4 5 zr 6r 7zz53 1 4 2 5d 2 3r 4r 5zz输出:yes noyes关键:1 两种解法:方法1:书上利用两棵树如果:前序+中序都相等(注意,前序和中序,如果孩子结点为空,需要打印值,否则出现无法区分错误)如下图:   3 和 3  3       3时间复杂度:O(n+m),空间复杂度O(n+m)方法2:递归遍历比较,时间复杂度O(nm),空间复杂度O( log(n)+log(m) )牛逼,没想到用前序和中序字符串比较来确定2建树,这里创建结点需要封装为一个指针函数(是一个函数) ,TreeNode* (*pFun)错:指针函数是函数,只不过返回值为指针比如, float* getFloat()而函数指针是指针,该指针指向一个函数,写法,就是把函数名替换为 "*" 加上"函数名" 加上括号的方式int printFunc(int value){     cout<<"this is a print function. the value is:"<<value<<endl;     return 0; }int (*pFunction)(int x);pFunction = printFunc;(*pFunction)(7);void buildTree(vector<int>& vecData , void (*pFun)(int , int, string& , int , int) )3 需要加上大循环bool judgeSubTree(TreeNode* head1 , TreeNode* head2){//漏掉一种情况,空树一定是子树if(NULL == head2){return true;}*/typedef struct TreeNode{int _value;TreeNode* _pLeft;TreeNode* _pRight;TreeNode* _pParent;}TreeNode;const int MAXSIZE = 1000000;int g_index1;int g_index2;TreeNode g_treeNodeArray1[MAXSIZE];TreeNode g_treeNodeArray2[MAXSIZE];TreeNode* createTree1Node(){++g_index1;g_treeNodeArray1[g_index1]._pLeft = g_treeNodeArray1[g_index1]._pRight = g_treeNodeArray1[g_index1]._pParent = NULL;return &g_treeNodeArray1[g_index1];}TreeNode* createTree2Node(){++g_index2;g_treeNodeArray2[g_index2]._pLeft = g_treeNodeArray2[g_index2]._pRight = g_treeNodeArray2[g_index2]._pParent = NULL;return &g_treeNodeArray2[g_index2];}void buildNode1(int index ,int value , string& childFlag, int leftIndex , int rightIndex){g_treeNodeArray1[index]._value = value;if("d" == childFlag){g_treeNodeArray1[index]._pLeft = &g_treeNodeArray1[leftIndex];g_treeNodeArray1[leftIndex]._pParent = &g_treeNodeArray1[index];g_treeNodeArray1[index]._pRight = &g_treeNodeArray1[rightIndex];g_treeNodeArray1[rightIndex]._pParent = &g_treeNodeArray1[index];}else if("l" == childFlag){g_treeNodeArray1[index]._pLeft = &g_treeNodeArray1[leftIndex];g_treeNodeArray1[leftIndex]._pParent = &g_treeNodeArray1[index];}else if("r" == childFlag){g_treeNodeArray1[index]._pRight = &g_treeNodeArray1[rightIndex];g_treeNodeArray1[rightIndex]._pParent = &g_treeNodeArray1[index];}}void buildNode2(int index ,int value , string& childFlag, int leftIndex , int rightIndex){g_treeNodeArray2[index]._value = value;if("d" == childFlag){g_treeNodeArray2[index]._pLeft = &g_treeNodeArray2[leftIndex];g_treeNodeArray2[leftIndex]._pParent = &g_treeNodeArray2[index];g_treeNodeArray2[index]._pRight = &g_treeNodeArray2[rightIndex];g_treeNodeArray2[rightIndex]._pParent = &g_treeNodeArray2[index];}else if("l" == childFlag){g_treeNodeArray2[index]._pLeft = &g_treeNodeArray2[leftIndex];g_treeNodeArray2[leftIndex]._pParent = &g_treeNodeArray2[index];}else if("r" == childFlag){g_treeNodeArray2[index]._pRight = &g_treeNodeArray2[rightIndex];g_treeNodeArray2[rightIndex]._pParent = &g_treeNodeArray2[index];}}/*建树,这里创建结点需要封装为一个指针函数(是一个函数) ,TreeNode* (*pFun)错:指针函数是函数,只不过返回值为指针比如, float* getFloat()而函数指针是指针,该指针指向一个函数,写法,就是把函数名替换为 "*" 加上"函数名" 加上括号的方式int printFunc(int value){     cout<<"this is a print function. the value is:"<<value<<endl;     return 0; }int (*pFunction)(int x);pFunction = printFunc;(*pFunction)(7);*/void buildTree(vector<int>& vecData , void (*pFun)(int , int, string& , int , int) ){if(vecData.empty()){return;}int size = vecData.size();string childFlag;int leftIndex = 0;int rightIndex = 0;for(int i = 1 ; i <= size; i++){int value = vecData.at(i-1);cin >> childFlag;if("d" == childFlag){cin >> leftIndex >> rightIndex;pFun(i , value , childFlag , leftIndex, rightIndex);//创建结点}else if("l" == childFlag){cin >> leftIndex;pFun(i , value , childFlag , leftIndex, rightIndex);//创建结点}else if("r" == childFlag){cin >> rightIndex;pFun(i , value , childFlag , leftIndex, rightIndex);//创建结点}//别忘记赋值else if("z" == childFlag){pFun(i , value , childFlag , leftIndex, rightIndex);//创建结点}}}//判断两颗树中,head2为首结点的树是否为以head1为树顶结点的树的子树bool isSubTree(TreeNode* head1 , TreeNode* head2){//如果两个都为空,肯定是if(NULL == head1 && NULL == head2){return true;}//如果一棵树根节点不空,令一个为空,必定不是;else if(NULL == head1){return false;}else if(NULL == head2){return false;}//两个结点都不空,判断值是否相等else{if(head1->_value != head2->_value){return false;}else{return isSubTree(head1->_pLeft , head2->_pLeft) && isSubTree(head1->_pRight , head2->_pRight);}}}//前序遍历:根左右void preOrderVisit(TreeNode* head, stringstream& ss){//根节点为空,打印NULLif(NULL == head){ss << "NULL";return;}else{ss << head->_value;}//递归访问左孩子,对是否为空需要做判断,如果孩子为空,直接打印#if(head->_pLeft){preOrderVisit(head->_pLeft , ss);}else{ss << "#";}if(head->_pRight){preOrderVisit(head->_pRight , ss);}else{ss << "#";}}//中序遍历,根节点在中间:左根右,这些打印的字符串必须放在字符串流中void middleOrderVisit(TreeNode* head, stringstream& ss){if(NULL == head){ss << "NULL";return;}//先遍历左孩子if(head->_pLeft){middleOrderVisit(head->_pLeft ,ss);}else{ss << "#";}if(head){ss << head->_value ;}if(head->_pRight){middleOrderVisit(head->_pRight , ss);}else{ss << "#";}}//无需担心子树中除了缺少某个结点而其他与另一棵树一样会被误认为是子树,因为缺少的结点对应打印#,会和不缺少结点的树不一样bool judgeSubTree_orderVisit(TreeNode* head1 , TreeNode* head2){stringstream streamPreOrder1;stringstream streamPreOrder2;stringstream streamMiddleOrder1;stringstream streamMiddleOrder2;TreeNode* tempNode1 = head1;TreeNode* tempNode2 = head2;preOrderVisit(tempNode1 , streamPreOrder1);preOrderVisit(tempNode2 , streamPreOrder2);tempNode1 = head1;tempNode2 = head2;middleOrderVisit(tempNode1 , streamMiddleOrder1);middleOrderVisit(tempNode2 , streamMiddleOrder2);string sPreOrder1 = streamPreOrder1.str();string sPreOrder2 = streamPreOrder2.str();string sMiddleOrder1 = streamMiddleOrder1.str();string sMiddleOrder2 = streamMiddleOrder2.str();if(string::npos == sPreOrder1.find(sPreOrder2)){return false;}if(string::npos == sMiddleOrder1.find(sMiddleOrder2)){return false;}return true;}//需要加上大循环bool judgeSubTree_recursion(TreeNode* head1 , TreeNode* head2){//漏掉一种情况,空树一定是子树if(NULL == head2){return true;}bool isSub = isSubTree(head1 , head2);if(isSub){return true;}else{bool isLeftSub = isSubTree(head1->_pLeft , head2);if(isLeftSub){return true;}bool isRightSub = isSubTree(head1->_pRight , head2);if(isRightSub){return true;}}return false;}TreeNode* findRoot(TreeNode* head){if(NULL == head){return NULL;}if(NULL == head->_pParent){return head;}else{return findRoot(head->_pParent);}}void process(){int treeNum;int nodeNum;int value;vector<int> vecData;/*void (*pBuildNode1)(int , int, string& , int , int);void (*pBuildNode2)(int , int, string& , int , int);pBuildNode1 = buildNode1;pBuildNode2 = buildNode2;*/while(cin >> treeNum){g_index1 = 0;g_index2 = 0;for(int i = 0 ; i < treeNum ; i++){cin >> nodeNum;vecData.clear();for(int j = 0 ; j < nodeNum ; j++){cin >> value;vecData.push_back(value);}if(i == 0){buildTree(vecData , buildNode1);}else if(i == 1){buildTree(vecData , buildNode2);}}//树构建好了,下面是判断子树TreeNode* head1 = findRoot(&g_treeNodeArray1[1]);TreeNode* head2 = findRoot(&g_treeNodeArray2[1]);//bool isSub = judgeSubTree_recursion(head1 , head2);bool isSub = judgeSubTree_orderVisit(head1 , head2);if(isSub){cout << "yes" << endl;}else{cout << "no" << endl;}}}int main(int argc, char* argv[]){process();getchar();return 0;}

0 0
原创粉丝点击