四則運算(挑戰出題)解答之輪子哥版-1


四則運算(挑戰出題)解答之輪子哥版-1

先看看題目:結對編程-四則運算(挑戰出題)

下一篇:四則運算(挑戰出題)解答之輪子哥版-2

以下思路來自輪子哥:

故事

6月11日凌晨1點半左右(北京時間),輪子哥就上面的四則運算(挑戰出題)題目開始熱血貢獻思路,本來有點睡不着,這下倒好,我腦子也開始轉起來了。經過半小時的文字迭代,輪子哥確定思路,說第二天(對我來說是當天)還記得就實現一個demo(我一定會記得提醒!😄)。於是...,我們先說說思路是啥。

思路

  1. 直接產生后綴表達式
  2. 歸一化表達式(相同題目的后綴表達式一定一樣)
  3. 通過直接比較歸一化的后綴表達式字符串來去重
  4. 后綴轉中綴輸出

整個過程中我們依然有表達式樹的概念,只是各個節點都是存放字符串中的各個字符。

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,下一次避免產生/
  • 如何從任意一個運算符開始,搜索歸屬於該運算符的完整表達式?
    • 參考思路:
      • 從右往左開始搜索
      • 標記開始
      • 操作數操作符分別計數
      • 操作數個數比操作符個數多1的地方停止
      • 標記結束

參考

附輪子哥的代碼(部分信息未在博客中解讀,后續更新):

https://gist.github.com/vczh/2c058aed996effc0a519ed3d265a3eb5

下一篇:四則運算(挑戰出題)解答之輪子哥版-2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM