前言
如標題所言,本次要實現是使用類似 “A(B(C),D(,E))” 這樣的字符串構建、輸出二叉樹的代碼,其實這種表示方式就是廣義表的表示方式,有些書上也叫括號表示法。
這種表示法的規則是:以字符來表示結點,結點後緊跟的括號表示此結點的孩子結點,例如樹 “A(B,C)”,若孩子結點後面還有孩子結點,也按照此方法遞歸表示。兄弟結點之間使用逗號分隔。
需要注意的是,如果結點A後面只有一個左孩子B,那麼表示成這樣 “A(B)”,而如果結點A後面只有一個右孩子B,則是“A(,B)”。
理解清楚概念能夠更好地幫助我們理清寫代碼的邏輯。
寫碼
首先定義樹的結點結構和一些基礎的方法:
#include <iostream>
#include <algorithm>
using namespace std;
//樹節點定義,寫了一個簡單的構造函數
struct Node
{
char data;
Node *lchild;
Node *rchild;
Node(char c = '\0') : data(c), lchild(NULL), rchild(NULL) {}
};
//銷燬二叉樹
void destroy(Node *p)
{
if (!p)
return;
destroy(p->lchild);
destroy(p->rchild);
delete p;
}
int main()
{
return 0;
}
這裏要注意的是樹的銷燬函數,必須是這種類似後序遍歷的寫法,遍歷完左右孩子再delete父節點。
從字符串構建二叉樹
直接上代碼,關鍵的地方都寫註釋裏了:
Node *createTree(string &s)
{
Node *root = NULL; //最後要返回的樹根結點
Node *cur = NULL; //當前節點
stack<Node *> stk; //輔助棧
//flag用來標記當前節點是父節點的哪個孩子,左孩子還是右孩子
int flag = 0; // 1 -> lchild, 2 -> rchild, 0 -> nothing
for (char c : s)
{
switch (c)
{
case '(': //遇到左括號意味着當前的cur結點有孩子結點,入棧並標記下一個孩子應該是此結點的左孩子
if (!cur)
cout << "error" << endl;
stk.push(cur);
flag = 1;
break;
case ')': //表示該結點的孩子已經處理完畢,出棧該結點
stk.pop();
break;
case ',': //遇到逗號表示下一個結點應是一個右孩子結點,使用flag標記
flag = 2;
break;
default: //遇到字符的情況,創建節點並判斷該節點是上一個節點的左孩子還是右孩子
cur = new Node(c);
if (!root) //根爲空,當前的cur就是根結點
root = cur;
if (flag == 1)
stk.top()->lchild = cur;
else if (flag == 2)
stk.top()->rchild = cur;
flag = 0;
break;
}
}
return root; //最後返回根結點
}
以字符串形式輸出二叉樹
代碼挺簡單的,要解釋的也都寫註釋裏了:
void print(Node *p)
{
if (!p) //p爲空直接返回
return;
cout << p->data; //能執行到這裏說明p不空,輸出p的data
if (p->lchild || p->rchild) //如果有孩子結點再處理
{
cout << "("; //有孩子結點,輸出左括號
print(p->lchild); //這裏直接遞歸,如果左孩子爲空會直接返回,不空會遞歸輸出
if (p->rchild) //有右孩子則輸出逗號並遞歸輸出右孩子的孩子
{
cout << ",";
print(p->rchild);
}
cout << ")"; //最後輸出右括號
}
}
完整代碼
加了前中後序遍歷來驗證二叉樹是否正確構建,由於遞歸輸出無法處理最後的換行,就寫了個teaverse函數處理換行
#include <iostream>
#include <string>
#include <algorithm>
#include <stack>
using namespace std;
struct Node
{
char data;
Node *lchild;
Node *rchild;
Node(char c = '\0') : data(c), lchild(NULL), rchild(NULL) {}
};
Node *createTree(string &s)
{
Node *root = NULL;
Node *cur = NULL;
stack<Node *> stk;
int flag = 0; // 1 -> lchild, 2 -> rchild, 0 -> nothing
for (char c : s)
{
switch (c)
{
case '(':
if (!cur)
cout << "error" << endl;
stk.push(cur);
flag = 1;
break;
case ')':
stk.pop();
break;
case ',':
flag = 2;
break;
default:
cur = new Node(c);
if (!root)
root = cur;
if (flag == 1)
stk.top()->lchild = cur;
else if (flag == 2)
stk.top()->rchild = cur;
flag = 0;
break;
}
}
return root;
}
void print(Node *p)
{
if (!p)
return;
cout << p->data;
if (p->lchild || p->rchild)
{
cout << "(";
print(p->lchild);
if (p->rchild)
{
cout << ",";
print(p->rchild);
}
cout << ")";
}
}
void preorder(Node *p)
{
if (!p)
return;
cout << p->data << " ";
preorder(p->lchild);
preorder(p->rchild);
}
void inorder(Node *p)
{
if (!p)
return;
inorder(p->lchild);
cout << p->data << " ";
inorder(p->rchild);
}
void postorder(Node *p)
{
if (!p)
return;
postorder(p->lchild);
postorder(p->rchild);
cout << p->data << " ";
}
void destory(Node *p)
{
if (!p)
return;
destory(p->lchild);
destory(p->rchild);
delete p;
}
void traverse(void(tFunc)(Node *p), Node *root)
{
tFunc(root);
cout << endl;
}
int main()
{
Node *root = NULL;
string s = "A(B(D,G),C(H,F))";
cout << s << endl;
root = createTree(s);
traverse(preorder, root);
traverse(inorder, root);
traverse(postorder, root);
print(root);
destory(root);
return 0;
}
完