【資料結構學習筆記】——根據中綴表示式構建二叉樹並輸出
要求
輸入一箇中綴表示式,構造表示式樹,以文字方式輸出樹結構。
輸入:例如,輸入a+b+c*(d+e)
輸出:以縮排表示二叉樹的層次,左(根),右(葉),上(右子樹),下(左子樹)
分析
我們有兩個核心的問題需要解決,一是如何按照中綴表示式來構建二叉樹,二是如何用這種形式進行輸出。
放碼過來
postfix.h
我們知道由字尾表示式生成二叉樹很簡單,所以我們需要做的第一步就是講中綴表示式轉換為字尾表示式。
中綴表示式轉字尾表示式的方法:
1.遇到運算元:直接輸出(新增到字尾表示式中)
2.棧為空時,遇到運算子,直接入棧
3.遇到左括號:將其入棧
4.遇到右括號:執行出棧操作,並將出棧的元素輸出,直到彈出棧的是左括號,左括號不輸出。
5.遇到其他運算子:加減乘除:彈出所有優先順序大於或者等於該運算子的棧頂元素,然後將該運算子入棧
6.最終將棧中的元素依次出棧,輸出。
string postfix(string exp)
{
char token; //當前讀取的字元
string s;
Stack<char>opStack;
string postfixExp; //目標字尾表示式
for(int i=0;i<exp.length();i++)
{
token=(char)exp[i];
switch(token)
{
case '(':
opStack.push(token); //是 ( 則放入棧中
break;
case ')':
while(opStack.top()!='(') //將與之匹配的(之前的所有元素出棧
{
s=opStack.top();
postfixExp.append(s);
opStack.pop();
}
opStack.pop();
break ;
case '+'://若棧為空 或 棧頂為( 或 當前為*/ 且棧頂為 +- (當前運算子優先順序更高) 則直接入棧
case '-':
case '*':
case '/':
if(opStack.empty()||opStack.top()=='('||(token=='*'||token=='/')&&(opStack.top()=='+'||opStack.top()=='-'))
{
opStack.push(token);
}
else //若優先順序低於棧頂元素
{
s=opStack.top();
postfixExp.append(s);
opStack.pop();
if(opStack.top()=='+'||opStack.top()=='-')
{
s=opStack.top();
postfixExp.append(s);
opStack.pop();
}
opStack.push(token);
}
break;
default: //若為運算元則直接輸出
s=token;
postfixExp.append(s);
break;
}
}
while(!opStack.empty()) //將棧中剩餘的元素依次輸出
{
s=opStack.top();
postfixExp.append(s);
opStack.pop();
}
return postfixExp;
};
binaryTree.h
字尾表示式生成二叉樹(字尾表示式沒有括號 )
1. 依次讀取表示式;
2. 如果是運算元,則將該運算元壓入棧中;
3. 如果是操作符,則彈出棧中的兩個運算元,第一個彈出的運算元作為右孩子,
第二個彈出的運算元作為左孩子;然後再將該操作符壓入棧中。
class Tnode
{
public:
char value;
Tnode *left,*right;
Tnode(){}
Tnode(const char value,Tnode *left=NULL,Tnode *right=NULL):value(value), left(left), right(right)
{}
Tnode* buildTree(string exp);//字尾表示式生成二叉樹
int depth(Tnode *root);//返回樹高
void print(Tnode *node_ptr, int depth);//轉向列印二叉樹(逆時針轉90度列印)
// void inorderOutput(Tnode *t);
};
//字尾表示式生成二叉樹
Tnode* Tnode::buildTree(string exp)
{
Tnode *ptr;
Stack<Tnode*>nodeStack;
char c;
int i=0;
c=exp[i++];
while(c!='\0')
{
if(c!='+' && c!='-' && c!='*' && c!='/')
{
ptr=new Tnode(c);
nodeStack.push(ptr);
c=exp[i++];
}
else
{
ptr=new Tnode(c);
if(!nodeStack.empty())
{
ptr->right=nodeStack.top();
nodeStack.pop();
}
if(!nodeStack.empty())
{
ptr->left=nodeStack.top();
nodeStack.pop();
}
nodeStack.push(ptr);
c=exp[i++];
}
}
return ptr;
}
//返回樹高
int Tnode::depth(Tnode *root)
{
int i,j;
if(root==NULL)
return 0;
i=depth(root->left);
j=depth(root->right);
return i>j?i+1:j+1;
}
void Tnode::print(Tnode *node_ptr, int depth)//逆時針90度列印二叉樹(直接輸出)
{
if (node_ptr!= NULL)
{
print(node_ptr->right, depth+1);
cout<<setw(3*depth)<<" ";
cout<<node_ptr->value<<endl;
print(node_ptr->left,depth+1);
}
}
說明:
一、在C++中,setw(int n)用來控制輸出間隔。
例如:
cout<<’s’<< setw(8)<< ‘a’<< endl;
則在螢幕顯示
s a
//s與a之間有7個空格,setw()只對其後面緊跟的輸出產生作用,如上例中,表示’a’共佔8個位置,不足的用空格填充。若輸入的內容超過setw()設定的長度,則按實際長度輸出。
setw()預設填充的內容為空格,可以setfill()配合使用設定其他字元填充。
如
cout<< setfill(‘*’)<< setw(5)<< ‘a’<< endl;
則輸出:
****a //4個和字元a共佔5個位置。
test.cpp
int main()
{
// string infix="a+b+c*(d+e)"; //輸入的中綴表示式
string infix; //自己輸出任意表達式
cout<<"請輸入中綴表示式"<<endl;
getline(cin,infix);
string s=postfix(infix); //s為輸出化為的字尾表示式
cout<<"字尾表示式為:"<<s<<endl;
Tnode tree(' ');
Tnode *root;
int high;
root=tree.buildTree(s);
high=tree.depth(root);
tree.print(root,high);
}
結果