現在我們來看遞迴演算法中非常經典的思想回溯法,這樣的演算法思想通常都應用在一類問題上,這類問題叫做樹型問題,這類問題他本身沒有定義在一顆二叉樹中,但我們具體分析這個問題時就會發現解決這個問題的思路本質是一顆樹的形狀。
解題思路
比如我們輸入的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 ;
vectorres;
//index表示從該數字開始在字串,存於s中
// s中儲存了此時從digits[0...index-1]翻譯得到的乙個字母字串
// 尋找和digits[index]匹配的字母, 獲得digits[0...index]翻譯得到的解
void findcombination(const string &digits, int index, const string &s)
//獲得數字
char c = digits[index];
//對應的字母串
string letters = lettermap[c - '0'];
for (int i = 0; i < letters.size(); i++)
return ;
}public:
vectorlettercombinations(string digits)
findcombination(digits, 0, "");
return res;
}};複製**
遞迴呼叫的乙個重要特徵-要返回。回溯法是暴力解法的乙個主要實現手段。
回溯演算法能處理一類重要的問題是排列問題,如果我們要用1,2,3進行排列,我們可以先抽出乙個元素,比如我們現在抽出1,那麼我們下面要做的事就是使用2,3兩個元素構造排列。我們又需要抽出乙個元素,如果我們抽出2,我們剩下唯一的元素就是3,我們通過這個路徑獲得排列123,用23排列如果選3,那麼就剩下2我們得到排列132。相應的我們考慮最開始選擇2或者選擇3。
這也是乙個樹形問題perms(nums[0…n-1]) = + perms(nums[ - 這個數字])
class solution
for (int i = 0; i < nums.size(); i++)}}
public:
vector> permute(vector& nums)
visitor = vector(nums.size(), false);
vectorp;
generatepermute(nums, 0, p);
return res;
}};複製**
回溯的意思就是要回去,遞迴函式自動保證了回去,但是我們設定的其他變數如果有必要的話也必須要回到原位。
解題思路
我們在1,2,3,4中取出你兩個數。在第一步時如果我們取1,那麼接下來就在2,3,4中取乙個數,我們可以得到組合12,13,14。如果第一步取2,那麼第二步在3,4中取乙個數,可以得到組合23,24。如果我們第一步取3,那麼第二步只能取4,得到組合34。**實現
class solution
for (int i = start; i <= n; i++)
return;
}public:
vector> combine(int n, int k)
vectorc;
generatecombinations(n, k, 1, c);
return res;
}};複製**
這是我們對這道題遞迴樹建立的模型,在這個模型裡存在乙個地方我們是明顯沒必要去走的,就是在於最後的地方,我們根本不需要去嘗試取4,這是因為我們取4之後無法再取任意乙個數了。在我們上面的演算法中我們還是嘗試取了4,取完4之後當取第二個數時發現我們什麼都取不了了,所以只好再返回回去,對於這一部分我們完全可以把它剪掉。換句話說,我們只嘗試取1,2,3。
#include
#include
using namespace std;
class solution
// 還有k - c.size()個空位, 所以,[i...n]中至少要有k-c.size()個元素
// i最多為 n - (k-c.size()) + 1
for( int i = start ; i <= n - (k-c.size()) + 1 ; i ++ )
return;
}public:
vector> combine(int n, int k)
};複製**
解題思路
**實現
class solution , , , };
bool inarea(int x, int y)
bool searchword(vector>& board, string word, int index, int startx, int starty)
if (board[startx][starty] == word[index])}}
//回溯
visited[startx][starty] = false;
}return
false;
}
public:
bool exist(vector>& board, string word)
for (int i = 0; i < m; i++)}}
return
false;
}};複製**
解題思路
首先我們從二維陣列最開始的地方(0,0)找起,這個地方是1,我們就找到了乙個新的島嶼,但我們需要標記和這塊陸地同屬於乙個島嶼的陸地,當我們尋找下乙個島嶼的時候才不會重複。那麼這個過程就是floodfill過程。其實就是從初始點開始進行一次深度優先遍歷,和上面那道題的尋找很相似,對每乙個島嶼進行四個方向尋找。**實現
class solution , , ,};
int m, n;
vector> visited;
bool inarea(int x, int y)
void dfs(vector>& grid, int x, int y)
}return;
}public:
int numislands(vector>& grid)
n = grid[0].size();
if (n == 0)
for (int i = 0; i < m; i++)
int res = 0;
for (int i = 0; i < m; i++)}}
return res;
}};複製**
在這裡,我們似乎沒有看見回溯的過程,也就是說我們不需要找到乙個位置讓visited[x][y]為false,這是因為我們的目的就是要把和最初我們執行的(i,j)這個點相連線的島嶼全部標記上,而不是在其中找到某乙個特定的序列或者乙個具體的值,所以我們只標記true,不會把它倒著標記成false。所以對於這個問題是否叫做回溯法,這是乙個見仁見智的問題。在搜尋的過程中一定會回去,這是遞迴的特性。但它沒有對資訊進行重置。不過它的解題思路是經典的floodfill。
回溯法師經典人工智慧的基礎
**快速判斷合法的情況
對於四皇后為例看一下如何遞迴回溯。首先肯定每行都應該有乙個皇后,否則就會有一行出現多個皇后。那麼第二行只能在第三個位置或第四個位置,考慮第三個位置。那麼第三行無論在哪都會有衝突。說明我們第二行的皇后不能放在第三個位置,我們回溯,在第四個位置放置皇后。**實現每一次在一行中嘗試擺放乙個皇后,來看我們能不能擺下這個皇后,如果不能擺下,回去上一行重新擺放上一行皇后的位置,直到我們在四行都擺放皇后。
class solution
// 嘗試將第index行的皇后擺放在第i列
for (int i = 0; i < n; i++)
}return;
}vectorgenerateboard(int n, vector&row)
return board;
}public:
vector> solvenqueens(int n)
};複製**
-------------------------華麗的分割線--------------------
看完的朋友可以點個喜歡/關注,您的支援是對我最大的鼓勵。
個人部落格番茄技術小棧和掘金主頁
leetcode 6 遞迴和回溯
leetcode相關題目 93 131 1 給定乙個只包含數字的字串,復原它並返回所有可能的 ip 位址格式。leetcode 93 2 給定乙個字串 s,將 s 分割成一些子串,使每個子串都是回文串。leetcode 131 leetcode相關題目 46 47 1 給定乙個 沒有重複 數字的序列...
Leetcode題解記錄 回溯
給定乙個二維網格和乙個單詞,找出該單詞是否存在於網格中。單詞必須按照字母順序,通過相鄰的單元格內的字母構成,其中 相鄰 單元格是那些水平相鄰或垂直相鄰的單元格。同乙個單元格內的字母不允許被重複使用。board a b c e s f c s a d e e 給定 word abcced 返回 tru...
Leetcode 遞迴 回溯
又稱試探法,即走不通就退回再走 當探索到某一步走不動時,發現原先選擇達不到目標,就退回一步重新選擇.用棧是否可以?數字 n 代表生成括號的對數,請你設計乙個函式,用於能夠生成所有可能的並且 有效的 括號組合。示例 1 輸入 n 3 輸出 示例 2 輸入 n 1 輸出 1 n 8 class solu...