二叉树的相关问题
//头文件定义
typedef struct BtNode
{
BtNode *lchild;
BtNode *rchild;
BtNode *pre;
char data;
}BtNode;
1、建立一棵二叉树
1> BtNode* CreateTree() //用#表示为空的孩子,这样就可以找到叶子结点了。
{
BtNode *s = NULL;
char item;
scanf("%c",&item);
if(item != '#')
{
s = BuyNode();
s->data = item;
s->lchild = CreateP();
s->rchild = CreateP();
}
return s;
}
2>BtNode* CreateTree3(char * * const str)//这里相当于,(&str)属于引用
{
BtNode *s = NULL;
if(**str != '#' && str!=NULL && *str!=NULL)
{
s = BuyNode();
s->data = **str;
s->lchild = CreateTree3(&(++*str));
s->rchild = CreateTree3(&(++*str));
}
return s;
}
根据两个遍历序列建树,两两组合。
==========
3、根据二叉树的前序遍历串和中序遍历串建树
思路就是根据二叉树的前序和中序遍历特点,前序遍历的第一个结点就是根节点,然后在中序遍历串里找这个根节点,把中序遍历串分成了两个子串(分别是左子树和右子树),继续用前序遍历的第二个结点,在中序遍历串的左子树子串里面找然后返回下标,又把中序遍历串的左子树子串分成了两个子串(分别是左子树和右子树),以此递归调用,分别生成二叉树的左右子树。
BtNode * Create1(char *ps,char *is,int n)
{
BtNode *s = NULL;
if(n > 0)
{
s = BuyNode();
s->data = ps[0];
int pos = FindIs(is,n,ps[0]);
if(pos == -1) exit(1);
s->lchild = Create1(ps+1,is,pos);
s->rchild = Create1(ps+pos+1,is+pos+1,n-pos-1);
}
return s;
}
BtNode* CreatePI(char* ps,char *is)
{
if(ps == NULL || is == NULL)
{
return NULL;
}else
{
int n = strlen(ps);
return Create1(ps,is,n);
}
}
4、根据二叉树的中序遍历串和后序遍历串建树
主要思路就是,后序遍历从后面往前找,也先是根节点,然后去中序遍历串里面找这个节点,返回下标,把中序遍历串分成两个子串(左子树串和右子树串)。
BtNode * Create2(char *is,char *ls,int n)
{
BtNode *s = NULL;
if(n > 0)
{
int pos = FindIs(is,n,ls[n-1]);
if(pos == -1) exit(1);
s = Buynode();
s->data = ls[n-1];
s->leftchild = Create2(is,ls,pos);
s->rightchild = Create2(is+pos+1,ls+pos,n-pos-1);
}
return s;
}
BtNode * CreateIL(char *is,char *ls,int n)
{
if(is == NULL || ls == NULL || n < 1)
return NULL;
else
return Create2(is,ls,n);
}
5、!!!!前序遍历和后序遍历串无法进行建树,因为没有办法找到左右子树。
2、二叉树的遍历
前、中、后序的递归版本
void PreOrder(BtNode *ptr)
{
if(ptr != NULL)
{
printf("%c ",ptr->data);
PreOrder(ptr->lchild);
PreOrder(ptr->rchild);
}
printf("\n");
}
void InOrder(BtNode *ptr)
{
if(ptr != NULL)
{
InOrder(ptr->lchild);
printf("%c ",ptr->data);
InOrder(ptr->rchild);
}
printf("\n");
}
void PastOrder(BtNode *ptr)
{
if(ptr != NULL)
{
PastOrder(ptr->lchild);
PastOrder(ptr->rchild);
printf("%c ",ptr->data);
}
printf("\n");
}
重点来了,前、中、后序的遍历的非递归版本,中心思想都是用自己的栈代替递归栈帧
前序遍历的非递归版本,先把根节点访问,访问的过程就是出栈顶元素,然后把栈顶元素打印,然后再去把左右结点入栈,再访问。
void NicePerOrder(BtNode *ptr)
{
stack<BtNode*> mystack;
BtNode *head;
mystack.push(ptr);
while(!mystack.empty())
{
head = mystack.top();
mystack.pop();
printf("%c,",head->data);
if(head->rchild!=NULL)
{
mystack.push(head->rchild);
}
if(head->lchild!=NULL)
{
mystack.push(head->lchild);
}
}
printf("\n");
}
中序遍历,一直去找最左边的叶子结点,把一路上的结点都入栈,找到后往回退(出栈),得到栈顶元素,把它的右边入栈,继续找左叶子,注意head结点的切换
void NiceInOrder(BtNode *ptr)
{
if(ptr == NULL) return ;
stack<BtNode*> mystack;
BtNode *head = ptr;
while(!mystack.empty()|| head!=NULL)
{
if(head!=NULL)
{
mystack.push(head);
head = head->lchild;
}else
{
head = mystack.top();
mystack.pop();
printf("%c,",head->data);
head = head->rchild;
}
}
}
后序遍历的非递归版本,这里提供两种方式实现
(1)两个栈,s1,s2,s1做的事情就是头结点入栈,然后出栈,入到s2里面,然后访问左子树,右子树,依次入栈,然后把依次出栈,再入s2里面,这时s2里面从底到顶的顺序就是头、右、左,然后s2的出栈顺序就是后序遍历的顺序。
void NicePastOrder(BtNode *ptr)
{
stack<BtNode*> s1,s2;
BtNode *head = ptr;
s1.push(head);
while(!s1.empty())
{
head = s1.top();
s1.pop();
s2.push(head);
if(head->lchild!=NULL)
{
s1.push(head->lchild);
}
if(head->rchild!=NULL)
{
s2.push(head->rchild);
}
}
while(!s2.empty())
{
printf("%c,",s2.top()->data);
s2.pop();
}
}
(2)一个栈实现后序遍历:
主要是访问完一个根节点的左子树后再访问右子树,要对右子树有一个标记,防止退回去的时候再进到右子树里面去。
void NicePastOrder2(BtNode *ptr)//用一个栈实现
{
stack<BtNode*> s;
BtNode* tag;
if(ptr == NULL)return;
while(!s.empty() || ptr!=NULL)
{
while(ptr!=NULL)
{
s.push(ptr);
ptr = ptr->lchild;
}
ptr = s.top();
s.pop();
if(ptr->rchild == NULL || ptr->rchild == tag)
{
printf("%c ",ptr->data);
tag = ptr;
ptr = NULL;//这句一定不能少,不然ptr就一直是最左边的叶节点;
}else
{
s.push(ptr->rchild);
ptr = ptr->rchild;
}
}
}