程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 巧妙地用二叉樹完成算式計算算法<計算器,二叉樹,C++,獨辟蹊徑>,

巧妙地用二叉樹完成算式計算算法<計算器,二叉樹,C++,獨辟蹊徑>,

編輯:C++入門知識

巧妙地用二叉樹完成算式計算算法<計算器,二叉樹,C++,獨辟蹊徑>,


#01、引言,我們知道算式計算的問題是棧裡面一個非常經典的題目。但是用棧來實現是一個非常麻煩的過程,第一要解決算式判斷,是否為符合規則的算式,第二要由中最表達式轉化為後綴表達式。這兩個部分是棧實現計算算式表達式的比較復雜的地方。不僅如此,棧實現裡面的各種運算符的優先級,各種條件判斷,可以說是麻煩的要命。但是,實際上有一種數據結構比棧更適合解決這類問題。可以說是得天獨厚的優勢。對,就是二叉樹。例如一個表達式:1+2*3-4/5

我們構造這樣一個二叉樹

當構造這樣一個二叉樹之後,解決表達式的值的方法,也就浮出水面了,把2和3相乘,存到*的節點中,然後再和1相加,存到+的節點中.....最後根節點-節點中存放的就是最後的計算結果。就是葉子節點執行其雙親節點的運算,結果存到雙親節點中。 #02、選二叉樹作為算法的存儲結構有什麼好處。        這主要有兩個方面的好處,這也是針對於棧算法的兩個麻煩的地方。 <1>//免除了算式表達式的檢查過程。為什麼能免除檢查,表達式的規范性呢?並不是不需要檢查,而是檢查的過程就包含在創建二叉樹的過程。認真分析這棵二叉樹,我們會發現,所有的葉子節點必須是操作數節點,而所有的非葉子節點必須是運算符節點,否則表達式的結構一點不正確,創建二叉樹的過程就可以對表達式經行檢查。表達式是否正確也只取決於兩個方面,第一、表達式的結構是否正確,比如不能出現2*+6這樣的表達式,第二、表達式的數據是否正確,例如不能出現1+2.2.3這樣的表達式,2.2.3不是一個符合規則的數據。而數據的檢查,也可以在給葉子節點賦值的時候檢查。所以避免的單獨經行表達式檢查的繁瑣。 <2>//不需要轉化為後綴表達式再經行表達式結果的計算,這也是得益於二叉樹這種結構的天然優勢,自我感覺就完全是為這種算法題設計的,天造地設嘛! #03、算法實現        0x001、數據結構的定義:


 1 #define Maxsize 100
 2 //定義數據元素類型
 3 typedef char  elemtype;
 4 //定義二叉樹數據變量
 5 typedef union 
 6 {
 7     char Operator;
 8     double date;
 9 }perdate;
10 //定義二叉樹鏈式存儲結構
11 typedef struct node
12 {
13     perdate DATE;//用union類型存運算符或操作數
14     struct node *lchild;
15     struct node *rchild;
16 }btnode;    

 




1 struct op 
2 {
3     char opration;
4     int index;//括號層數//當這個index被標記為-1時,就不會再次被查找到
5     int locate;//op的位置
6 };

 

用union定義一個perdate類型,用來分別記錄操作數和運算符。op是查找運算符時用,從後往前查找,括號級數最低的作為根節點來創建二叉樹。

     0x002、實現的函數

 
//查找op,並填充Aop數組
int Sortop(char str[], op Aop[], int &index);
//將字符串轉化為浮點數
double str_to_flaot(char strpoly[], int p,int q);
//判斷數組是不是1.2類型,就是只有數據
bool isdate(char str[],int p,int q);//p,q指向str的開始和結尾處
//判斷str是否為運算符和括號
bool isoprater(char str[],int p,int q);//p,q指向str的開始和結尾處
//用算數表達式創建二叉樹
void Createbtnode(btnode *b, char *str, int p, int q,int tail);//p,q指向str的開始和結尾處;tail是Aop的尾指針
//計算二叉樹算式的結果
double Comp(btnode *b);

    0x003、main函數,整個算法過程簡述

#include"標頭.h"
int index = 0;//記錄最大的括號層數
struct op Aop[Maxsize];

 

 1 int main()
 2 {
 3     btnode * b;
 4     b = new btnode;
 5     char str[Maxsize];
 6     cout << "算式計算器[張安源]" << endl;
 7         while(true)   
 8         {
 9             cout << "[Type \"exit\" to exit]" << endl << "請輸入你要求的表達式:" << endl;
10            cin.getline(str, Maxsize);
11           if (strcmp("exit", str) == 0)  break;//如果輸入的是exit則退出
12           else
13           {
14               int tail = Sortop(str, Aop, index);//整理得到Aop的結構數組
15               Createbtnode(b, str, 0, strlen(str) - 1, tail);
16               double result = Comp(b);
17               cout << result << endl;
18           }
19         }
20 }

 一直循環,讓用戶輸入一個表達式,當輸入為exit時,退出循環。Sortop函數將表達式的操作符的括號層數和其在表達式的位置經行記錄到Aop數組裡面,返回值是最大的括號層數。然後由Createbtnode函數創建一個二叉樹b。comp求出二叉樹表達式的結構,然後輸出結果。大致的過程是這樣,但是裡面卻還包含了一些實現的細節,具體代碼是怎麼實現的就不啰嗦了,看代碼比講解跟方便。

      0x004、整個project。

