重温数据结构。整理一些代码。
这一篇包括二叉树的先序建立,三种顺序递归访问,三种顺序非递归访问,力求精简。
二叉树的默认是如下形状:
#define OK true
#define ERROR false
#include<stdio.h>
#include<malloc.h>
#include<stack> //由于二叉树的非递归访问等需要一个栈来记录节点顺序,这里使用stack简单好理解
using namespace std;
typedef struct BtNode{
char data; //二叉树的节点元素
struct BtNode *lchild,*rchild; //二叉节点
}BtNode;
BtNode *CTBT_pre(BtNode *T,int &n,char list[]);//先序递归建立二叉树
//递归方式先序,中序,后序访问二叉树
void preOrder(BtNode *T);
void inOrder(BtNode *T);
void laOrder(BtNode *T);
//非递归访问声明
void preOrder_st(BtNode *T);
void inOrder_st(BtNode *T);
void laOrder_st(BtNode *T);
void main()
{
BtNode *Tree;
Tree=NULL; //初始化指针
//以下建立二叉树
char list[20]="abc de f gh ";//字符串是空表示节点到头。
int n=0; //利用字符串建立二叉树时需要辅助的参数
Tree=CTBT_pre(Tree,n,list);//利用Tree来生成二叉树
//返回Tree二叉树
laOrder_st(Tree);
}
//结点的访问函数
bool visit(BtNode *p)
{
if(p){
printf("%c ",p->data);
return OK;
}
else
return ERROR;
}
//先序建立二叉树(递归)
BtNode *CTBT_pre(BtNode *T,int &n,char list[]){
//按照先序递归生成二叉树
if(list[n]==' '){
n++;
return NULL;//如果输入出现了空,表明到了叶子结点
}
else{
if(!(T=(BtNode *)malloc(sizeof(BtNode)))) return false;//这里要验证内存是否分配成功;
T->data=list[n++];
T->lchild=CTBT_pre(T->lchild,n,list);
T->rchild=CTBT_pre(T->rchild,n,list);
return T;
}
}
//三种方式的递归访问二叉树,简单直接暴力= =
void preOrder(BtNode *T)
{//前序访问
if(T){
visit(T);
preOrder(T->lchild);
preOrder(T->rchild);
}
}
void inOrder(BtNode *T)
{//中序访问
if(T){
inOrder(T->lchild);
visit(T);
inOrder(T->rchild);
}
}
void laOrder(BtNode *T)
{//后序访问
if(T){
laOrder(T->lchild);
laOrder(T->rchild);
visit(T);
}
}
//三种方式的非递归访问二叉树
void preOrder_st(BtNode *T)//非递归先序
{
stack <BtNode*> Stack;
if(!T) return;
while(T || !Stack.empty())//注意理解这个进行循环的入口
{
while(T){
Stack.push(T);//入栈后,我们下次用的不是他,而是他的右孩子。
visit(T);
T=T->lchild;
}
T=Stack.top();//左边走到头,下一个应该是双亲的右孩子,好,把他取出来。
T=T->rchild;
Stack.pop();
}
}
void inOrder_st(BtNode *T)//非递归中序
{
stack <BtNode*> Stack;
if(!T) return;
while(T || !Stack.empty())//某种意义上,先序和中序的思路是一致的,只是visit出现的位置。
{
while(T){
Stack.push(T);
T=T->lchild;
}
T=Stack.top();
visit(T);
Stack.pop();
T=T->rchild;
}
}
void laOrder_st(BtNode *T)//非递归后序遍历
{
stack <BtNode*> Stack;
bool tag[20];//后序遍历的麻烦在于,每当访问栈里的节点时需要分清楚是从左孩子还是右孩子回来
//所以这里使用一个对应等长的标记,左孩子回来即true,右孩子回来的则是false
if(!T) return;
while(T || !Stack.empty())
{
while(T){
Stack.push(T);
tag[Stack.size()]=true;
T=T->lchild;
}
while(tag[Stack.size()]==false && !Stack.empty()){
visit(Stack.top());
Stack.pop();
//T=Stack.top();
}
if(!Stack.empty()){//来到这里的时候,左边已经访问完了
tag[Stack.size()]=false;//所以下次回来,对于这个节点来说就是两边都访问了false
T=Stack.top()->rchild;
}
}
}