思考:如何才能确定一棵树?
结论: 通过中序遍历和先序遍历可以确定一个树
通过中序遍历和后续遍历可以确定一个树
通过先序遍历和后序遍历确定不了一个树。
单独先序遍历:能求解根,但不能求解左子树什么时候结束、右子树什么时候开始。
根据先序和中序结果画树
算法1、通过先序遍历找到根结点A,再通过A在中序遍历的位置找出左子树,右子树
2、在A的左子树中,找左子树的根结点(在先序中找),转步骤1
3、在A的右子树中,找右子树的根结点(在先序中找),转步骤1
根据如下遍历结果创建二叉树:
先序遍历结果:ABDHKECFIGJ
中序遍历结果:HKDBEAIFCGJ
代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
typedef struct Node{
Node *lchild;
Node *rchild;
char c;
}Node,*Tree;//静态内存分配数组
//int loc;
//对原始代码进行了改进!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
char str1[30];
char str2[30];
Node *createNode(){
// Tree[loc].lchild = Tree[loc].rchild=NULL;
// return &Tree[loc++];
Tree node = (Tree)malloc(sizeof(Node));
if (node) {
node->lchild=node->rchild=NULL;
return node;
}else{
printf("无法分配节点");
exit(0);
}
}
//后序遍历算法
void postOrder(Node *T){
if (T->lchild!=NULL) {
postOrder(T->lchild);
}
if (T->rchild!=NULL) {
postOrder(T->rchild);
}
printf("%c",T->c); //这里要用“%c”,因为就输出一个字符, 不能用“%s”, 它是输出字符串
}
//先序遍历
void preOrder(Tree T){
printf("%c",T->c); //这里要用“%c”,因为就输出一个字符, 不能用“%s”, 它是输出字符串
if (T->lchild!=NULL) {
preOrder(T->lchild);
}
if (T->rchild!=NULL) {
preOrder(T->rchild);
}
}
//中序遍历
void midOrder(Tree T){
if (T->lchild!=NULL) {
midOrder(T->lchild);
}
printf("%c",T->c); //这里要用“%c”,因为就输出一个字符, 不能用“%s”, 它是输出字符串
if (T->rchild!=NULL) {
midOrder(T->rchild);
}
}
Node * build(int s1,int e1,int s2,int e2){
//根据前序遍历序列str1和中序遍历序列str2构建树,并返回树的根节点,
Node * ret = createNode();//构建根节点,
ret->c = str1[s1];
int rootIdx;
for (int i = s2; i<=e2;i++ ) {
if (str2[i]==str1[s1]) {
rootIdx = i;//在中序遍历序列中找到根节点的下标;
break;
}
}
if(rootIdx!=s2){//说明左子树不为空
ret->lchild = build(s1+1, s1+(rootIdx-s2), s2, rootIdx-1);
}
if (rootIdx!=e2) {//说明右子树不为空
ret->rchild=build(s1+(rootIdx-s2)+1, e1, rootIdx+1, e2);
}
return ret;
}
//建立一颗二叉树,前序
Node *buildTree(){
//输入字符建立二叉树,遇到#便设置这个节点为空
Node *root =(Tree)malloc(sizeof(Node));
char a;
scanf("%c",&a);
if (a=='#') {
root = NULL;
}else{
root->c = a;
root->lchild = buildTree();
root->rchild = buildTree();
}
return root;
}
//求树的高度
int high_Tree(Tree T){
if (T==NULL) {
return 0;
}else{
int lh = high_Tree(T->lchild);
int rh = high_Tree(T->rchild);
return lh>rh?lh+1:rh+1;
}
}
int main() {
while(scanf("%s",str1)!=EOF){
scanf("%s",str2);
int L1 = strlen(str1);
int L2 = strlen(str2);
Node * T = build(0, L1-1, 0, L2-1);
postOrder(T);
printf("%d\n",high_Tree(T));
printf("\n");
}
return 0;
}
源代码二:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;;
const int N=31;
typedef struct BitNode
{
char value;
BitNode *lchild,*rchild;
}BitNode,*BiTree;
/* *&代表什么? //https://zhidao.baidu.com/question/2266744263935050308.html
这是C++的语法写法,&在形参中表示“引用”实参,
LNode * &lst ; 中LNode * 是个整体,表示变量类型是LNode类指针, &lst中的&表明引用实参,即代表实参的一个别名。
标准C是不支持这种写法的。
追问
&不是取地址符吗? 引用参数是什么意思
追答
&在变量定义区,表示引用,要注意它的用法,
&在变量操作区,表示取地址符,如:
int x=10, *p=&x ; //这里&作用在x上, 是取地址符
int &x ; //引用是C++引入的一个新特性,你要学的不是C++,则上述代码你是搞不懂的。 这里的&就表示引用。 一般这种形式会在形参中出现。
LNode * &lst ; 中LNode * 是个整体,表示变量类型是LNode类指针, &lst中的&表明引用实参,即代表实参的一个别名。 操作引用变量就相当于操作实参变量
*/
void CreatTree(BitNode* &root,char *pre,int l1,int r1,char *in,int l2,int r2)
{
if(l1<=r1&&l2<=r2)
{
int key=pre[l1];
int midIndex=-1;
for(int i=l2;i<=r2;i++)
{
if(in[i]==key)
{
midIndex=i;
break;
}
}
root=(BitNode *)malloc(sizeof(BitNode));
root->value=key;
root->lchild=NULL;
root->rchild=NULL;
int llen=midIndex-l2;
CreatTree(root->lchild, pre, l1+1, l1+llen, in, l2, midIndex-1);
CreatTree(root->rchild, pre, l1+llen+1, r1, in, midIndex+1, r2);
}
}
void postOrderTraverse(BitNode *&root)
{
if(root->lchild)
postOrderTraverse(root->lchild);
if(root->rchild)
postOrderTraverse(root->rchild);
printf("%c",root->value);
}
int main()
{
char pre[N],in[N];
while(scanf("%s",pre)!=EOF)
{
scanf("%s",in);
int len1=strlen(pre);
int len2=strlen(in);
BitNode *root=NULL;
CreatTree(root,pre,0,len1-1,in,0,len2-1);
postOrderTraverse(root);
printf("\n");
}
return 0;
}