leetcode題解(遞迴和回溯法)
在計算機問題中,大量的問題都需要使用遞迴演算法,上一篇部落格我們介紹了一下二叉樹中的遞迴問題。現在我們來看遞迴演算法中非常經典的思想回溯法,這樣的演算法思想通常都應用在一類問題上,這類問題叫做樹型問題,這類問題他本身沒有定義在一顆二叉樹中,但我們具體分析這個問題時就會發現解決這個問題的思路本質是一顆樹的形狀。
<!--more-->
樹形問題
現在我們來看遞迴演算法中非常經典的思想回溯法,這樣的演算法思想通常都應用在一類問題上,這類問題叫做樹型問題,這類問題他本身沒有定義在一顆二叉樹中,但我們具體分析這個問題時就會發現解決這個問題的思路本質是一顆樹的形狀。
leetcode 17. 電話號碼的字母組合
解題思路
比如我們輸入的digits=“23”,2能代表abc三個字母,當2代表a時,3代表def,同理我們就可以畫出一棵樹。
遞迴過程:
digits是數字字串
s(digits)是digits所能代表的字母字串
s(digits[0…n-1]) = letter(digits[0]) + s(digits[1…n-1]) = letter(digits[0]) + letter(digits[1]) + s(digits[2…n-1]) = …
程式碼實現
class Solution {
private:
const string letterMap[10] = {
" ", //0
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz" //9
};
vector res;
//index表示從該數字開始在字串,存於s中
// s中儲存了此時從digits[0...index-1]翻譯得到的一個字母字串
// 尋找和digits[index]匹配的字母, 獲得digits[0...index]翻譯得到的解
void findCombination(const string &digits, int index, const string &s){
if (index == digits.size()){
res.push_back(s);
return;
}
//獲得數字
char c = digits[index];
//對應的字母串
string letters = letterMap[c - '0'];
for (int i = 0; i letterCombinations(string digits) {
res.clear();
if (digits.size() == 0){
return res;
}
findCombination(digits, 0, "");
return res;
}
};
什麼是回溯 遞迴呼叫的一個重要特徵-要返回。回溯法是暴力解法的一個主要實現手段。
思考題
- leetcode 93
-
leetcode 131
-
- *
leetcode 46. 全排列
解題思路
回溯演算法能處理一類重要的問題是排列問題,如果我們要用1,2,3進行排列,我們可以先抽出一個元素,比如我們現在抽出1,那麼我們下面要做的事就是使用2,3兩個元素構造排列。我們又需要抽出一個元素,如果我們抽出2,我們剩下唯一的元素就是3,我們透過這個路徑獲得排列123,用23排列如果選3,那麼就剩下2我們得到排列132。相應的我們考慮最開始選擇2或者選擇3。
這也是一個樹形問題
Perms(nums[0…n-1]) = {取出一個數字} + Perms(nums[{0…n-1} - 這個數字])
程式碼實現
class Solution {
private:
vector> res;
vector visitor;
//產生一個解
//p[0, index-1]已經是一個組合了
//要生成index大小的組合
// p中儲存了一個有index-1個元素的排列。
// 向這個排列的末尾新增第index個元素, 獲得一個有index個元素的排列
void generatePermute(const vector& nums, int index, vector& p){
if (index == nums.size()){
res.push_back(p);
return;
}
for (int i = 0; i > permute(vector& nums) {
res.clear();
if (nums.size() == 0){
return res;
}
visitor = vector(nums.size(), false);
vector p;
generatePermute(nums, 0, p);
return res;
}
};
相似問題
- leetcode 47
回溯的意思就是要回去,遞迴函式自動保證了回去,但是我們設定的其他變數如果有必要的話也必須要回到原位。
leetcode 77. 組合
解題思路
我們在1,2,3,4中取出你兩個數。在第一步時如果我們取1,那麼接下來就在2,3,4中取一個數,我們可以得到組合12,13,14。如果第一步取2,那麼第二步在3,4中取一個數,可以得到組合23,24。如果我們第一步取3,那麼第二步只能取4,得到組合34。
程式碼實現
class Solution {
private:
vector> res;
//前start-1個組合已經完成
// 求解C(n,k), 當前已經找到的組合儲存在c中, 需要從start開始搜尋新的元素
void generateCombinations(int n, int k, int start, vector &c){
if (c.size() == k){
res.push_back(c);
return;
}
for (int i = start; i > combine(int n, int k) {
res.clear();
if (n c;
generateCombinations(n, k, 1, c);
return res;
}
};
回溯法解決組合問題的最佳化
這是我們對這道題遞迴樹建立的模型,在這個模型裡存在一個地方我們是明顯沒必要去走的,就是在於最後的地方,我們根本不需要去嘗試取4,這是因為我們取4之後無法再取任意一個數了。在我們上面的演算法中我們還是嘗試取了4,取完4之後當取第二個數時發現我們什麼都取不了了,所以只好再返回回去,對於這一部分我們完全可以把它剪掉。換句話說,我們只嘗試取1,2,3。
回溯法的剪枝
#include
#include
using namespace std;
class Solution {
private:
vector> res;
// 求解C(n,k), 當前已經找到的組合儲存在c中, 需要從start開始搜尋新的元素
void generateCombinations(int n, int k, int start, vector &c){
if( c.size() == k ){
res.push_back(c);
return;
}
// 還有k - c.size()個空位, 所以,[i...n]中至少要有k-c.size()個元素
// i最多為 n - (k-c.size()) + 1
for( int i = start ; i > combine(int n, int k) {
res.clear();
if( n n )
return res;
vector c;
generateCombinations(n, k, 1, c);
return res;
}
};
相似問題
- leetcode 39
- leetcode 40
- leetcode 216
- leetcode 78
- leetcode 90
- leetcode 401
-
- *
leetcode79. 單詞搜尋
解題思路
對於每一個位置,我們按照上右下左從四個方向尋找,當選擇的方向匹配時,則選擇這個位置繼續進行上右下左尋找,如果四個方向都不匹配,則退回上一步的位置尋找下一個方向。
程式碼實現
class Solution {
//從board[startx][starty]開始, 尋找[index...word.size()]
private:
vector> visited;
int m,n;//行與列
int d[4][2] = {{-1,0}, {0, 1}, {1, 0}, {0, -1}};
bool inArea(int x, int y){
return x >= 0 && x = 0 && y >& board, string word, int index, int startx, int starty){
//尋找到最後一個元素了
if (index == (word.size() -1)){
return board[startx][starty] == word[index];
}
if (board[startx][starty] == word[index]){
visited[startx][starty] = true;
// 從startx, starty出發,向四個方向尋
for (int i = 0; i >& board, string word) {
m = board.size();
assert(m > 0);
n = board[0].size();
//初始化visitor
for (int i = 0; i (n, false));
}
for (int i = 0; i
floodfill演算法,一類經典問題
leetcode 200. 島嶼的個數
解題思路
首先我們從二維陣列最開始的地方(0,0)找起,這個地方是1,我們就找到了一個新的島嶼,但我們需要標記和這塊陸地同屬於一個島嶼的陸地,當我們尋找下一個島嶼的時候才不會重複。那麼這個過程就是floodfill過程。其實就是從初始點開始進行一次深度優先遍歷,和上面那道題的尋找很相似,對每一個島嶼進行四個方向尋找。
程式碼實現
class Solution {
private:
int d[4][2] = {{0,1}, {0, -1}, {1,0},{-1, 0}};
int m, n;
vector> visited;
bool inArea(int x, int y){
return x >= 0 && x = 0 && y >& grid, int x, int y){
visited[x][y] = true;
for (int i = 0; i >& grid) {
m = grid.size();
if (m == 0){
return 0;
}
n = grid[0].size();
if (n == 0){
return 0;
}
for (int i = 0; i (n, false));
}
int res = 0;
for (int i = 0; i
在這裡,我們似乎沒有看見回溯的過程,也就是說我們不需要找到一個位置讓visited[x][y]為false,這是因為我們的目的就是要把和最初我們執行的(i,j)這個點相連線的島嶼全部標記上,而不是在其中找到某一個特定的序列或者一個具體的值,所以我們只標記true,不會把它倒著標記成false。所以對於這個問題是否叫做回溯法,這是一個見仁見智的問題。在搜尋的過程中一定會回去,這是遞迴的特性。但它沒有對資訊進行重置。不過它的解題思路是經典的floodfill。
相似問題
- leetcode 130
-
leetcode 417
-
- *
回溯法師經典人工智慧的基礎
leetcode 51. N皇后
解題思路
**快速判斷合法的情況
- dia1: 橫縱座標相加相同
- dia2:橫座標-縱座標相同
對於四皇后為例看一下如何遞迴回溯。首先肯定每行都應該有一個皇后,否則就會有一行出現多個皇后。那麼第二行只能在第三個位置或第四個位置,考慮第三個位置。那麼第三行無論在哪都會有衝突。說明我們第二行的皇后不能放在第三個位置,我們回溯,在第四個位置放置皇后。
每一次在一行中嘗試擺放一個皇后,來看我們能不能擺下這個皇后,如果不能擺下,回去上一行重新擺放上一行皇后的位置,直到我們在四行都擺放皇后。
程式碼實現
class Solution {
private:
vector col, dia1, dia2;
vector> res;
//嘗試在一個n皇后問題中,擺放第index行的皇后位置
void putQueen(int n, int index, vector &row){
if (index == n){
res.push_back(generateBoard(n, row));
return;
}
// 嘗試將第index行的皇后擺放在第i列
for (int i = 0; i generateBoard(int n, vector &row){
assert(n == row.size());
vector board(n, string(n, '.'));
for(int i = 0; i > solveNQueens(int n) {
res.clear();
col.clear();
dia1.clear();
dia2.clear();
col = vector(n, false);
dia1 = vector(2*n -1, false);
dia2 = vector(2*n -1, false);
vector row;
putQueen(n, 0, row);
return res;
}
};
相似問題
- leetcode 52
-
leetcode 37
-
- *
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4550/viewspace-2803355/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- js使用遞迴回溯法解八皇后問題程式碼分享JS遞迴
- c++迷宮問題回溯法遞迴演算法C++遞迴演算法
- 用棧+回溯+非遞迴解決N皇后問題遞迴
- 回溯和遞迴實現迷宮問題(C語言)遞迴C語言
- 關於遞迴和回溯的一次深入思考遞迴
- Java資料結構與演算法--遞迴和回溯Java資料結構演算法遞迴
- 資料結構和演算法——遞迴-八皇后問題(回溯演算法)資料結構演算法遞迴
- 回溯法解決迷宮問題
- 回溯法解決喝酒問題 (轉)
- 第七章 遞迴、DFS、剪枝、回溯等問題 ------------- 7.3 題解:機器人走方格問題遞迴機器人
- Leetcode 題解系列 -- 對稱二叉樹(遞迴)LeetCode二叉樹遞迴
- 遞迴和尾遞迴遞迴
- ?30 秒瞭解尾遞迴和尾遞迴優化遞迴優化
- 回溯法解決全排列問題總結
- 【LeetCode回溯演算法#08】遞增子序列,鞏固回溯演算法中的去重問題LeetCode演算法
- 遞迴解決全排列問題遞迴
- 「演算法之美系列」遞迴與回溯(JS版)演算法遞迴JS
- 原:八皇后問題的遞迴和非遞迴Java實現遞迴Java
- 二分法的簡單實現——-遞迴和非遞迴遞迴
- 回溯法(排列樹)解決八(N)皇后問題
- 遞迴思想----解決飲料問題遞迴
- Josephus問題解決方法五(遞迴)遞迴
- leetcode:遞迴:括號生成LeetCode遞迴
- 回溯法求迷宮問題
- (回溯法)解決一系列組合問題
- python 遞迴和二分法Python遞迴
- 回溯法
- 遞迴和遞推總結遞迴
- 八皇后之回溯法解決
- 三、遞迴樹分析法遞迴
- leetcode 解題 5. 最長迴文子串 python@ 官解,暴力法,動態法,manacher 法LeetCodePython
- Oracle和Mysql遞迴OracleMySql遞迴
- 漢諾塔和遞迴遞迴
- leetcode:組合總和II(回溯java)LeetCodeJava
- python3:遞迴解漢諾塔問題Python遞迴
- 【LeetCode回溯演算法#07】子集問題I+II,鞏固解題模板並詳解回溯演算法中的去重問題LeetCode演算法
- JavaScript專題之遞迴JavaScript遞迴
- 遞迴路徑問題遞迴