高手请进!!!一个数据结构的算法问题!!!

fi9 2002-05-14 02:23:28
已知一棵二叉树的先序与中序序列,现想求出它的后序序列;请用程序写出。
//---------------------------------------
这是我的一个思路!大家看看行不!
void GetTree(CMyTree *pMyTree, char cCurr, char *cpFList, char *cpMList)
//二叉树,当前结点,前序,中序
{
int nPinML;//父结点在中序中的位置
int nCinML;//当前结点在中序中的位置
char cP;//父结点的值
/*
cP = pMyTree->GetNode();//求了它的值
//再由cP,cCurr和cpMList分别求出nPinML和nCinML
if (nCinML > nPinML)//当前结点是右结点
cpMList->AddRight(cP);//添加右结点
if (nCinML < nPinML)//当前结点是左结点
cpMList->AddLeft(cP);//添加左结点
else//等于;当前结点是树的根
cpMList->AddNode();//添加结点
cpFList++;
if (cpFList)//非串尾
GetTree(pMyTree, *cpFList, cpFList, cpMList );//递归
*/
}

int main(void)
{
char *cpFList;//前序
char *cpMList;//中序
CMyTree *pMyTree = new CMyTree();//二叉树
//输入字符到cpFList and cpMList
pMyTree->AddNode(*cpFList);//前序的第一个结点为根
cpFList++;//前序指向下一个结点
//由前序与中序生成树
//树的指针,前序的当前结点,前序,中序
GetTree(pMyTree, *cpFList; cpFList, cpMList);
pMyTree->GetBackList;//求出树的后序
}
//------------------------------------------------
回去又在想了一下,上面我的那个思想是在一个条件下去想的
这个条件就是:整棵树的每个结点都是唯一的
想一下没有这个条件限制的情况之下的:
如:
前序是: *++ABCD
中序是: A+B+C*D
生成的二叉树应该是:
_________*______
________/__\_____
_______+___D____
______/__\_______
_____+___C______
____/__\_________
___A___B_________
//------------------------------------------------
看看大家有什么好点的方法
//------------------------------------------------
详情请看:
http://cpp.chinaccd.net/bbs/showthread.php?s=&postid=5208#post5208
...全文
80 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
LeeMaRS 2002-06-15
  • 打赏
  • 举报
回复
偶公布了一种比较好的解法,请查看"[关注]有关已知二叉树的前序和中序,求后序问题的独特算法. "
programeer 2002-06-15
  • 打赏
  • 举报
回复
to::magicsnake(北极狐)
用归纳法可以证明!
magicsnake 2002-05-21
  • 打赏
  • 举报
回复
顺带提一个问题,能不能够论证一下有了前序和中序,得到的后序是唯一的??
magicsnake 2002-05-21
  • 打赏
  • 举报
回复
我觉得ydaye(ydaye) 的想法不错
可是有一个问题啊,要是根结点不是序列中唯一的字符,比如说序列中有4个*号,那还应该先判断在中序中哪个*才是根结点,同样的道理,在前序中有多个相同的子节点,还应该在中序中作出判断,这个判断的方法是什么?有待讨论....
fi9 2002-05-20
  • 打赏
  • 举报
