二叉树的创建(前序中序与中序后序)
0.引言1.单独中序序列建树不唯一2.'前+中','中+后' 建树3.代码实现4.完整测试代码5.指针的引用
0.引言
大话数据结构书中讲,将前序递归建树中的顺序变一下就是中序,后序,但是当我测试的时候无法成功,书中有错:
结论放前面,常用的几种建树方式:
1.前序序列可以建树,可能不唯一,但把空节点标注出来可以唯一。2.前序遍历序列和中序遍历序列,可以唯一建树。3.后序遍历序列和中序遍历序列,可以唯一建树。4.层序遍历+三种组合。
1.单独中序序列建树不唯一
前序建树不唯一,但是只要把空节点标出来,它就能构建出唯一树。这也是一些算法书上使用#来标注空节点的原因。为什么不唯一,可以参考这篇博客.
preorder: 1 2 3 4; postorder:2 4 3 1.两种序列不加空节点标志都可以建出case1、case2的情形,而且遍历出来结果也是正确的:
该图也来自参考博客,但是感觉后面没有讲清楚,可不看了。
2.‘前+中’,‘中+后’ 建树
这里参考这篇博客,讲得十分的详细。语言总结一下就是,前序序列和后序序列的根节点在最前和最后,能够知道它的位置;而中序序列无法确定哪个是根节点,所以建树不唯一。知道根节点后,从中序序列就可以知道哪部分是左儿子哪部分是右儿子了。
3.代码实现
就以上述参考博客为例子进行实现,实现过程我也画了一遍:
两种建树函数,对照着图看逻辑很清晰了!
void InPostCraeteBiTree(BiTree
*bt
, char *Inorder
, char *Postorder
,int n
)
{
if (n
== 0)
{
*bt
= NULL;
return;
}
(*bt
) = (BiTree
)malloc(sizeof(BiTreeNode
));
if (!*bt
)
exit(OVERFLOW
);
(*bt
)->Element
= Postorder
[n
- 1];
int i
;
for (i
= 0; i
< n
; i
++)
if (Inorder
[i
] == Postorder
[n
- 1])
break;
InPostCraeteBiTree(&(*bt
)->Left
, Inorder
, Postorder
,i
);
InPostCraeteBiTree(&(*bt
)->Right
,Inorder
+ i
+ 1, Postorder
+ i
, n
- i
- 1);
}
void PreInCraeteBiTree(BiTree
*bt
, char *Preorder
, char *Inorder
, int n
)
{
if (n
== 0)
{
*bt
= NULL;
return;
}
(*bt
) = (BiTree
)malloc(sizeof(BiTreeNode
));
if (!*bt
)
exit(OVERFLOW
);
(*bt
)->Element
= Preorder
[0];
int i
;
for (i
= 0; i
< n
; i
++)
if (Inorder
[i
] == Preorder
[0])
break;
PreInCraeteBiTree(&(*bt
)->Left
, Preorder
+ 1, Inorder
, i
);
PreInCraeteBiTree(&(*bt
)->Right
, Preorder
+ i
+ 1, Inorder
+ i
+ 1, n
- i
- 1);
}
4.完整测试代码
#include <iostream>
using namespace std
;
typedef char ElementType
;
typedef struct BiTreeNode
*PtrToTree
;
typedef PtrToTree BiTree
;
struct BiTreeNode
{
ElementType Element
;
BiTree Left
;
BiTree Right
;
};
void PreorderCraeteBiTree(BiTree
*bt
,char* &Preorder
)
{
if (*Preorder
== '#')
{
*bt
= NULL;
return;
}
else
{
*bt
= (BiTree
)malloc(sizeof(BiTreeNode
));
if (!*bt
)
exit(OVERFLOW
);
(*bt
)->Element
= *Preorder
;
PreorderCraeteBiTree(&(*bt
)->Left
,++Preorder
);
PreorderCraeteBiTree(&(*bt
)->Right
,++Preorder
);
}
}
void InPostCraeteBiTree(BiTree
*bt
, char *Inorder
, char *Postorder
,int n
)
{
if (n
== 0)
{
*bt
= NULL;
return;
}
(*bt
) = (BiTree
)malloc(sizeof(BiTreeNode
));
if (!*bt
)
exit(OVERFLOW
);
(*bt
)->Element
= Postorder
[n
- 1];
int i
;
for (i
= 0; i
< n
; i
++)
if (Inorder
[i
] == Postorder
[n
- 1])
break;
InPostCraeteBiTree(&(*bt
)->Left
, Inorder
, Postorder
,i
);
InPostCraeteBiTree(&(*bt
)->Right
,Inorder
+ i
+ 1, Postorder
+ i
, n
- i
- 1);
}
void PreInCraeteBiTree(BiTree
*bt
, char *Preorder
, char *Inorder
, int n
)
{
if (n
== 0)
{
*bt
= NULL;
return;
}
(*bt
) = (BiTree
)malloc(sizeof(BiTreeNode
));
if (!*bt
)
exit(OVERFLOW
);
(*bt
)->Element
= Preorder
[0];
int i
;
for (i
= 0; i
< n
; i
++)
if (Inorder
[i
] == Preorder
[0])
break;
PreInCraeteBiTree(&(*bt
)->Left
, Preorder
+ 1, Inorder
, i
);
PreInCraeteBiTree(&(*bt
)->Right
, Preorder
+ i
+ 1, Inorder
+ i
+ 1, n
- i
- 1);
}
void PreorderTraversal(BiTree
*bt
)
{
if (*bt
== NULL)
return;
cout
<< (*bt
)->Element
;
PreorderTraversal(&(*bt
)->Left
);
PreorderTraversal(&(*bt
)->Right
);
}
void InorderTraversal(BiTree
*bt
)
{
if (*bt
== NULL)
return;
InorderTraversal(&(*bt
)->Left
);
cout
<< (*bt
)->Element
;
InorderTraversal(&(*bt
)->Right
);
}
void PostorderTraversal(BiTree
*bt
)
{
if (*bt
== NULL)
return;
PostorderTraversal(&(*bt
)->Left
);
PostorderTraversal(&(*bt
)->Right
);
cout
<< (*bt
)->Element
;
}
void BiTreeTest()
{
char PreorderStr
[] = "EACBDGF";
char InorderStr
[] = "ABCDEFG";
char PostOrderStr
[] = "BDCAFGE";
int N
= sizeof(PreorderStr
) / sizeof(PreorderStr
[0]);
cout
<< "Source PreorderStr: " << PreorderStr
<< endl
<< "Source InorderStr: " << InorderStr
<< endl
<< "Source PostOrderStr: " << PostOrderStr
<< endl
<<endl
;
BiTree BT
;
InPostCraeteBiTree(&BT
, InorderStr
, PostOrderStr
, N
-1);
cout
<< "Result of the tree build with Inorder + Postorder: "<<endl
;
cout
<< "PreorderTraversal: ";
PreorderTraversal(&BT
);
cout
<< endl
;
cout
<< "InorderTraversal: ";
InorderTraversal(&BT
);
cout
<< endl
;
cout
<< "PostorderTraversal: ";
PostorderTraversal(&BT
);
cout
<< endl
<<endl
<<endl
;
BiTree BT1
;
PreInCraeteBiTree(&BT1
, PreorderStr
, InorderStr
, N
- 1);
cout
<< "Result of the tree build with Preorder + Inorder : "<<endl
;
cout
<< "PreorderTraversal: ";
PreorderTraversal(&BT1
);
cout
<< endl
;
cout
<< "InorderTraversal: ";
InorderTraversal(&BT1
);
cout
<< endl
;
cout
<< "PostorderTraversal: ";
PostorderTraversal(&BT1
);
cout
<< endl
<<endl
;
char PreorderStr2
[] = "EA#CB##D##GF###";
BiTree BT2
;
char *Preorderstr
= PreorderStr2
;
char *&Preorder
= Preorderstr
;
PreorderCraeteBiTree(&BT2
, Preorder
);
cout
<< "Result of the tree build with Preorder : " << endl
;
cout
<< "PreorderTraversal: ";
PreorderTraversal(&BT2
);
cout
<< endl
;
cout
<< "InorderTraversal: ";
InorderTraversal(&BT2
);
cout
<< endl
;
cout
<< "PostorderTraversal: ";
PostorderTraversal(&BT2
);
cout
<< endl
<< endl
;
}
int main(int argc
, char** argv
)
{
BiTreeTest();
system("pause");
return 0;
}
测试结果:
5.指针的引用
指针的引用参考.
在前序建树时,参数使用指针的引用,因为要让数组一直++下去,值传递,导致只能在前几个循环。如下图所示,使用指针做形参无法完全的递归下去。特别注意这一点。
我突然发现一个问题,要是有重复的元素呢?那种粗暴的找节点方式可能行不通,需要键值对解决