/**********************************************************************
二叉樹的基本操作
(1)二叉樹的資料結構
(2)二叉樹的構造
(3)二叉樹遍歷 :先序,中序,後序
************************************************************************/
#include <cstdio>
#include <cstdlib>
const int MAX=20;
//二叉樹的結構 利用雙連結串列實現
typedef struct Node
{
char ch; // 資料域
struct Node *plchild; //左子樹指標
struct Node *prchild; //右子樹指標
}BiTreeNode;
//構造二叉樹
//1.按照先序遍歷法構造 利用遞迴構造
// ABD#G##EH##I##CF#J###
void CreateBiTree_PreOrder(BiTreeNode * &T) //注意這裡傳入的是指標的引用
{
char ch;
scanf("%c",&ch);
if('#'==ch)
T=NULL;
else {
T=new BiTreeNode;
if(NULL==T)
exit(-1);
T->ch=ch;
CreateBiTree_PreOrder(T->plchild); // 構造左子樹
CreateBiTree_PreOrder(T->prchild); // 構造右子樹
}
}
//2.按照帶括號的字串建立二叉樹結構
/*思路:a(b(c,d),e(f(,g),h(i)))
'(': 表明已建立的節點為雙親節點,入棧。將要建立左孩子節點,用flag=1標記
',': 表明將要建立右孩子節點。用flag=2標記
')': 表明左右孩子節點建立和連結完畢,父節點出棧。
default: 建立節點,並且賦值,判斷連結情況
*/
void CreateBiTree_ByString(BiTreeNode* &T, char str[])
{
BiTreeNode* Stack[MAX]; // 棧用於儲存父節點
int top=0,flag=0;
BiTreeNode* p=NULL; //用於臨時指標
while(*str)
{
switch(*str)
{
case '(':
Stack[top++]=p;
flag=1; //表示即將要建立的是 左孩子節點
break;
case ',':
flag=2; //表明將要建立 右孩子節點
break;
case ')':
--top; //將父節點出棧
break;
default:
{
p=new BiTreeNode;
if(NULL==p)
return ;
if(T==NULL)
T=p;
p->ch=*str;
p->plchild=NULL;
p->prchild=NULL;
switch(flag) //此時 flag要初始化 不然非法訪問
{
case 1:
Stack[top-1]->plchild=p; //將父節點與左孩子節點連結
break;
case 2:
Stack[top-1]->prchild=p; //將父節點與右孩子節點連結
break;
}
break;
}
}
++str;
}
}
// 遞迴先序遍歷
void PreOrderTraverse_Recur(BiTreeNode *T)
{
if(T)
{
printf("%2c",T->ch);
PreOrderTraverse_Recur(T->plchild);
PreOrderTraverse_Recur(T->prchild);
}
}
// 非遞迴先序遍歷
/* 思路: 先訪問父節點,列印。然後壓入棧中,然後訪問左孩子節點,訪問列印壓棧。
將左孩子一個個壓入棧中,直到NULL.然後在訪問右孩子節點。同理按前
*/
void PreOrderTraverse_NoRecur(BiTreeNode *T)
{
BiTreeNode* Stack[MAX]; //利用陣列構造棧 先進後出
int top=0;
BiTreeNode *p=T;
while( p || top >0)
{
while(p)
{
printf("%2c",p->ch); //先訪問
Stack[top++]=p; //將 父節點壓棧
p=p->plchild; //訪問左孩子節點
}
if(top>0) //top 代表棧中有多個節點
{ //開始訪問 右孩子節點
p=Stack[--top]; //如果某個父節點的左孩子節點為NULL 那麼出棧該父節點。
p=p->prchild; //然後又跳入到 訪問該右孩子的左子樹
}
}
}
// 遞迴中序遍歷
void InOrderTraverse_Recur(BiTreeNode *T)
{
if(T) //如果節點不為NULL
{
InOrderTraverse_Recur(T->plchild); //先放問 左孩子樹
printf("%2c",T->ch); //然後在 訪問根節點
InOrderTraverse_Recur(T->prchild); // 然後在訪問 右孩子樹
}
}
// 非遞迴中序遍歷
/* 思路:相比於先序而言(一直將父節點壓棧,父節點出棧的時候就是要訪問該節點的右孩子樹)
所以中序遍歷,一直壓入左孩子節點,直到NULL.在出棧的時候列印父節點值。
*/
void InorderTraverse_NoRecur(BiTreeNode *T)
{
BiTreeNode* Stack[MAX];
int top=0;
BiTreeNode *p=T;
while( p || top>0 )
{
while(p)
{
Stack[top++]=p; //直接將父節點入棧
p=p->plchild; // 然後訪問左子樹
}
if(top>0)
{
p=Stack[--top]; //將父節點處棧
printf("%2c",p->ch); //列印父節點值
p=p->prchild; //訪問右孩子子樹
}
}
}
// 遞迴後序遍歷
void AfterOrderTraverse_Recur(BiTreeNode *T)
{
if(T)
{
AfterOrderTraverse_Recur(T->plchild);
AfterOrderTraverse_Recur(T->prchild);
printf("%2c",T->ch);
}
}
// 非遞迴後序遍歷
/*思路:依舊是將父節點依次壓棧。直到左節點為NULL,此時不出棧父節點,而是直接訪問右孩子節點
同時設定q用於檢測是否已經訪問過右孩子節點。
*/
void AfterOrderTraverse_NoRecur(BiTreeNode *T)
{
BiTreeNode* Stack[MAX];
int top=0;
BiTreeNode *p=T,*q=NULL; // q用於判斷右節點是否訪問過
while(p || top>0)
{
while(p) //直到沒有左孩子節點
{
Stack[top++]=p;
p=p->plchild;
}
if(top>0)
{
p=Stack[top-1]; //不出棧 直接訪問右子樹
if(p->prchild==NULL || p->prchild == q) //沒有右節點或者右節點已經訪問過
{
printf("%2c",p->ch);
q=p;
p=NULL;
top--;
}
else //有右節點 且沒被訪問過
p=p->prchild;
}
}
}
//銷燬二叉樹
void DestoryBiTree(BiTreeNode* &T)
{
if(T)
{
if(T->plchild)
DestoryBiTree(T->plchild);
if(T->prchild)
DestoryBiTree(T->prchild);
delete T;
T=NULL;
}
}
int main()
{//ABD#G##EH##I##CF#J###
BiTreeNode *T=NULL;
CreateBiTree_PreOrder(T);
puts("遞迴先序遍歷:");
PreOrderTraverse_Recur(T);
puts("");
puts("非遞迴先序遍歷:");
PreOrderTraverse_NoRecur(T);
puts("");
puts("遞迴中序遍歷:");
InOrderTraverse_Recur(T);
puts("");
puts("非遞迴中序遍歷:");
InorderTraverse_NoRecur(T);
puts("");
puts("遞迴後序遍歷:");
AfterOrderTraverse_Recur(T);
puts("");
puts("非遞迴後序遍歷:");
AfterOrderTraverse_NoRecur(T);
puts("");
DestoryBiTree(T); //銷燬二叉樹
puts("\n--按照帶括號的字串輸入------------");
BiTreeNode *T2=NULL;
char str[]="a(b(c,d),e(f(,g),h(i)))";
CreateBiTree_ByString(T2,str);
puts("遞迴先序遍歷:");
PreOrderTraverse_Recur(T2);
puts("");
puts("非遞迴先序遍歷:");
PreOrderTraverse_NoRecur(T2);
puts("");
puts("遞迴中序遍歷:");
InOrderTraverse_Recur(T2);
puts("");
puts("非遞迴中序遍歷:");
InorderTraverse_NoRecur(T2);
puts("");
puts("遞迴後序遍歷:");
AfterOrderTraverse_Recur(T2);
puts("");
puts("非遞迴後序遍歷:");
AfterOrderTraverse_NoRecur(T2);
puts("");
//DestoryBiTree(T2); //銷燬二叉樹
}