Programe_Of_Beauty :3.9 重建二叉树

本文介绍了一种通过前序和中序遍历结果重建二叉树的算法,并提供了详细的实现步骤及示例代码。此外,还讨论了内存管理中的一些常见问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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右子树中的节点

重建后的图如下:

绘图1

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.youkuaiyun.com/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的左孩子。

转载于:https://www.cnblogs.com/cluster/archive/2011/05/29/2062127.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值