chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

分治算法詳解:表達(dá)式的不同優(yōu)先級(jí)

算法與數(shù)據(jù)結(jié)構(gòu) ? 來(lái)源:算法與數(shù)據(jù)結(jié)構(gòu) ? 作者:labuladong ? 2021-01-04 14:04 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

我們號(hào)已經(jīng)寫了 動(dòng)態(tài)規(guī)劃算法,回溯(DFS)算法,BFS 算法,貪心算法,雙指針?biāo)惴?,滑?dòng)窗口算法,現(xiàn)在就差個(gè)分治算法沒寫了,今天來(lái)寫一下,集齊七顆龍珠,就能召喚神龍了~

其實(shí),我覺得回溯、分治和動(dòng)態(tài)規(guī)劃算法可以劃為一類,因?yàn)樗鼈兌紩?huì)涉及遞歸。

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

動(dòng)態(tài)規(guī)劃是一類算法問題,肯定是讓你求最值的。因?yàn)閯?dòng)態(tài)規(guī)劃問題擁有 最優(yōu)子結(jié)構(gòu),可以通過狀態(tài)轉(zhuǎn)移方程從小規(guī)模的子問題最優(yōu)解推導(dǎo)出大規(guī)模問題的最優(yōu)解。

分治算法呢,可以認(rèn)為是一種算法思想,通過將原問題分解成小規(guī)模的子問題,然后根據(jù)子問題的結(jié)果構(gòu)造出原問題的答案。這里有點(diǎn)類似動(dòng)態(tài)規(guī)劃,所以說運(yùn)用分治算法也需要滿足一些條件,你的原問題結(jié)果應(yīng)該可以通過合并子問題結(jié)果來(lái)計(jì)算。

其實(shí)這幾個(gè)算法之間界定并沒有那么清晰,有時(shí)候回溯算法加個(gè)備忘錄似乎就成動(dòng)態(tài)規(guī)劃了,而分治算法有時(shí)候也可以加備忘錄進(jìn)行剪枝。

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

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

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

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

下面來(lái)看一道具體的算法題。

添加括號(hào)的所有方式

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

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

簡(jiǎn)單說,就是給你輸入一個(gè)算式,你可以給它隨意加括號(hào),請(qǐng)你窮舉出所有可能的加括號(hào)方式,并計(jì)算出對(duì)應(yīng)的結(jié)果。

函數(shù)簽名如下:

//計(jì)算所有加括號(hào)的結(jié)果
ListdiffWaysToCompute(Stringinput);

看到這道題的第一感覺肯定是復(fù)雜,我要窮舉出所有可能的加括號(hào)方式,是不是還要考慮括號(hào)的合法性?是不是還要考慮計(jì)算的優(yōu)先級(jí)?

是的,這些都要考慮,但是不需要我們來(lái)考慮。利用分治思想和遞歸函數(shù),算法會(huì)幫我們考慮一切細(xì)節(jié),也許這就是算法的魅力吧,哈哈哈。

廢話不多說,解決本題的關(guān)鍵有兩點(diǎn):

1、不要思考整體,而是把目光聚焦局部,只看一個(gè)運(yùn)算符。

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

說白了,解決遞歸相關(guān)的算法問題,就是一個(gè)化整為零的過程,你必須瞄準(zhǔn)一個(gè)小的突破口,然后把問題拆解,大而化小,利用遞歸函數(shù)來(lái)解決。

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

這也是前文經(jīng)常提到的一個(gè)點(diǎn),因?yàn)檫f歸函數(shù)要自己調(diào)用自己,你必須搞清楚函數(shù)到底能干嘛,才能正確進(jìn)行遞歸調(diào)用。

下面來(lái)具體解釋下這兩個(gè)關(guān)鍵點(diǎn)怎么理解。

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

1 + 2 * 3 - 4 * 5

請(qǐng)問,這個(gè)算式有幾種加括號(hào)的方式?請(qǐng)?jiān)谝幻胫畠?nèi)回答我。

估計(jì)你回答不出來(lái),因?yàn)槔ㄌ?hào)可以嵌套,要窮舉出來(lái)肯定得費(fèi)點(diǎn)功夫。

不過呢,嵌套這個(gè)事情吧,我們?nèi)祟悂?lái)看是很頭疼的,但對(duì)于算法來(lái)說嵌套括號(hào)不要太簡(jiǎn)單,一次遞歸就可以嵌套一層,一次搞不定大不了多遞歸幾次。

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

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

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

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

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

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

發(fā)現(xiàn)規(guī)律了么?其實(shí)就是按照運(yùn)算符進(jìn)行分割,給每個(gè)運(yùn)算符的左右兩部分加括號(hào),這就是之前說的第一個(gè)關(guān)鍵點(diǎn),不要考慮整體,而是聚焦每個(gè)運(yùn)算符。

現(xiàn)在單獨(dú)說上面的第三種情況:

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

我們用減號(hào)-作為分隔,把原算式分解成兩個(gè)算式1 + 2 * 34 * 5。

分治分治,分而治之,這一步就是把原問題進(jìn)行了「分」,我們現(xiàn)在要開始「治」了。

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

(1) + (2 * 3) = 7

(1 + 2) * (3) = 9

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

1 + 2 * 3 = [9, 7]

4 * 5當(dāng)然只有一種加括號(hào)方式,就是4 * 5 = [20]。

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

顯然,可以推導(dǎo)出來(lái)(1 + 2 * 3) - (4 * 5)有兩種結(jié)果,分別是:

9 - 20 = -11

7 - 20 = -13

那你可能要問了,1 + 2 * 3 = [9, 7]的結(jié)果是我們自己看出來(lái)的,如何讓算法計(jì)算出來(lái)這個(gè)結(jié)果呢?

這個(gè)簡(jiǎn)單啊,再回頭看下題目給出的函數(shù)簽名:

//定義:計(jì)算算式 input 所有可能的運(yùn)算結(jié)果
ListdiffWaysToCompute(Stringinput);

這個(gè)函數(shù)不就是干這個(gè)事兒的嗎?這就是我們之前說的第二個(gè)關(guān)鍵點(diǎn),明確函數(shù)的定義,相信并且利用這個(gè)函數(shù)定義。

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

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

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;
}

好,現(xiàn)在(1 + 2 * 3) - (4 * 5)這個(gè)例子是如何計(jì)算的,你應(yīng)該完全理解了吧,那么回來(lái)看我們的原始問題。

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

不是,每個(gè)運(yùn)算符都可以把原問題分割成兩個(gè)子問題,剛才已經(jīng)列出了所有可能的分割方式:

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

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

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

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

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

ListdiffWaysToCompute(Stringinput){
Listres=newLinkedList<>();
for(inti=0;icharc=input.charAt(i);
//掃描算式input中的運(yùn)算符
if(c=='-'||c=='*'||c=='+'){
/******分******/
//以運(yùn)算符為中心,分割成兩個(gè)字符串,分別遞歸計(jì)算
List
left=diffWaysToCompute(input.substring(0,i));
List
right=diffWaysToCompute(input.substring(i+1));
/******治******/
//通過子問題的結(jié)果,合成原問題的結(jié)果
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為空,說明算式是一個(gè)數(shù)字,沒有運(yùn)算符
if(res.isEmpty()){
res.add(Integer.parseInt(input));
}
returnres;
}

有了剛才的鋪墊,這段代碼應(yīng)該很好理解了吧,就是掃描輸入的算式input,每當(dāng)遇到運(yùn)算符就進(jìn)行分割,遞歸計(jì)算出結(jié)果后,根據(jù)運(yùn)算符來(lái)合并結(jié)果。

這就是典型的分治思路,先「分」后「治」,先按照運(yùn)算符將原問題拆解成多個(gè)子問題,然后通過子問題的結(jié)果來(lái)合成原問題的結(jié)果。

當(dāng)然,一個(gè)重點(diǎn)在這段代碼:

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

遞歸函數(shù)必須有個(gè) base case 用來(lái)結(jié)束遞歸,其實(shí)這段代碼就是我們分治算法的 base case,代表著你「分」到什么時(shí)候可以開始「治」。

我們是按照運(yùn)算符進(jìn)行「分」的,一直這么分下去,什么時(shí)候是個(gè)頭?顯然,當(dāng)算式中不存在運(yùn)算符的時(shí)候就可以結(jié)束了。

那為什么以res.isEmpty()作為判斷條件?因?yàn)楫?dāng)算式中不存在運(yùn)算符的時(shí)候,就不會(huì)觸發(fā) if 語(yǔ)句,也就不會(huì)給res中添加任何元素。

至此,這道題的解法代碼就寫出來(lái)了,但是時(shí)間復(fù)雜度是多少呢?

如果單看代碼,真的很難通過 for 循環(huán)的次數(shù)看出復(fù)雜度是多少,所以我們需要改變思路,本題在求所有可能的計(jì)算結(jié)果,不就相當(dāng)于在求算式input的所有合法括號(hào)組合嗎?

那么,對(duì)于一個(gè)算式,有多少種合法的括號(hào)組合呢?這就是著名的「卡特蘭數(shù)」了,最終結(jié)果是一個(gè)組合數(shù),推導(dǎo)過程稍有些復(fù)雜,我這里就不寫了,有興趣的讀者可以自行搜索了解一下。

其實(shí)本題還有一個(gè)小的優(yōu)化,可以進(jìn)行遞歸剪枝,減少一些重復(fù)計(jì)算,比如說輸入的算式如下:

1 + 1 + 1 + 1 + 1

那么按照算法邏輯,按照運(yùn)算符進(jìn)行分割,一定存在下面兩種分割情況:

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

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

算法會(huì)依次遞歸每一種情況,其實(shí)就是冗余計(jì)算嘛,所以我們可以對(duì)解法代碼稍作修改,加一個(gè)備忘錄來(lái)避免這種重復(fù)計(jì)算:

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

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

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

當(dāng)然,這個(gè)優(yōu)化沒有改變?cè)嫉膹?fù)雜度,只是對(duì)一些特殊情況做了剪枝,提升了效率。

最后總結(jié)

解決上述算法題利用了分治思想,以每個(gè)運(yùn)算符作為分割點(diǎn),把復(fù)雜問題分解成小的子問題,遞歸求解子問題,然后再通過子問題的結(jié)果計(jì)算出原問題的結(jié)果。

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

責(zé)任編輯:xj

原文標(biāo)題:分治算法詳解:表達(dá)式的不同優(yōu)先級(jí)

文章出處:【微信公眾號(hào):算法與數(shù)據(jù)結(jié)構(gòu)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 算法
    +關(guān)注

    關(guān)注

    23

    文章

    4738

    瀏覽量

    96704
  • 分治法
    +關(guān)注

    關(guān)注

    0

    文章

    3

    瀏覽量

    5812

原文標(biāo)題:分治算法詳解:表達(dá)式的不同優(yōu)先級(jí)

文章出處:【微信號(hào):TheAlgorithm,微信公眾號(hào):算法與數(shù)據(jù)結(jié)構(gòu)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    I1電流計(jì)算對(duì)不對(duì)?怎么推導(dǎo)不出來(lái)I1的表達(dá)式是圖中那樣

    I1電流計(jì)算對(duì)不對(duì)?怎么推導(dǎo)不出來(lái)I1的表達(dá)式是圖中那樣
    發(fā)表于 09-28 18:01

    優(yōu)先級(jí)線程無(wú)法調(diào)度怎么解決?

    1,設(shè)置了3,5,6,8幾個(gè)優(yōu)先級(jí),設(shè)備在現(xiàn)場(chǎng)正常運(yùn)行了一年多后,顯示、前端、后端這3個(gè)低優(yōu)先級(jí)線程異常了,表現(xiàn)為屏幕不動(dòng),前端采集數(shù)據(jù)沒有變化等,其他高優(yōu)先級(jí)的線程如通訊,按鍵都能正常運(yùn)行,通訊有喂狗操作,停止通訊,會(huì)看門狗復(fù)
    發(fā)表于 09-25 07:33

    什么是RTOS中的優(yōu)先級(jí)反轉(zhuǎn)

    當(dāng)一個(gè)高優(yōu)先級(jí)任務(wù)正在等待一個(gè)資源,但一個(gè)低優(yōu)先級(jí)任務(wù)正在持有它,一個(gè)中等優(yōu)先級(jí)任務(wù)繼續(xù)在中間運(yùn)行時(shí),就會(huì)發(fā)生優(yōu)先級(jí)反轉(zhuǎn)——阻止低優(yōu)先級(jí)任務(wù)
    的頭像 發(fā)表于 09-09 14:50 ?515次閱讀

    TLe9893怎么調(diào)整外設(shè)的中斷優(yōu)先級(jí)?

    你好林工,我該怎么調(diào)整外設(shè)的中斷優(yōu)先級(jí)?是否可以通過工具調(diào)整?默認(rèn)設(shè)置下,是不是Brdv的在中斷優(yōu)先級(jí)高于T20和can?
    發(fā)表于 08-01 06:20

    labview如何使用VISA串口資源查找的正則表達(dá)式提取串口的資源名稱?

    如圖,如何利用VISA資源查找的正則表達(dá)式從很多串口當(dāng)中提取想要的目標(biāo)串口(Quectel USB AT Port這個(gè)串口)?
    發(fā)表于 07-07 17:20

    Cubeide1.18.1在線調(diào)試改變\"現(xiàn)場(chǎng)表達(dá)式\"中的值提示找不到地址,為什么?

    Cubeide1.18.1在線調(diào)試時(shí),在\"現(xiàn)場(chǎng)表達(dá)式\"中添加全局變量,然后改變其數(shù)值,Console窗口提示: Failed to read all registers
    發(fā)表于 06-12 06:50

    干貨分享 | 零基礎(chǔ)上手!TSMaster圖形信號(hào)表達(dá)式實(shí)操指南

    TSMaster軟件支持在圖形里面的信號(hào)表達(dá)式功能,主要用于多信號(hào)表達(dá)式運(yùn)算和顯示的場(chǎng)景。本文將以A2L中的標(biāo)定變量為例,介紹如何使用圖形中的信號(hào)表達(dá)式功能進(jìn)行多信號(hào)的后處理運(yùn)算和顯示。本文關(guān)鍵詞
    的頭像 發(fā)表于 06-06 20:03 ?489次閱讀
    干貨分享 | 零基礎(chǔ)上手!TSMaster圖形信號(hào)<b class='flag-5'>表達(dá)式</b>實(shí)操指南

    Cubeide1.18.1在線調(diào)試改變\"現(xiàn)場(chǎng)表達(dá)式\"中的值提示找不到地址,怎么解決?

    Cubeide1.18.1在線調(diào)試時(shí),在\"現(xiàn)場(chǎng)表達(dá)式\"中添加全局變量,然后改變其數(shù)值,Console窗口提示: Failed to read all registers
    發(fā)表于 06-06 08:27

    CyU3PDebugPrint的最高優(yōu)先級(jí)和最低優(yōu)先級(jí)是什么?

    [i]CyU3PDebugPrint的最高優(yōu)先級(jí)和最低優(yōu)先級(jí)是什么?
    發(fā)表于 05-13 08:22

    Cubeide1.18.1在線調(diào)試改變\"現(xiàn)場(chǎng)表達(dá)式\"中的值提示找不到地址怎么解決?

    Cubeide1.18.1在線調(diào)試時(shí),在\"現(xiàn)場(chǎng)表達(dá)式\"中添加全局變量,然后改變其數(shù)值,Console窗口提示: Failed to read all registers
    發(fā)表于 04-27 06:18

    配電柜—斷電危機(jī)?配電柜故障排查優(yōu)先級(jí)指南

    在排查配電柜故障過程中,合理安排排查優(yōu)先級(jí)至關(guān)重要。下面聊一下如何科學(xué)合理安排配電柜故障排查優(yōu)先級(jí)順序。
    的頭像 發(fā)表于 03-06 18:55 ?622次閱讀
    配電柜—斷電危機(jī)?配電柜故障排查<b class='flag-5'>優(yōu)先級(jí)</b>指南

    利用棧結(jié)構(gòu)實(shí)現(xiàn)四則運(yùn)算的巧妙方法

    ,非常符合人的正常思維,但是計(jì)算機(jī)計(jì)算的話不方便。 中綴表達(dá)式可以轉(zhuǎn)換成后綴表達(dá)式,這種表達(dá)式看起來(lái)抽象一些,但是不需要括號(hào)或者優(yōu)先級(jí),計(jì)算機(jī)計(jì)算的話比較方便。 整個(gè)過程有點(diǎn)復(fù)雜,分的
    的頭像 發(fā)表于 02-07 11:06 ?852次閱讀

    表達(dá)式畫Coms電路,最近二周有比賽第一次接觸Cmos,主要用與或非門電路畫

    用與或非門電路繪畫,通過表達(dá)式,來(lái)繪畫cmos門電路
    發(fā)表于 12-04 16:02

    詳解nginx中的正則表達(dá)式

    前言,我這里驗(yàn)證的nginx-v1.23.2單機(jī)環(huán)境下的nginx中的正則表達(dá)式、location路徑匹配規(guī)則和優(yōu)先級(jí)。
    的頭像 發(fā)表于 12-03 09:59 ?1142次閱讀
    <b class='flag-5'>詳解</b>nginx中的正則<b class='flag-5'>表達(dá)式</b>

    Verilog表達(dá)式的位寬確定規(guī)則

    很多時(shí)候,Verilog中表達(dá)式的位寬都是被隱式確定的,即使你自己設(shè)計(jì)了位寬,它也是根據(jù)規(guī)則先確定位寬后,再擴(kuò)展到你的設(shè)計(jì)位寬,這常常會(huì)導(dǎo)致結(jié)果產(chǎn)生意想不到的錯(cuò)誤。
    的頭像 發(fā)表于 10-22 15:41 ?1899次閱讀
    Verilog<b class='flag-5'>表達(dá)式</b>的位寬確定規(guī)則