色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

分治算法詳解:表達式的不同優先級

算法與數據結構 ? 來源:算法與數據結構 ? 作者:labuladong ? 2021-01-04 14:04 ? 次閱讀

我們號已經寫了 動態規劃算法,回溯(DFS)算法,BFS 算法,貪心算法,雙指針算法,滑動窗口算法,現在就差個分治算法沒寫了,今天來寫一下,集齊七顆龍珠,就能召喚神龍了~

其實,我覺得回溯、分治和動態規劃算法可以劃為一類,因為它們都會涉及遞歸。

回溯算法就一種簡單粗暴的算法技巧,說白了就是一個暴力窮舉算法,比如讓你 用回溯算法求子集、全排列、組合,你就窮舉唄,就考你會不會漏掉或者多算某些情況。

動態規劃是一類算法問題,肯定是讓你求最值的。因為動態規劃問題擁有 最優子結構,可以通過狀態轉移方程從小規模的子問題最優解推導出大規模問題的最優解。

分治算法呢,可以認為是一種算法思想,通過將原問題分解成小規模的子問題,然后根據子問題的結果構造出原問題的答案。這里有點類似動態規劃,所以說運用分治算法也需要滿足一些條件,你的原問題結果應該可以通過合并子問題結果來計算。

其實這幾個算法之間界定并沒有那么清晰,有時候回溯算法加個備忘錄似乎就成動態規劃了,而分治算法有時候也可以加備忘錄進行剪枝。

我覺得吧,沒必要過分糾結每個算法的定義,定義這東西無非文學詞匯而已,反正能把題做出來你說這是啥算法都行,所以大家還是得多刷題,刷出感覺,各種算法都手到擒來。

最典型的分治算法就是歸并排序了,核心邏輯如下:

voidsort(int[]nums,intlo,inthi){
intmid=(lo+hi)/2;
/******分******/
//對數組的兩部分分別排序
sort(nums,lo,mid);
sort(nums,mid+1,hi);
/******治******/
//合并兩個排好序的子數組
merge(nums,lo,mid,hi);
}

「對數組排序」是一個可以運用分治思想的算法問題,只要我先把數組的左半部分排序,再把右半部分排序,最后把兩部分合并,不就是對整個數組排序了嗎?

下面來看一道具體的算法題。

添加括號的所有方式

我來借力扣第 241 題講講什么是分治算法,先看看題目:

1aaef858-48d3-11eb-8b86-12bb97331649.png

簡單說,就是給你輸入一個算式,你可以給它隨意加括號,請你窮舉出所有可能的加括號方式,并計算出對應的結果。

函數簽名如下:

//計算所有加括號的結果
ListdiffWaysToCompute(Stringinput);

看到這道題的第一感覺肯定是復雜,我要窮舉出所有可能的加括號方式,是不是還要考慮括號的合法性?是不是還要考慮計算的優先級?

是的,這些都要考慮,但是不需要我們來考慮。利用分治思想和遞歸函數,算法會幫我們考慮一切細節,也許這就是算法的魅力吧,哈哈哈。

廢話不多說,解決本題的關鍵有兩點:

1、不要思考整體,而是把目光聚焦局部,只看一個運算符。

這一點我們前文經常提及,比如 手把手刷二叉樹第一期 就告訴你解決二叉樹系列問題只要思考每個節點需要做什么,而不要思考整棵樹需要做什么。

說白了,解決遞歸相關的算法問題,就是一個化整為零的過程,你必須瞄準一個小的突破口,然后把問題拆解,大而化小,利用遞歸函數來解決。

2、明確遞歸函數的定義是什么,相信并且利用好函數的定義。

這也是前文經常提到的一個點,因為遞歸函數要自己調用自己,你必須搞清楚函數到底能干嘛,才能正確進行遞歸調用。

下面來具體解釋下這兩個關鍵點怎么理解。

我們先舉個例子,比如我給你輸入這樣一個算式:

1 + 2 * 3 - 4 * 5

請問,這個算式有幾種加括號的方式?請在一秒之內回答我。

估計你回答不出來,因為括號可以嵌套,要窮舉出來肯定得費點功夫。

不過呢,嵌套這個事情吧,我們人類來看是很頭疼的,但對于算法來說嵌套括號不要太簡單,一次遞歸就可以嵌套一層,一次搞不定大不了多遞歸幾次。

所以,作為寫算法的人類,我們只需要思考,如果不讓括號嵌套(即只加一層括號),有幾種加括號的方式?

還是上面的例子,顯然我們有四種加括號方式:

(1) + (2 * 3 - 4 * 5)

(1 + 2) * (3 - 4 * 5)

(1 + 2 * 3) - (4 * 5)

(1 + 2 * 3 - 4) * (5)

發現規律了么?其實就是按照運算符進行分割,給每個運算符的左右兩部分加括號,這就是之前說的第一個關鍵點,不要考慮整體,而是聚焦每個運算符。

現在單獨說上面的第三種情況:

(1 + 2 * 3) - (4 * 5)

我們用減號-作為分隔,把原算式分解成兩個算式1 + 2 * 34 * 5

分治分治,分而治之,這一步就是把原問題進行了「分」,我們現在要開始「治」了。

1 + 2 * 3可以有兩種加括號的方式,分別是:

(1) + (2 * 3) = 7

(1 + 2) * (3) = 9

或者我們可以寫成這種形式:

1 + 2 * 3 = [9, 7]

4 * 5當然只有一種加括號方式,就是4 * 5 = [20]

然后呢,你能不能通過上述結果推導出(1 + 2 * 3) - (4 * 5)有幾種加括號方式,或者說有幾種不同的結果?

顯然,可以推導出來(1 + 2 * 3) - (4 * 5)有兩種結果,分別是:

9 - 20 = -11

7 - 20 = -13

那你可能要問了,1 + 2 * 3 = [9, 7]的結果是我們自己看出來的,如何讓算法計算出來這個結果呢?

這個簡單啊,再回頭看下題目給出的函數簽名:

//定義:計算算式 input 所有可能的運算結果
ListdiffWaysToCompute(Stringinput);

這個函數不就是干這個事兒的嗎?這就是我們之前說的第二個關鍵點,明確函數的定義,相信并且利用這個函數定義。

你甭管這個函數怎么做到的,你相信它能做到,然后用就行了,最后它就真的能做到了。

那么,對于(1 + 2 * 3) - (4 * 5)這個例子,我們的計算邏輯其實就是這段代碼:

ListdiffWaysToCompute("(1+2*3)-(4*5)"){
Listres=newLinkedList<>();
/******分******/
Listleft=diffWaysToCompute("1+2*3");
Listright=diffWaysToCompute("4*5");
/******治******/
for(inta:left)
for(intb:right)
res.add(a-b);

returnres;
}

好,現在(1 + 2 * 3) - (4 * 5)這個例子是如何計算的,你應該完全理解了吧,那么回來看我們的原始問題。

原問題1 + 2 * 3 - 4 * 5是不是只有(1 + 2 * 3) - (4 * 5)這一種情況?是不是只能從減號-進行分割?

不是,每個運算符都可以把原問題分割成兩個子問題,剛才已經列出了所有可能的分割方式:

(1) + (2 * 3 - 4 * 5)

(1 + 2) * (3 - 4 * 5)

(1 + 2 * 3) - (4 * 5)

(1 + 2 * 3 - 4) * (5)

所以,我們需要窮舉上述的每一種情況,可以進一步細化一下解法代碼:

ListdiffWaysToCompute(Stringinput){
Listres=newLinkedList<>();
for(inti=0;icharc=input.charAt(i);
//掃描算式input中的運算符
if(c=='-'||c=='*'||c=='+'){
/******分******/
//以運算符為中心,分割成兩個字符串,分別遞歸計算
List
left=diffWaysToCompute(input.substring(0,i));
List
right=diffWaysToCompute(input.substring(i+1));
/******治******/
//通過子問題的結果,合成原問題的結果
for(inta:left)
for(intb:right)
if(c=='+')
res.add(a+b);
elseif(c=='-')
res.add(a-b);
elseif(c=='*')
res.add(a*b);
}
}
//basecase
//如果res為空,說明算式是一個數字,沒有運算符
if(res.isEmpty()){
res.add(Integer.parseInt(input));
}
returnres;
}

有了剛才的鋪墊,這段代碼應該很好理解了吧,就是掃描輸入的算式input,每當遇到運算符就進行分割,遞歸計算出結果后,根據運算符來合并結果。

這就是典型的分治思路,先「分」后「治」,先按照運算符將原問題拆解成多個子問題,然后通過子問題的結果來合成原問題的結果

當然,一個重點在這段代碼:

//basecase
//如果res為空,說明算式是一個數字,沒有運算符
if(res.isEmpty()){
res.add(Integer.parseInt(input));
}

遞歸函數必須有個 base case 用來結束遞歸,其實這段代碼就是我們分治算法的 base case,代表著你「分」到什么時候可以開始「治」。

我們是按照運算符進行「分」的,一直這么分下去,什么時候是個頭?顯然,當算式中不存在運算符的時候就可以結束了。

那為什么以res.isEmpty()作為判斷條件?因為當算式中不存在運算符的時候,就不會觸發 if 語句,也就不會給res中添加任何元素。

至此,這道題的解法代碼就寫出來了,但是時間復雜度是多少呢?

如果單看代碼,真的很難通過 for 循環的次數看出復雜度是多少,所以我們需要改變思路,本題在求所有可能的計算結果,不就相當于在求算式input的所有合法括號組合嗎?

那么,對于一個算式,有多少種合法的括號組合呢?這就是著名的「卡特蘭數」了,最終結果是一個組合數,推導過程稍有些復雜,我這里就不寫了,有興趣的讀者可以自行搜索了解一下。

其實本題還有一個小的優化,可以進行遞歸剪枝,減少一些重復計算,比如說輸入的算式如下:

1 + 1 + 1 + 1 + 1

那么按照算法邏輯,按照運算符進行分割,一定存在下面兩種分割情況:

(1 + 1) + (1 + 1 + 1)

(1 + 1 + 1) + (1 + 1)

算法會依次遞歸每一種情況,其實就是冗余計算嘛,所以我們可以對解法代碼稍作修改,加一個備忘錄來避免這種重復計算:

//備忘錄
HashMap>memo=newHashMap<>();

ListdiffWaysToCompute(Stringinput){
//避免重復計算
if(memo.containsKey(input)){
returnmemo.get(input);
}
/******其他都不變******/
Listres=newLinkedList<>();
for(inti=0;i//...
}
if(res.isEmpty()){
res.add(Integer.parseInt(input));
}
/***********************/

//將結果添加進備忘錄
memo.put(input,res);
returnres;
}

當然,這個優化沒有改變原始的復雜度,只是對一些特殊情況做了剪枝,提升了效率。

最后總結

解決上述算法題利用了分治思想,以每個運算符作為分割點,把復雜問題分解成小的子問題,遞歸求解子問題,然后再通過子問題的結果計算出原問題的結果。

把大規模的問題分解成小規模的問題遞歸求解,應該是計算機思維的精髓了吧,建議大家多練~

責任編輯:xj

原文標題:分治算法詳解:表達式的不同優先級

文章出處:【微信公眾號:算法與數據結構】歡迎添加關注!文章轉載請注明出處。


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 算法
    +關注

    關注

    23

    文章

    4607

    瀏覽量

    92840
  • 分治法
    +關注

    關注

    0

    文章

    3

    瀏覽量

    5756

原文標題:分治算法詳解:表達式的不同優先級

文章出處:【微信號:TheAlgorithm,微信公眾號:算法與數據結構】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    詳解nginx中的正則表達式

    前言,我這里驗證的nginx-v1.23.2單機環境下的nginx中的正則表達式、location路徑匹配規則和優先級
    的頭像 發表于 12-03 09:59 ?147次閱讀
    <b class='flag-5'>詳解</b>nginx中的正則<b class='flag-5'>表達式</b>

    Verilog表達式的位寬確定規則

    很多時候,Verilog中表達式的位寬都是被隱式確定的,即使你自己設計了位寬,它也是根據規則先確定位寬后,再擴展到你的設計位寬,這常常會導致結果產生意想不到的錯誤。
    的頭像 發表于 10-22 15:41 ?450次閱讀
    Verilog<b class='flag-5'>表達式</b>的位寬確定規則

    nginx中的正則表達式和location路徑匹配指南

    前言,我這里驗證的nginx-v1.23.2單機環境下的nginx中的正則表達式、location路徑匹配規則和優先級
    的頭像 發表于 09-29 16:02 ?716次閱讀
    nginx中的正則<b class='flag-5'>表達式</b>和location路徑匹配指南

    freertos中斷優先級在哪設置

    FreeRTOS是一個流行的實時操作系統,它廣泛應用于嵌入式系統開發。在FreeRTOS中,中斷優先級是一個重要的概念,因為它決定了中斷處理的順序和響應時間。 1. 理解中斷優先級 在討論如何設置
    的頭像 發表于 09-02 14:17 ?641次閱讀

    求助,以下恒流源電路Io的計算表達式怎么計算?

    這個恒流源電路Io的計算表達式怎么計算,求給出詳細計算過程
    發表于 08-22 08:16

    TestStand表達式中常用的語法規則和運算符使用

    TestStand也有自己的語言嘛?在回答這個問題之前大家可以想一下在使用TestStand時有一個和語言密切相關的屬性。沒錯那就是表達式(Expressions),在這篇文章中,小編將以Q&A的方式來帶著大家來理解并熟悉TestStand表達式中較為常用的一些語法規則以
    的頭像 發表于 08-15 18:10 ?1355次閱讀
    TestStand<b class='flag-5'>表達式</b>中常用的語法規則和運算符使用

    鴻蒙原生應用元服務開發-倉頡基本概念表達式(二)

    ,以下程序使用 do-while 表達式,基于蒙特卡洛算法,近似計算圓周率的值: import std.random.* main() { let random = Random() var
    發表于 08-09 14:26

    鴻蒙原生應用元服務開發-倉頡基本概念表達式(一)

    在一些傳統編程語言中,一個表達式由一個或多個操作數(operand)通過零個或多個操作符(operator)組合而成,表達式總是隱含著一個計算過程,因此每個表達式都會有一個計算結果,對于只有操作數而
    發表于 08-08 10:27

    求助,有關表達式選項卡(ADS)的問題求解

    你好。 我看不到表達式選項卡中的某些變量值。 數組的大小顯然是 256,但我最多只能看到 100。 請問問題出在哪里? 謝謝。
    發表于 06-03 06:23

    mapgis屬性篩選表達式

    篇文章中,我們將詳細討論MapGIS的屬性篩選表達式,包括語法、操作符和函數等。 屬性篩選表達式是一種在MapGIS中用于指定要素選擇條件的代碼。它由一組操作符、函數和屬性字段組成,用于描述要篩選的要素的特征。在MapGIS中,屬性篩選
    的頭像 發表于 02-25 10:58 ?1614次閱讀

    西門子博途的算術表達式

    算術表達式既可以是一個數字值,也可以是由帶有算術運算符的兩個值或表達式組合而成。 算術運算符可以處理當前 CPU 所支持的各種數據類型。如果在該運算中有 2 個操作數,那么可根據以下條件來確定結果的數據類型。
    的頭像 發表于 01-24 11:36 ?1000次閱讀

    你還不會gvim正則表達式?一文搞懂!

    gvim正則表達式常在命令行模式下使用,一般用于文本文件字符串的替換、刪除等操作。
    的頭像 發表于 01-19 16:47 ?1170次閱讀

    rs觸發器的邏輯表達式

    邏輯表達式是描述邏輯關系的符號表示,可以用于定義和描述各種電路和邏輯操作。在邏輯電路中,RS觸發器是一種基本的存儲器元件,也被稱為鎖存器。 RS觸發器是由兩個與門組成的,其輸出互相連接,形成一個反饋
    的頭像 發表于 01-12 14:09 ?3114次閱讀

    華為和思科默認路由優先級

    優先級值不同,則優先級值最小的為最優路由(無論開銷值是否相同,另一種理解就是對不同路由來源或路由協議之間的比較)。
    的頭像 發表于 01-11 10:47 ?1178次閱讀

    GD32如何配置中斷優先級分組以及中斷優先級

    使用GD32 MCU的過程中,大家可能會有以下疑問:中斷優先級如何配置和使用?
    的頭像 發表于 01-10 10:30 ?3056次閱讀
    GD32如何配置中斷<b class='flag-5'>優先級</b>分組以及中斷<b class='flag-5'>優先級</b>
    主站蜘蛛池模板: 伊人精品国产| 萝莉御姐被吸奶| 亚洲中文字幕乱码熟女在线| 精品一区二区三区四区五区六区| 伊人久99久女女视频精品免| 久久中文字幕人妻AV熟女| 91久久偷偷做嫩草影院免| 欧亚一卡二卡日本一卡二卡| 帝王受PLAY龙椅高肉NP| 午夜精品久久久久久久爽牛战| 国内精品七七久久影院| 又爽又黄又粗又大免费视频| 免费三级现频在线观看| 成年人视频在线免费| 无罩看奶禁18| 久久国产亚洲电影天堂| 99欧美精品| 叔叔 电影完整版免费观看韩国| 国内精品久久人妻无码HD浪潮| 伊人情人网综合| 欧美极限扩肛| 国产精品-区区久久久狼| 亚洲色图在线观看视频| 男gv纯肉免费视频| 高清国语自产拍在线| 亚洲男人在线观看| 男女XX00上下抽搐动态图| 古月娜下面好紧好爽| 亚洲精品无码专区在线播放 | 无限资源在线观看8| 苍井空教师BD在线观看全集| 免费国产午夜理论不卡| 曰本xxⅹ孕妇性xxx| 狠狠色噜噜狠狠狠狠米奇777| 我与恶魔的h生活ova| 攻把受做得合不拢腿play| 日韩欧美三区| 成人在线免费看片| 日本高清天码一区在线播放| jizz破处| 青青热久精品国产亚洲AV无码|