【演算法Part2】回溯演算法
阿新 • • 發佈:2020-08-03
一.概念
回溯演算法實際上一個類似列舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就 “回溯” 返回,嘗試別的路徑。
回溯法是一種選優搜尋法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為 “回溯點”。
許多複雜的,規模較大的問題都可以使用回溯法,有“通用解題方法”的美稱。
二.基本思想
回溯演算法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。
- 演算法在包含問題的所有解的解空間樹中,按照深度優先的策略,從根結點出發搜尋解空間樹。演算法搜尋至解空間樹的任一結點時,總是先判斷該結點是否肯定不包含問題的解。如果肯定不包含,則跳過對以該結點為根的子樹的系統搜尋,逐層向其祖先結點回溯。否則,進入該子樹,繼續按深度優先的策略進行搜尋。
- 回溯法在用來求問題的所有解時,要回溯到根,且根結點的所有子樹都已被搜尋遍才結束。
- 回溯法在用來求問題的任一解時,只要搜尋到問題的一個解就可以結束。(摘自回溯演算法)
三.解題步驟
- 針對所給問題,確定問題的解空間:
首先應明確定義問題的解空間,問題的解空間應至少包含問題的一個(最優)解 - 確定結點的擴充套件搜尋規則
- 以深度優先方式搜尋解空間,並在搜尋過程中用剪枝函式避免無效搜尋
四.演算法框架
bool finished = FALSE; /* 是否獲得全部解? */ backtrack(int a[], int k, data input) { int c[MAXCANDIDATES]; /*這次搜尋的候選 */ int ncandidates; /* 候選數目 */ int i; /* counter */ if (is_a_solution(a,k,input)) process_solution(a,k,input); else { k = k+1; construct_candidates(a,k,input,c,&ncandidates); for (i=0; i<ncandidates; i++) { a[k] = c[i]; make_move(a,k,input); backtrack(a,k,input); unmake_move(a,k,input); if (finished) return; /* 如果符合終止條件就提前退出 */ } } }
變數解釋:
- a[]表示當前獲得的部分解;
- k表示搜尋深度;
- input表示用於傳遞的更多的引數;
- is_a_solution(a,k,input)判斷當前的部分解向量a[1...k]是否是一個符合條件的解
- construct_candidates(a,k,input,c,ncandidates)根據目前狀態,構造這一步可能的選擇,存入c[]陣列,其長度存入ncandidates
- process_solution(a,k,input)對於符合條件的解進行處理,通常是輸出、計數等
- make_move(a,k,input)和unmake_move(a,k,input),前者將採取的選擇更新到原始資料結構上,後者把這一行為撤銷。
五.題型解答
1. 求一個集合的所有子集(leecode.78)
//給定一組不含重複元素的整數陣列 nums,返回該陣列所有可能的子集(冪集)
class Solution {
public:
vector<vector<int>> res;
vector<int> temp;
void backtrace(vector<int>& nums,int start){
res.push_back(temp);
for(int i = start;i < nums.size();++i){
temp.push_back(nums[i]);
backtrace(nums,i+1);
temp.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
if(!nums.size())
return res;
backtrace(nums,0);
return res;
}
};
2.電話號碼的字母組合(leecode.17)
/*
給定一個僅包含數字 2-9 的字串,返回所有它能表示的字母組合。
給出數字到字母的對映如下(與電話按鍵相同)。注意 1 不對應任何字母。
*/
class Solution {
public:
map<char, string> mp = { { '2', "abc" }, { '3', "def" }, { '4', "ghi" }, { '5', "jkl" },
{ '6', "mno" }, { '7', "pqrs" }, { '8', "tuv" }, { '9', "wxyz" } };
vector<string> res;
string temp;
void backtrace(string digits, int start){
if (!digits.size())
res.push_back(temp);
else{
char num = digits[0];
string letter = mp[num];
for (int i = 0; i<letter.size(); i++){
temp.push_back(letter[i]);
backtrace(digits.substr(1), i + 1);
temp.pop_back();
}
}
}
vector<string> letterCombinations(string digits) {
if (!digits.size())
return res;
backtrace(digits, 0);
return res;
}
};