<1>Header.h

 1 #pragma once
 2 #include<iostream>
 3 using namespace std;
 4 #define Maxsize 100
 5 //定義數據元素類型
 6 //*********int check = 0;//作為判斷表達式是否正確的標記
 7 typedef char  elemtype;
 8 //定義二叉樹數據變量
 9 typedef union 
10 {
11     char Operator;
12     double date;
13 }perdate;
14 //定義二叉樹鏈式存儲結構
15 typedef struct node
16 {
17     perdate DATE;//用union類型存運算符或操作數
18     struct node *lchild;
19     struct node *rchild;
20 }btnode;
21 //定義查找運算符的結構數組
22 struct op 
23 {
24     char opration;
25     int index;//括號層數//當這個index被標記為-1時,就不會再次被查找到
26     int locate;//op的位置
27 };
28 extern int index;
29 extern struct op Aop[Maxsize];
30 //******************************************************
31 //查找op,並填充Aop數組
32 int Sortop(char str[], op Aop[], int &index);
33 //將字符串轉化為浮點數
34 double str_to_flaot(char strpoly[], int p,int q);
35 //判斷數組是不是1.2類型,就是只有數據
36 bool isdate(char str[],int p,int q);//p,q指向str的開始和結尾處
37 //判斷str是否為運算符和括號
38 bool isoprater(char str[],int p,int q);//p,q指向str的開始和結尾處
39 //用算數表達式創建二叉樹
40 void Createbtnode(btnode *b, char *str, int p, int q,int tail);//p,q指向str的開始和結尾處;tail是Aop的尾指針
41 //計算二叉樹算式的結果
42 double Comp(btnode *b);

