四則運算(挑戰出題)解答之輪子哥版-1
先看看題目:結對編程-四則運算(挑戰出題)
以下思路來自輪子哥:
故事
6月11日凌晨1點半左右(北京時間),輪子哥就上面的四則運算(挑戰出題)題目開始熱血貢獻思路,本來有點睡不着,這下倒好,我腦子也開始轉起來了。經過半小時的文字迭代,輪子哥確定思路,說第二天(對我來說是當天)還記得就實現一個demo(我一定會記得提醒!😄)。於是...,我們先說說思路是啥。
思路
- 直接產生后綴表達式
- 歸一化表達式(相同題目的后綴表達式一定一樣)
- 通過直接比較歸一化的后綴表達式字符串來去重
- 后綴轉中綴輸出
整個過程中我們依然有表達式樹的概念,只是各個節點都是存放字符串中的各個字符。
1) 產生后綴表達式
我們題目里面規定數值只能是整數 1~9
(輪子哥實現的是 0~9
,但是不影響思路),運算符只能是 +
,-
,*
,/
,於是我們實現時可以簡化,一個字符就是一個操作數or操作符。
- 我們要產生合法的表達式
- 什么時候判斷結束呢?
- 如果需要產生
n
個運算符的表達式,那么表達式總長度為2*n+1
,終止條件很明確。
- 如果需要產生
- 期間如何判斷這一次1)能不能產生操作符?2)能不能產生操作數?
- 產生操作符的前提是至少有兩棵子樹。
- 操作數總個數為
n+1
個,期間產生的操作數個數不能超過限制。
下面以產生3
個操作符的表達式為例:
- 首先我們的子樹個數為
0
,需要產生操作數作為子樹,例如:2
,此時后綴表達式是2
- 此時子樹個數不足
2
,繼續產生操作數作為子樹,例如:1
,此時后綴表達式是21
- 此時子樹個數不足
tree1 | tree2 |
---|---|
2 | 1 |
- 有大於等於2棵子樹,接着我們可隨機產生
操作數
或者操作符
,比如產生出+
,此時后綴表達式是21+
,+
產生時,是一個操作符,需要選取靠近它的兩棵子樹作為左右子樹,並將它們合並成一棵樹
left | right |
---|---|
2 | 1 |
tree1 |
---|
21+ |
- 這時只有一棵子樹了,我們必須繼續產生操作數來增加新的子樹(如果再繼續產生新的操作符,表達式就不合法了),比如產生出
3
,此時后綴表達式是21+3
tree1 | tree2 |
---|---|
21+ | 3 |
- 有大於等於2棵子樹,接下來可以產生
操作數
或者操作符
,比如產生4
,此時表達式為21+34
,多一棵子樹
tree1 | tree2 | tree3 |
---|---|---|
21+ | 3 | 4 |
- 有大於等於2棵子樹,接下來可以產生
操作數
或者操作符
,比如產生-
,此時表達式為21+34-
,-
產生時,是一個操作符,需要選取靠近它的兩棵子樹作為左右子樹,並將它們合並成一棵樹
tree1 | left | right |
---|---|---|
21+ | 3 | 4 |
tree1 | tree2 |
---|---|
21+ | 34- |
- 有大於等於2棵子樹,接下來可以產生
操作數
或者操作符
,比如產生*
,此時表達式為21+34-*
,*
產生時,是一個操作符,需要選取靠近它的兩棵子樹作為左右子樹,並將它們合並成一棵樹
left | right |
---|---|
21+ | 34- |
tree1 |
---|
21+34-* |
最后結果為 21+34-*
,中綴表達式為 (2+1)*(3-4)
2) 歸一化表達式
歸一化的目的是為了去重,手段是讓相同表達式的后綴表現為相同的字符串。
如何做呢?很簡單
- 讓表達式子樹按大小排序,即小的在左邊,大的在右邊。
- 比較大小即比較左右子樹的字符串大小即可
注意:僅+
和*
有交換律,需要做排序
舉例:
- 在產生操作符
+
時,假如后綴表達式為43+
,此時左右子樹是:
left | right |
---|---|
4 | 3 |
我們對左右子樹做字符串排序,則交換為
left | right |
---|---|
3 | 4 |
再連接兩棵子樹:
tree1 |
---|
34+ |
- 在產生操作符
*
時,假如后綴表達式為34+21-*
,此時左右子樹是:
left | right |
---|---|
34+ | 21- |
我們對左右子樹做字符串排序,則交換為:
left | right |
---|---|
21- | 34+ |
再連接兩棵子樹:
tree1 |
---|
21-34+* |
3) 去重
這里就很簡單了,我們在集合也存放后綴表達式的字符串,放入集合中保證集合內沒有相同的字符串,則一定不是相同的表達式。
C++中直接往std::set<std::string>
中存放即可
4) 后綴轉中綴輸出
大把參考資料,略。
故事(續)
第二天輪子哥沒經提醒就已經開始實現了,實現過程中自曝有bug,調試,寫注釋,最后給出代碼。經測試發現有重復題目,查證后發現並不是產生后綴的問題,是在輸出中綴時括號處理不當造成。
期間輪子哥一句話我不能同意更多:
寫注釋業是一種code review的辦法,在沒有人幫你的情況下
Skills
- 如何解決除0問題?
- 參考思路:產生后綴表達式過程中記下每棵子樹的值,如果右子樹的值為0,下一次避免產生
/
- 參考思路:產生后綴表達式過程中記下每棵子樹的值,如果右子樹的值為0,下一次避免產生
- 如何從任意一個運算符開始,搜索歸屬於該運算符的完整表達式?
- 參考思路:
- 從右往左開始搜索
- 標記
開始
- 對
操作數
和操作符
分別計數 - 當
操作數
個數比操作符
個數多1的地方停止 - 標記
結束
- 參考思路:
參考
附輪子哥的代碼(部分信息未在博客中解讀,后續更新):
https://gist.github.com/vczh/2c058aed996effc0a519ed3d265a3eb5