回复
看来大家都在很努力地想嘛!其实现在说优化算法可能会早了点,因为我们还没有找到好的算法,如果一找到,就明白我们现在所做的优化其实是很笨的!
//====================
我也来说说我的看法,不知道大家发现在了没有:某结点有左孩子,在中序中的左边就包含有这个左孩子,这样我们可以用这个特点来判断是否正确和稍微加快一点速度。像:
前序是: *++ABCD
中序是: A+B+C*D
//====================
1."*"为根
2."*"在中序中的位置为5[以C/C++语言来说]
3."+"为第二个结点
4.在中序中的[0~4]找到第一个"+"的位置为1
5.第三个结点还是"+"
6.因为中序中表明第一个"+"还有左孩子,所以在中序的[0~0]中找"+"的位置,由于不存在,所以说明第4步错了,返回第4步
7.在中序中的[0~4]找到第二个"+"的位置为3,表明它有左孩子
8.第三个结点是"+"
9.在中序中的[0~2]找到"+"的位置为1,表明它有左孩子
10.第四个结点是"A"
11.在中序中的[0~0]找到它的位置为0,表明它是叶子,返回9,再来
12.第五个结点是"B"
13.在中序中的[2~2]找到它的位置为2,表明它是叶子,返回9,再来
14.因为9已经做完,返回上一层,到3
15.第五个结点是"C"
16.在中序中的[4~4]找到它的位置为4,表明它是叶子,返回3
17.因为3已经做完,返回上一层,到1
18.第六个结点是"D"
19.在中序中的[6~6]找到它的位置为6,表明它是叶子,返回1
20.因为1已经做完
21.完成
//=====================
大家看看行不!
ydaye 2002-05-18
  • 打赏
  • 举报
回复
抱歉,错了,更正如下:

建树函数:
1、在前序中读第一个元素,
2、查找(或继续查找)其元素在中序中的位置,这个元素将中序分为前后两部分,对应本节点的左子树和右子树,
3、分别计算左子树和右子树的元素数,这又将前序分成三部分:本节点、左子树、右子树,
3'、检查从前序中分出来的左子树中的元素是不是在从中序中分出来的左子树重全能找到,然后再看右子树,若都通过,进入第4步,否则,传到第2步,
4、若左子树不为空就用前序和中序的左子树部分建立左子树(递归),否则返回根节点指针,
5、若右子树不为空就用前序和中序的右子树部分建立右子树(递归),否则返回根节点指针。
ydaye 2002-05-18
  • 打赏
  • 举报
回复
已经有进展了,至少有了一种方法,看我于2002-5-17 17:20:52的回帖第一段。

现重新整理算法,增加第2'步,具体如下:

建树函数:
1、在前序中读第一个元素,
2、查找(或继续查找)其元素在中序中的位置,这个元素将中序分为前后两部分,对应本节点的左子树和右子树,
2'、检查从前序中分出来的左子树中的元素是不是在从中序中分出来的左子树重全能找到,然后再看右子树,若都通过,进入第3步,否则,传到第2步,
3、分别计算左子树和右子树的元素数,这又将前序分成三部分:本节点、左子树、右子树,
4、若左子树不为空就用前序和中序的左子树部分建立左子树(递归),否则返回根节点指针,
5、若右子树不为空就用前序和中序的右子树部分建立右子树(递归),否则返回根节点指针。
fi9 2002-05-18
  • 打赏
  • 举报
回复
TO:cui(蚊子王)
你说的是很不错,
但是对不起我并没有去看着你的代码
因为我希望得到的一个好的思路
有了代码并不到处都用得上
可是思路就可以
//-----------------
To:ydaye(ydaye)
你做得也不错
但是,你所说的是太偏了
不过那的得确也是个问题所在
//-------------------
To:chxr(sxl)
这个问题在书上不是那么容易找到的
其实,现在我们就在讨论着
//--------------------
问题还没有真正的解决
希望大家再继续……
kingzeus 2002-05-18
  • 打赏
  • 举报
回复
lee,我被逼睡觉,明天继续!
ydaye 2002-05-18
  • 打赏
  • 举报
回复
关于优化大家有什么想法,不成熟也请说出来,大家一起想,我现在已经不容易突破自己那个方法的禁锢了(也许是懒),若谁有新的思路说出来让我也开拓一下思维。呵呵。
kingzeus 2002-05-18
  • 打赏
  • 举报
回复
这要看你如何检查二叉树根节点在中序中的位置了,可以从左往右,或者从右往左。
还有一个问题,如果检验出来两个都可以,就都列出来,这不可以吗?我觉得应该把所以满足条件的都列出来啊!

还是讨论这么优化算法啊,如果序列很长,里面的字符重复很多,那绝对是恶梦啊!
LeeMaRS 2002-05-18
  • 打赏
  • 举报
