代碼隨想錄第三十天|332.重新安排行程、51.N皇后爪膊、37.解數(shù)獨(dú)权悟、回溯總結(jié)

332.重新安排行程(二刷回看)

思路:

用回溯記錄可能的行程,用vector<bool>used記錄是否使用過,用如下代碼判斷是否放進(jìn)path:

if(tickets[i][0]!=path.back())

????????????????continue;

????????????if(used[i]==true)

????????????????continue;

注意中止條件是

if(path.size()==(tickets.size()+1))

????????{

????????????result.push_back(path);

????????????return;

????????}

搜索所有路徑會(huì)超時(shí)推盛,應(yīng)該找如何搜索時(shí)找最優(yōu)解峦阁。


看代碼后:

這道題目有幾個(gè)難點(diǎn):

1. 一個(gè)行程中,如果航班處理不好容易變成一個(gè)圈耘成,成為死循環(huán)

2. 有多種解法榔昔,字母序靠前排在前面,讓很多同學(xué)望而退步瘪菌,如何該記錄映射關(guān)系呢 撒会?

3. 使用回溯法(也可以說深搜) 的話,那么終止條件是什么呢师妙?

4. 搜索的過程中诵肛,如何遍歷一個(gè)機(jī)場(chǎng)所對(duì)應(yīng)的所有機(jī)場(chǎng)。

這道題難是難在容器的選擇和使用上默穴。

第二問:

一個(gè)機(jī)場(chǎng)映射多個(gè)機(jī)場(chǎng)怔檩,機(jī)場(chǎng)之間要靠字母序排列,一個(gè)機(jī)場(chǎng)映射多個(gè)機(jī)場(chǎng)壁顶,可以使用std::unordered_map珠洗,如果讓多個(gè)機(jī)場(chǎng)之間再有順序的話,就是用std::map 或者std::multimap 或者 std::multiset若专。

在遍歷unordered_map<出發(fā)機(jī)場(chǎng), map<到達(dá)機(jī)場(chǎng), 航班次數(shù)>> targets的過程中,可以使用"航班次數(shù)"這個(gè)字段的數(shù)字做相應(yīng)的增減蝴猪,來標(biāo)記到達(dá)機(jī)場(chǎng)是否使用過了调衰。

如果“航班次數(shù)”大于零膊爪,說明目的地還可以飛,如果“航班次數(shù)”等于零說明目的地不能飛了嚎莉,而不用對(duì)集合做刪除元素或者增加元素的操作米酬。

相當(dāng)于說不刪就做一個(gè)標(biāo)記!

函數(shù)返回值用的是bool趋箩!因?yàn)槲覀冎恍枰业揭粋€(gè)行程赃额,就是在樹形結(jié)構(gòu)中唯一的一條通向葉子節(jié)點(diǎn)的路線.

代碼如下:

unordered_map<string,map<string,int>>?targets;

????bool?backtracking(int?ticketNum,?vector<string>?&result)

????{

????????if(result.size()==ticketNum+1)

????????????return?true;

????????for(pair<const?string,?int>?&target?:?targets[result[result.size()-1]])

????????{

????????????if(target.second?>?0)

????????????{

????????????????result.push_back(target.first);

????????????????target.second--;

????????????????if(backtracking(ticketNum,result))?return?true;

????????????????result.pop_back();

????????????????target.second++;

????????????}

????????}

????????return?false;

????}

????vector<string>?findItinerary(vector<vector<string>>&?tickets)?{

????????targets.clear();

????????vector<string>?result;

????????for(const?vector<string>?&?vec:tickets)

????????????targets[vec[0]][vec[1]]++;

????????result.push_back("JFK");

????????backtracking(tickets.size(),result);

????????return?result;


????}



51.N皇后(二刷回看)

思路:

無思路。

看視頻:

畫成樹之后能發(fā)現(xiàn)問題變成了在每一行遍歷回溯每個(gè)節(jié)點(diǎn)是否能放皇后叫确。

定義參數(shù):

vector<vector<string>>?result;//存放結(jié)果

vector<string>?chessboard(n,?string(n,'.'));//初始化棋盤

void?backtracking(int?n,?int?row,?vector<string>?&chessboard)//n為棋盤邊界跳芳,row為行,傳入棋盤

終止條件:

if(row?==?n)

????????{

????????????result.push_back(chessboard);

????????????return;

????????}//到達(dá)葉子節(jié)點(diǎn)

單層邏輯:

for(int?col=0;col<n;col++)

????????{

????????????if(isValid(row,col,chessboard,n))

????????????{

????????????????chessboard[row][col]?=?'Q';

????????????????backtracking(n,row+1,?chessboard);

????????????????chessboard[row][col]?=?'.';

????????????}

????????}

是否符合條件的判斷:

bool?isValid(int?row,?int?col,?vector<string>?&chessboard,?int?n)

????{

????????for?(int?i?=?0;?i?<?row;?i++)?

????????{

????????????if(chessboard[i][col]==?'Q')

????????????????return?false;

????????}// 同列的情況

????????for(int?i=row-1,j=col-1;i>=0&&j>=0;i--,j--)

????????{

????????????if(chessboard[i][j]==?'Q')

????????????????return?false;

????????}//45°的情況

????????for(int?i=row-1,j=col+1;i>=0?&&?j<n;?i--,j++)

????????{

????????????if(chessboard[i][j]=='Q')

????????????????return?false;

????????}

????????return?true;

????}//135°的情況



37.解數(shù)獨(dú)(二刷回看)

思路:

isValid函數(shù)中:判斷橫豎是否有相同元素竹勉,以及3*3中有沒有相同元素飞盆。

回溯算法中:

中止條件為橫豎都等于9,限制條件是如果元素不是'.'就跳過.但具體回溯不知道怎么寫

看視頻后:

遞歸函數(shù)的返回值需要是bool類型, 因?yàn)橹恍枰业揭粋€(gè)符合的情況就返回

本題遞歸不用終止條件次乓,解數(shù)獨(dú)是要遍歷整個(gè)樹形結(jié)構(gòu)尋找可能的葉子節(jié)點(diǎn)就立刻返回吓歇。

本題是二維遞歸:一個(gè)for循環(huán)遍歷棋盤的行,一個(gè)for循環(huán)遍歷棋盤的列票腰,一行一列確定下來之后城看,遞歸遍歷這個(gè)位置放9個(gè)數(shù)字的可能。

bool?backtracking(vector<vector<char>>&?board)

????{

????????for(int?i=0;i<board.size();i++)

????????{

????????????for(int?j=0;j<board[0].size();j++)

????????????{

????????????????if(board[i][j]!='.')

????????????????????continue;

????????????????for(char?k='1';?k<='9';k++)

????????????????{

????????????????????if(isValid(i,j,k,board))

????????????????????{

????????????????????????board[i][j]=k;

????????????????????????if(backtracking(board))?return?true;

????????????????????????board[i][j]='.';

????????????????????}

????????????????}

????????????????return?false;

????????????}

????????}

????????return?true;

????}

討論是否合法:同行是否重復(fù)杏慰;同列是否重復(fù)测柠;9宮格里是否重復(fù)

bool?isValid(int?row,?int?col,?char?val,?vector<vector<char>>?&board)

????{

????????for(int?i=0;i<9;i++)

????????{

????????????if(board[row][i]==val)

????????????????return?false;

????????}

????????for(int?j=0;j<9;j++)

????????{

????????????if(board[j][col]==val)

????????????????return?false;

????????}

????????int?startRow=(row/3)*3;

????????int?startCol=(col/3)*3;

????????for(int?i=startRow;i<startRow+3;i++)

????????{

????????????for(int?j=startCol;j<startCol+3;j++)

????????????{

????????????????if(board[i][j]==val)

????????????????????return?false;

????????????}

????????}

????????return?true;

????}



回溯總結(jié):

回溯是遞歸的副產(chǎn)品,只要有遞歸就會(huì)有回溯逃默,因此會(huì)和二叉樹遍歷和深度優(yōu)先搜索結(jié)合鹃愤。

回溯能解決如下問題:

組合問題:N個(gè)數(shù)里面按一定規(guī)則找出k個(gè)數(shù)的集合

排列問題:N個(gè)數(shù)按一定規(guī)則全排列,有幾種排列方式

切割問題:一個(gè)字符串按一定規(guī)則有幾種切割方式

子集問題:一個(gè)N個(gè)數(shù)的集合里有多少符合條件的子集

棋盤問題:N皇后完域,解數(shù)獨(dú)等等

理解回溯應(yīng)該使用樹形結(jié)構(gòu)软吐!

回溯是用遞歸控制for循環(huán)嵌套的數(shù)量:for循環(huán)橫向遍歷,遞歸縱向遍歷吟税,回溯不斷調(diào)整結(jié)果集凹耙。

回溯的優(yōu)化方法只有剪枝剪枝精髓是:for循環(huán)在尋找起點(diǎn)的時(shí)候要有一個(gè)范圍肠仪,如果這個(gè)起點(diǎn)到集合終止之間的元素已經(jīng)不夠題目要求的k個(gè)元素了肖抱,就沒有必要搜索了

回溯的模板:

void backtracking(參數(shù)) {

? ? if (終止條件) {

? ? ? ? 存放結(jié)果;

? ? ? ? return;

? ? }

? ? for (選擇:本層集合中元素(樹中節(jié)點(diǎn)孩子的數(shù)量就是集合的大幸炀伞)) {

? ? ? ? 處理節(jié)點(diǎn);

? ? ? ? backtracking(路徑意述,選擇列表); // 遞歸

? ? ? ? 回溯,撤銷處理結(jié)果

? ? }

}

組合問題:

注意startIndex的應(yīng)用:如果是一個(gè)集合來求組合的話,就需要startIndex荤崇,如果是多個(gè)集合取組合拌屏,各個(gè)集合之間相互不影響,那么就不用startIndex术荤。

切割問題:

解決以下問題

1.切割問題其實(shí)類似組合問題

2.如何模擬那些切割線

3.切割問題中遞歸如何終止

4.在遞歸循環(huán)中如何截取子串

5.如何判斷回文

巧用startIndex和i倚喂,注意切割過的地方不能重復(fù)切割所以遞歸函數(shù)需要傳入i + 1

子集問題:

在樹形結(jié)構(gòu)中子集問題是要收集所有節(jié)點(diǎn)的結(jié)果瓣戚,而組合問題是收集葉子節(jié)點(diǎn)的結(jié)果端圈。

子集問題不需要加終止條件,因?yàn)楸緛砭托枰闅v整棵樹子库。注意遞增子序列這道題舱权。

排列問題:

排列問題不需要startIndex,因?yàn)槊繉佣际菑?開始搜索而不是startIndex。需要used數(shù)組記錄path里都放了哪些元素了刚照。

去重問題:

注意樹層去重和樹枝去重刑巧,可以用used數(shù)組或者set去重,但set效率比較低无畔。組合問題可以抽象為樹形結(jié)構(gòu)啊楚,那么“使用過”在這個(gè)樹形結(jié)構(gòu)上是有兩個(gè)維度的,一個(gè)維度是同一樹枝上“使用過”浑彰,一個(gè)維度是同一樹層上“使用過”恭理。

例如:

used[i - 1] == true,說明同一樹枝candidates[i - 1]使用過

used[i - 1] == false郭变,說明同一樹層candidates[i - 1]使用過

棋盤問題(二刷重點(diǎn)):

N皇后:棋盤的寬度就是for循環(huán)的長(zhǎng)度颜价,遞歸的深度就是棋盤的高度

解數(shù)獨(dú):使用二維遞歸,本題中棋盤的每一個(gè)位置都要放一個(gè)數(shù)字诉濒,并檢查數(shù)字是否合法周伦,解數(shù)獨(dú)的樹形結(jié)構(gòu)要比N皇后更寬更深。

性能分析:

子集問題分析:

時(shí)間復(fù)雜度:O(2^n)未荒,因?yàn)槊恳粋€(gè)元素的狀態(tài)無外乎取與不取专挪,所以時(shí)間復(fù)雜度為O(2^n)

空間復(fù)雜度:O(n),遞歸深度為n片排,所以系統(tǒng)棧所用空間為O(n)寨腔,每一層遞歸所用的空間都是常數(shù)級(jí)別,注意代碼里的result和path都是全局變量率寡,就算是放在參數(shù)里迫卢,傳的也是引用,并不會(huì)新申請(qǐng)內(nèi)存空間冶共,最終空間復(fù)雜度為O(n)

排列問題分析:

時(shí)間復(fù)雜度:O(n!)乾蛤,這個(gè)可以從排列的樹形圖中很明顯發(fā)現(xiàn)每界,每一層節(jié)點(diǎn)為n,第二層每一個(gè)分支都延伸了n-1個(gè)分支幻捏,再往下又是n-2個(gè)分支盆犁,所以一直到葉子節(jié)點(diǎn)一共就是 n * n-1 * n-2 * ..... 1 = n!命咐。

空間復(fù)雜度:O(n)篡九,和子集問題同理。

組合問題分析:

時(shí)間復(fù)雜度:O(2^n)醋奠,組合問題其實(shí)就是一種子集的問題榛臼,所以組合問題最壞的情況,也不會(huì)超過子集問題的時(shí)間復(fù)雜度窜司。

空間復(fù)雜度:O(n)沛善,和子集問題同理。

N皇后問題分析:

時(shí)間復(fù)雜度:O(n!) 塞祈,其實(shí)如果看樹形圖的話金刁,直覺上是O(n^n),但皇后之間不能見面所以在搜索的過程中是有剪枝的议薪,最差也就是O(n!)尤蛮,n!表示n * (n-1) * .... * 1。

空間復(fù)雜度:O(n)斯议,和子集問題同理产捞。

解數(shù)獨(dú)問題分析:

時(shí)間復(fù)雜度:O(9^m) , m是'.'的數(shù)目。

空間復(fù)雜度:O(n^2)哼御,遞歸的深度是n^2

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坯临,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恋昼,更是在濱河造成了極大的恐慌看靠,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件液肌,死亡現(xiàn)場(chǎng)離奇詭異挟炬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)矩屁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門辟宗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吝秕,你說我怎么就攤上這事泊脐。” “怎么了烁峭?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵容客,是天一觀的道長(zhǎng)秕铛。 經(jīng)常有香客問我,道長(zhǎng)缩挑,這世上最難降的妖魔是什么但两? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮供置,結(jié)果婚禮上谨湘,老公的妹妹穿的比我還像新娘。我一直安慰自己芥丧,他們只是感情好紧阔,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著续担,像睡著了一般擅耽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上物遇,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天乖仇,我揣著相機(jī)與錄音,去河邊找鬼询兴。 笑死乃沙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蕉朵。 我是一名探鬼主播崔涂,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼始衅!你這毒婦竟也來了冷蚂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤汛闸,失蹤者是張志新(化名)和其女友劉穎蝙茶,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诸老,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡隆夯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了别伏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹄衷。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖厘肮,靈堂內(nèi)的尸體忽然破棺而出愧口,到底是詐尸還是另有隱情,我是刑警寧澤类茂,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布耍属,位于F島的核電站托嚣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏厚骗。R本人自食惡果不足惜示启,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望领舰。 院中可真熱鬧夫嗓,春花似錦、人聲如沸提揍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劳跃。三九已至,卻和暖如春浙垫,著一層夾襖步出監(jiān)牢的瞬間刨仑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工夹姥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杉武,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓辙售,卻偏偏與公主長(zhǎng)得像轻抱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子旦部,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容