Programe_Of_Beauty :3.9 重建二叉树
2011-05-29 15:30 x_feng 阅读(225) 评论(0) 收藏 举报1.问题定义
对于二叉树的3中遍历方法,相信大家耳熟能详。那么如果我们知道了其中的两种遍历结果,能不能把一棵二叉树重新建立起来?给定一棵二叉树,假设每个节点都用唯一的字符来表示,没有重复。结构如下:
struct _Node
{
_Node* Lchild;
_Node* Rchild;
char data;
};
假设已有了前序遍历和中序遍历的结果,希望通过一个算法来重建这棵树。
前序遍历:a, b, d, c, e, f 显然a是根节点
中序遍历:d, b, a, e, c, f a前面的d, b是a的左子树中的节点,a后面的e, c, f是a右子树中的节点
重建后的图如下:
2.分析与解法
不难发现a是前序遍历中的第一个节点,把中序遍历分成前后两个部分,分别是a的左右子树中的节点,长度分别为2,3,前序遍历中的第二个节点b又把d, b分成了前后两部分(后一部分没有节点),分别是b的左右子树中的节点,长度分别为1,0……这样递归下去,能找出每一个节点的左右子树中的节点,当节点没有左右子树的时候即为叶节点,当一个节点它的左右子树长度为1时,此节点的左右子树就一个节点,为0时,表示此节点没有左子树或右子树。编程之美上的算法如下:
#include <iostream>
using namespace std;
#define TREELEN 6
struct _Node
{
_Node* Lchild;
_Node* Rchild;
char data;
};
void ReBulid(char* pPreOrder,//当前节点的前序遍历结果
char* pInOrder, //当前节点的中序遍历结果
int nTreeLen, //当前节点为根的树长度
_Node**pRoot) //当前根节点
{
if (pPreOrder == NULL || pInOrder == NULL)//边界检查
{
return;
}
_Node* pTemp = new _Node;
pTemp->data = *pPreOrder;
pTemp->Lchild = NULL;
pTemp->Rchild = NULL;
if (*pRoot == NULL)//如果当前根节点为空,把前序遍历的第一个节点赋给它
{
*pRoot = pTemp;
}
if (nTreeLen == 1)//如果当前树长度为1,表明此节点是最后一个节点
{
return;
}
//寻找子树长度
char* pOrgInOrder = pInOrder;
char* pLeftEnd = pInOrder;
int nTempLen = 0;
//找到左子树的结尾
while(*pPreOrder != *pLeftEnd)
{
if (pPreOrder == NULL || pLeftEnd == NULL)
{
return;
}
nTempLen++;
if (nTempLen > nTreeLen)
{
break;
}
pLeftEnd++;
}
//寻找左子树长度
int nLeftLen = 0;
nLeftLen = (int)(pLeftEnd - pOrgInOrder);
//寻找右子树长度
int nRightLen = 0;
nRightLen = nTreeLen - nLeftLen - 1;
//重建左子树
if (nLeftLen > 0)
{
ReBulid(pPreOrder + 1, pInOrder, nLeftLen, &((*pRoot)->Lchild));
}
//重建右子树
if (nRightLen > 0)
{
ReBulid(pPreOrder + nLeftLen + 1, pInOrder + nLeftLen + 1, nRightLen, &((*pRoot)->Rchild));
}
};
void main()
{
char PerO[6] = {'a', 'b', 'd', 'c', 'e', 'f'};
char InO[6] = {'d', 'b', 'a', 'e', 'c', 'f'};
_Node * pRoot = NULL;
ReBulid(PerO, InO, TREELEN, &pRoot);
}
这里可能引起内存泄露。_Node * pRoot = NULL;并没有释放空间,详见:http://blog.csdn.net/flyinghearts/archive/2010/05/31/5638123.aspx
另这里给几个内存思考问题(有点肤浅,见笑):
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:程序崩溃。
因为 GetMemory 并不能传递动态内存,Test 函数中的 str 一直都是 NULL。
strcpy(str, "hello world");将使程序崩溃
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:可能是乱码。
因为 GetMemory 返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原
现的内容已经被清除,新内容不可知。
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:
(1)能够输出hello
(2)内存泄漏
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
请问运行Test 函数会有什么样的结果?
答:篡改动态内存区的内容,后果难以预
料,非常危险。因为 free(str);之后,str 成为野指针,if(str != NULL)语句不起作用。
//关于重建二叉树的方法,还有很多。我觉得有一种方法也可行:假如前序遍历的为a,b,c,那么中序遍历的结果只有5种:c,b,a | a,b,c | b,c,a | c,b,a | b,a,c。
当然前提是前序遍历的头两个节点,即a,b不能有共同的父亲。用a,b,c找出中序遍历中a,b,c分别的位置,就对应出a,b,c的树型结构。
还有一点,a是根节点,这没问题,一定要用a先找完a的左子树,然后再用a找完a的右子树,如前序:a,b,d,e,x,g,c,k,h,z , 中序为:d,b,x,g,e, (a) ,k,c,h,z
要这样操作:先a,b,d,e,x,g ->(a,b,d) ->(b,d,e)->(d,e,x)->(e,x,g)->(x,g)。然后a,c,k,h,z->(a,c,k)->(,c,k,h)->(k,h,z)->(h,z)。
*还要注意:(a,b,d)中前两个节点a,b不能有共同的父亲,否则这步操作不作!那么最后只有两个节点怎么办呢?
如:(x,g),如果在中序遍历中g在a的前面则说明g是a的右孩子,即中序中x,g反序了。否则g为a的左孩子。

浙公网安备 33010602011771号