回复
请看这个例子:
前序:++AB+CD
中序:A+B+C+D
请问它的后序是什么?

这棵树有两种还原方式(可能还不止,我今天做题做得眼花了,只数了两种,请见谅):
+
/ \
+ D
/ \
A C
\
+
\
B

或者
+
/ \
+ +
/ \ / \
A B C D

这都是合法的二叉树.
请问怎么判断?

其实现在已经把前序 中序 -> 后序的题目 加上了限制 变成了表达式的题目

个人见解.
kingzeus 2002-05-18
  • 打赏
  • 举报
回复
现在大家想法都差不多,通过递归来实现二叉树的构建,解决歧义靠不断重复验证来实现。
不过,个人意见,如果序列很长,里面的字符重复很多,那绝对是恶梦啊!
cui你也不用大段代码了,只要说一下,怎么优化算法就行了。

figlab我们意见一样,程序只是用来实现算法,检验算法的,而不应该用来描述算法。讨论算法用自然语言效果更好
chxr 2002-05-17
  • 打赏
  • 举报
回复
随便找本讲算法的书都有啊,用递归解决,有兴趣讨论
的话我们交流交流算法。请留下你的地址,谢谢。
ydaye 2002-05-17
  • 打赏
  • 举报
回复
倒也是,讲得有理,那你的程序没问题了,不错。
蚊子王 2002-05-17
  • 打赏
  • 举报
回复
to ydaye(ydaye):
你给的例子是完全是歧异的,而贴主的例子并不是歧异的。就是说你的例子就是人也没法做的;而贴主给的例子只是你提供的程序有歧义(准确说是程序得出的结果是错误的而不是歧义)而不是本身歧义,如果人来做完全不会有歧义的。
我是使用了你的程序,并且如果没有必要,我尽量不做任意修改。
对于你给的例子完全可以画出不同的树来(而贴主的只能是一棵树),我给你一个能和我程序得出结论一样的树(根据你的例子能画出的树实在太多了):

+
/ \
+ +
/ \
+ +
\ \
+ +
\
1
ydaye 2002-05-17
  • 打赏
  • 举报
回复
我看了一下,查错的步骤是不是:
先用和我的算法中一样的方法找到一个位置pos,然后再看看从前序中分出来的左子树中的元素是不是在从中序中分出来的左子树重全能找到,然后再看右子树,若都通过,则pos是正确的。

但是这个方法不是完美的(不光是速度的问题),看下面的树:
________+_________
____+_______+_____
_+____+___+____+__
_____1____________
前序:++++1+++
中序:++1+++++
正确的后序:+1++++++
用程序算出的后序:1+++++++

错误的原因我先前已经说过了,就是生成的序列应该带有数的一部分逻辑结构,而用字符串表示出来就会损失序列应含有的一部分信息,在这个情况下就是“此+非彼+”了,呵呵,因此容歧义实在是没什么必要。

一点拙见,欢迎指正。
蚊子王 2002-05-17
  • 打赏
  • 举报
回复
to FIGLAB(无花果):
我当时只是看了你的贴前面部分(没有看画的图),再想了一下算法后看ydaye(ydaye)的程序,觉得他与我的想法一样,所以判定ydaye(ydaye)是解决了。现在重新看了一下,原来是这样的,那我就对ydaye(ydaye) 的程序略加修改就可以解决你的问题了:

#include <stdio.h>
#include <alloc.h>
#include <string.h>
#include <stdlib.h>

#define FALSE 0
#define TRUE 1

typedef int BOOL;

typedef struct __tp
{
struct __tp *left;
char ch;
struct __tp *right;
}TreeElem;

char szFOrder[100],szMOrder[100];
TreeElem *root;

BOOL CreateTree(TreeElem**,char*,char*,int);
void FrontVisit(TreeElem *root);
void MiddleVisit(TreeElem *root);
void BackVisit(TreeElem *root);
BOOL checkout(char*,char*,int);

void main()
{
printf("input front-order's string:");
scanf("%s",szFOrder);
printf("input middle-order's string:");
scanf("%s",szMOrder);

int len=strlen(szFOrder);
if(len!=strlen(szMOrder))return;

if(CreateTree(&root,szFOrder,szMOrder,len))
{
FrontVisit(root);
printf("\n");
MiddleVisit(root);
printf("\n");
BackVisit(root);
}
}

BOOL CreateTree(TreeElem ** r,char* f,char* m,int len)
{
int pos;
TreeElem *root;

if((root=(TreeElem *)malloc(sizeof(TreeElem)))==NULL)
{
printf("Not Enough Memory!\n");
exit(0);
}

*r=root;
root->left=NULL;
root->right=NULL;
root->ch=f[0];

if(len==1)return TRUE;

for(pos=0;pos<len;pos++)
{
if((m[pos]==f[0])&&checkout(f+1,m,pos))
{
if(pos)
{
if(!CreateTree(&(root->left),f+1,m,pos))return FALSE;
}
if(len-pos-1)
{
if(!CreateTree(&(root->right),f+pos+1,m+pos+1,len-pos-1))return FALSE;
}
return TRUE;
}
}

return FALSE;
}

BOOL checkout(char *f,char *m,int len)
{
int i,j;
for(i=0;i<len;i++)
{
for(j=0;j<len;j++)
{
if(f[i]==m[j])break;
}
if(j>=len)return FALSE;
}
for(i=0;i<len;i++)
{
for(j=0;j<len;j++)
{
if(m[i]==f[j])break;
}
if(j>=len)return FALSE;
}
return TRUE;
}

void FrontVisit(TreeElem *root)
{
if(root==NULL)return;
printf("%c",root->ch);
FrontVisit(root->left);
FrontVisit(root->right);
}

void MiddleVisit(TreeElem *root)
{
if(root==NULL)return;
MiddleVisit(root->left);
printf("%c",root->ch);
MiddleVisit(root->right);
}

void BackVisit(TreeElem *root)
{
if(root==NULL)return;
BackVisit(root->left);
BackVisit(root->right);
printf("%c",root->ch);
}
ydaye 2002-05-17
  • 打赏
  • 举报
回复
to FIGLAB(无花果)
那个歧义的问题,在当前定义下应该是无法解决的,提供一种笨办法,不停地试:假定一定能成功并有n种歧义,建树的时候可以加入判断,不成功就试下一个。(很笨的办法)

还有,其实这个解决歧义的问题根本不是问题,也就是没有必要讨论,观点试阐述如下:
对树的遍历产生的序列本身应该隐含了树的逻辑结构,或结构的一部分,这也是重建树的先决条件,如:“*++ABCD”中的两个“+”按字符解释是等同的,除了位置上的小小不同,但是其实两者的不同不仅仅是位置上的这么一点点不同这么简单,也就是在记录遍历序列的时候损失了必要的信息,这也就是为什么讨论实现智能识别没什么意义了,完全可以在记录序列的时候就采用唯一的元素,比如ID之类的。

有人要反驳:这么说的话也就可以说考虑编译中的语法分析也没必要了,采用可以明显识别的记号就是了。

其实不是的,语法分析程序事先知道语法树,和这个问题不同。

一点拙见,欢迎指正。

对了,顺便描述我的程序的算法!
建树函数:
1、在前序中读第一个元素,
2、纪录其在中序中的位置,这个元素将中序分为前后两部分,对应本节点的左子树和右子树,
3、分别计算左子树和右子树的元素数,这又将前序分成三部分:本节点、左子树、右子树,
4、若左子树不为空就用前序和中序的左子树部分建立左子树(递归),否则返回根节点指针,
5、若右子树不为空就用前序和中序的右子树部分建立右子树(递归),否则返回根节点指针。

完毕!
蚊子王 2002-05-16
  • 打赏
  • 举报
回复
TO figlab(无花果) :
ydaye(ydaye)写的不错,他已经解决你的问题啦。
加载更多回复(2)

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