<2>op.cpp

  1 #include"標頭.h"
  2 //查找op,並填充Aop數組
  3 int Sortop(char str[], op Aop[], int &index)
  4 {
  5     int j = 0;//記錄Aop的top
  6     int i;
  7     int ind = 0;//記錄括號層數
  8     for (i = 0; str[i] != '\0'; i++)
  9     {
 10         if (str[i] == '(')
 11             ind++;
 12         else if (str[i] == ')')
 13             ind--;
 14         else if (str[i] == '+' || str[i] == '-' || str[i] == '*'||str[i]=='/' || str[i] == '^')
 15         {
 16             Aop[j].opration = str[i];
 17             Aop[j].index = ind;
 18             Aop[j].locate = i;
 19             j++;
 20         }
 21         index = (index > ind) ? index : ind;
 22     }
 23     return j;
 24 }
 25 //將字符串轉化為浮點數
 26 double str_to_flaot(char strpoly[], int p,int q)
 27 {
 28     if (strpoly[p] == '(')
 29         p++;
 30     if (strpoly[q] == ')')
 31         q--;
 32     //判斷小數點前有幾位數字
 33     int index = 0;
 34     int temp = p;//保存原來的p值
 35     double  n = 0;//最後的浮點數
 36     for (;( p <= q)&&(strpoly[p]!='.'); p++) index++;
 37     p = temp;
 38     for (; p<=q; p++)
 39     {
 40         if (strpoly[p] == '.')  continue;
 41         index--;
 42         n = n + ((double)(strpoly[p] - '0'))*(pow(10, index));
 43 
 44     }
 45         return n;
 46 }
 47 //判斷數組是不是1.2類型,就是只有數據//忽略括號
 48 bool isdate(char str[],int p,int q)
 49 {
 50     int i;
 51     int index = 0;
 52     for (i = p; i<=q; i++)
 53     {
 54         if (str[i] == '.')
 55             index++;
 56         if (str[i] == '+' || str[i] == '-' || str[i] == '*' ||str[i]=='/' || str[i] == '^')
 57             return false;
 58     }
 59     if (index== 0 || index == 1)
 60     {
 61         return true;
 62     }
 63     else
 64         abort();
 65 }
 66 //判斷str是否為運算符和括號
 67 bool isoprater(char str[],int p,int q)
 68 {
 69     if ((p==q)&&(str[p] == '(' || str[p] == ')' || str[p] == '*'||str[p]=='/' || str[p] == '^' || str[p] == '+' || str[p] == '-'))
 70         return true;
 71     else
 72         return false;
 73 }
 74 //用算數表達式創建二叉樹
 75 void Createbtnode(btnode *b, char *str, int p, int q,int tail)        //由str串創建二叉鏈
 76 {                                                            //p,q分別標志Aop的首尾
 77     int i = 0;
 78     int j = 0;//
 79     int find=0;
 80     if (isdate(str,p,q))//str為1.3類型
 81     {
 82         //創建頭節點,並將數據位置為str_to_double
 83         b->DATE.date = str_to_flaot(str,p,q);
 84         b->lchild = NULL;
 85         b->rchild = NULL;
 86     }
 87     else if (isoprater(str,p,q))//str為+、—、^、(、)、*
 88     {
 89         abort();
 90         b->DATE.Operator = str[i];
 91         b->lchild = NULL;
 92         b->rchild = NULL;
 93     }
 94     ///***************************************************************
 95     else
 96         for (int temp = 0; temp <= index; temp++)
 97         {
 98             for (j = tail; j >=0; j--)//從後往前找,才符合運算的法則,前面先算後面後算
 99             {
100                 if (Aop[j].index == temp && ((Aop[j].opration == '+')||(Aop[j].opration == '-')) && Aop[j].locate >= p&&Aop[j].locate <= q)
101                 {
102                     find++;
103                     Aop[j].index = -1;//標志這個已經被找過了
104                     btnode *lt, *rt;
105                     lt = new btnode;
106                     rt = new btnode;
107                     b->lchild = lt;
108                     b->rchild = rt;
109                     b->DATE.Operator = Aop[j].opration;
110                     Createbtnode(b->lchild, str, p, Aop[j].locate - 1,tail);
111                     Createbtnode(b->rchild, str, Aop[j].locate+1, q,tail);
112                 }
113             }
114         if(find==0)
115             for (j = tail; j >=0; j--)
116             {
117                 if (Aop[j].index == temp && ((Aop[j].opration == '*')||(Aop[j].opration=='/')) && Aop[j].locate >= p&&Aop[j].locate <= q)
118                 {
119                     find++;
120                     Aop[j].index = -1;//標志這個已經被找過了
121                     btnode *lt, *rt;
122                     lt = new btnode;
123                     rt = new btnode;
124                     b->lchild = lt;
125                     b->rchild = rt;
126                     b->DATE.Operator = Aop[j].opration;
127                     Createbtnode(b->lchild, str, p, Aop[j].locate - 1,tail);
128                     Createbtnode(b->rchild, str, Aop[j].locate+1, q,tail);
129                 }
130             }
131         if(find==0)
132             for (j = tail; j >=0; j--)
133             {
134                 if (Aop[j].index == temp && (Aop[j].opration == '^') && Aop[j].locate >= p&&Aop[j].locate <= q)
135                 {
136                     Aop[j].index = -1;//標志這個已經被找過了
137                     btnode *lt, *rt;
138                     lt = new btnode;
139                     rt = new btnode;
140                     b->lchild = lt;
141                     b->rchild = rt;
142                     b->DATE.Operator = Aop[j].opration;
143                     Createbtnode(b->lchild, str, p, Aop[j].locate - 1,tail);
144                     Createbtnode(b->rchild, str, Aop[j].locate+1, q,tail);
145                 }
146             }
147         }
148 }
149 //計算二叉樹算式的結果
150 double Comp(btnode *b)
151 {
152     double v1, v2;
153     if (b == NULL) return 0;
154     if (b->lchild == NULL && b->rchild == NULL)
155         return b->DATE.date;    //葉子節點直接返回節點值
156     v1 = Comp(b->lchild);
157     v2 = Comp(b->rchild);
158     switch (b->DATE.Operator)
159     {
160     case '+':
161         return v1 + v2;
162     case '-':
163         return v1 - v2;
164     case '*':
165         return v1*v2;
166     case '/':
167         if (v2 != 0)
168             return v1 / v2;
169         else
170             abort();
171     case '^':
172         return (pow(v1, v2));
173     default:
174         abort();
175     }
176 }

<3>main.cpp

 1 #include"標頭.h"
 2 int index = 0;//記錄最大的括號層數
 3 struct op Aop[Maxsize];
 4 int main()
 5 {
 6     btnode * b;
 7     b = new btnode;
 8     char str[Maxsize];
 9     cout << "算式計算器[張安源]" << endl;
10         while(true)   
11         {
12             cout << "[Type \"exit\" to exit]" << endl << "請輸入你要求的表達式:" << endl;
13            cin.getline(str, Maxsize);
14           if (strcmp("exit", str) == 0)  break;//如果輸入的是exit則退出
15           else
16           {
17               int tail = Sortop(str, Aop, index);//整理得到Aop的結構數組
18               Createbtnode(b, str, 0, strlen(str) - 1, tail);
19               double result = Comp(b);
20               cout << result << endl;
21           }
22         }
23 }

#04算法測試

                   

 當輸入的表達式符合規則時,返回表達式的值。

                 

                

當輸入的表達式不符合規則時,則調用abort函數。

#05、總結

好的數據結構能事半功倍,要培養善於發現的思維,當有某個思路然後去實現它,另外要積累經驗。好好理解數據結構!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved