目录
效能分析: 效能分析目前我無暇去了解,但代碼隨想錄有相關的說明,可以直接去看。
回溯算法總結圖: 这个图是 代码随想录知识星球 (opens new window)成员:莫非毛 (opens new window),所画,总结的非常好,分享给大家。
讀題
332.重新安排行程
自己看到题目的第一想法
看到第一眼,要返回最小詞序的路程,要去判斷整個路徑要怎麼走,看完題意,以當前我的理解沒辦法馬上想到一個解題思路去解決連終止條件單遞迴要怎麼做也沒有想法,先來看題解。
看完代码随想录之后的想法
- 紀錄映射關係,
- 一個機場映射多個機場: unorder_map
- 多個機場之間有順序 map、multimap、multiset3
- 映射關係
- unorder_map<string, multiset>: unorder_map<出發機場, 到達機場的集合> → 出發機場與到達機場之間進行映射關係
- unorder_map<string, map<string, int>> unorder_map < 出發機場,<到達機場,行班> 出發機場與到達機場的映射關係,並且到達機場也會與航班次數有一個映射關係
- 建議使用第二種做法,可使用航班次數來記錄使用次數,假設大於零代表這個目的地能飛,如果等於零,代表不能飛了
- 回溯法
-
模板
void backtracking(參數) { if(終止條件) { 存放結果; return; } for(選擇: 本層集合中的元素) { 處理節點 backtracking(路徑, 選擇列表) 回溯,撤銷處理結果 }
-
參數
- unordered_map<string, map<string, int>> targets → 紀錄航班的映射關係,定義為全局變量不用回溯與遞迴,只是在過程中進行次數的刪減
- ticketNum 代表有多少航班
- backtracking 設為bool 因為假設找到一個路徑是合法的,直接將這個路徑一路往上傳
-
參數初始值
-
將航班插入到unordered_map中進行映射
for (const vector<string>& vec : tickets) { targets[vec[0]][vec[1]]++; // 记录映射关系 } result.push_back("JFK"); // 起始机场
假設有以下機票
JFK -> LAX JFK -> SFO LAX -> SFO
將這些機票放入數據結構中會呈現以下狀態
{ "JFK": {"LAX": 1, "SFO": 1}, "LAX": {"SFO": 1} }
-
-
終止條件
- 假設有四個航班,只要找到機場數量是五,四個航班可以想像程點之間的線,所以四個航班有五個機場,假設機場個數 達到了航班數加一,則終止遞迴
-
單層搜索的邏輯
for (pair<const string, int>& target : targets[result[result.size() - 1]]) // 選擇要在map中選擇哪個到達機場 { if (target.second > 0 ) { // 紀錄機場是否飛過了 result.push_back(target.first); target.second--; if (backtracking(ticketNum, result)) return true; // 假設找到一個路徑符合條件,一路回傳回去,也因此不需要做回溯操作,因為一旦做回溯操作,結果就不對了 result.pop_back(); target.second++; } }
- 第一次調用會從JFK開始,是因為在for函數中的鍵值對在一開始就是JFK,因為是在result當眾去尋找目的機場。
- 在第一次搜索的時候,以上面為例會從LAX開始,假設target.second(航班次數) 大於零則往下遞迴,而整體在JFK開始的航班會有兩班 一個是SFO 另一個是 LAX
- 第二次搜索時,會從LAX 有一個航班也就是SFO而LAX也只會跑這一次,因為LAX的只有一個目的機場,也就是只會進行一次迭代
- 因為MAP的緣故,裡面的鍵值對都是有序的,但假設是從SFO開始,因為SFO沒有對應的目的機場,所以也不會進入到for迴圈當中
- 所以以這個簡單的例子最後會得出JFK → LAX → SFO的路徑
-
51.N皇后
自己看到题目的第一想法
先去看皇后的移動路徑,在其橫直斜都不能有東西,否則會被攻擊,用回溯有思考到應該就是一層一層去遞迴但如果是直跟橫,我想像的到可能可以用記錄used來避免直的
如果我的判斷式橫得很好避免,就是一層只能有一個位子被選中,直的也很好避免,使用used來避免直的撞車,那橫的我現在想到是每一層跑一次for回圈檢查 當前位置的cur + i cur -i 的位置?
這邊我真的想到有點困擾
看完代码随想录之后的想法
看完發現其實比起之前的題目,這題更多的是考察思路,代碼本身不難,跟著代碼隨想錄一題題的走下來,理解上不難
其實主要還是三點
終止條件要想清楚 row = n 代表走到底了
單層搜索邏輯
- 確認當前位置是否合法
- 合法→ 放置皇后, 不合法 → 跳過
- 進行遞迴
- 進行回溯
isValid
檢查chessboard的列以及135與45度角是否有Q存在,如果是則return false,如果全部檢查都不是return true
- hint: 因為每一層遞迴都會是底所以只要查看135度角以及45度角是否有數值
37.解数独
自己看到题目的第一想法
當下想一想以為跟N皇后很像,但發現N皇后每次只專注在一層,但在數獨上,需要考慮的不僅僅是橫的也需要考慮列,在實現上有困難,思路沒有打開
看完代码随想录之后的想法
學到了二層遞迴的概念,因為一題題的理解,雖然目前沒有辦法自己單獨寫出來,但一刷就跟著卡哥的思路一步步地做下來
332.重新安排行程 - 實作
思路
- 紀錄映射關係:unorder_map<string, map<string, int>> unorder_map < 出發機場,<到達機場,行班>
- 回溯函數
- 參數
- vector<string>& result → 紀錄航班的路徑
- ticketNum 代表有多少航班
- backtracking 設為bool 假設找到一個路徑是合法的,直接將這個路徑一路往上傳
- 終止條件 result.size() == ticketNums + 1
- 單層搜索的邏輯
- 選擇要在map中選擇哪個到達機場
- target.second > 0 判斷是否還可以飛
- 處理節點
- 遞迴 ,假設找到一個路徑符合條件,一路回傳回去
- 回溯
- 參數
- 主函數
- 建立results
- 將航班插入到unordered_map中進行映射
- 遞迴
- 回傳結果
Code
class Solution {
public:
unordered_map<string, map<string, int>> target;
bool backtracking(vector<string>& result, int ticketNums) {
if(result.size() == ticketNums + 1 ) return true;
for(pair<const string, int>& target : target[result[result.size()-1]]) {
if(target.second > 0){
result.push_back(target.first);
target.second--;
if(backtracking(result, ticketNums)) return true;
result.pop_back();
target.second++;
}
}
return false;
}
vector<string> findItinerary(vector<vector<string>>& tickets) {
target.clear();
vector<string> result;