LeetCode總結,回溯法小結

一,回溯法思想

一般教科書概念上的講解

      回溯算法實際上一個類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。

   回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術爲回溯法,而滿足回溯條件的某個狀態的點稱爲“回溯點”。

     許多複雜的,規模較大的問題都可以使用回溯法,有“通用解題方法”的美稱。

基本思想

   在包含問題的所有解的解空間樹中,按照深度優先搜索的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,如果包含,就從該結點出發繼續探索下去,如果該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜索算法)。

       若用回溯法求問題的所有解時,要回溯到根,且根結點的所有可行的子樹都要已被搜索遍才結束。

       而若使用回溯法求任一個解時,只要搜索到問題的一個解就可以結束。

解題一般步驟

    (1)針對所給問題,確定問題的解空間:首先應明確定義問題的解空間,問題的解空間應至少包含問題的一個(最優)解。

    (2)確定結點的擴展搜索規則

    (3)以深度優先方式搜索解空間,並在搜索過程中用剪枝函數避免無效搜索。


二,回溯法典型

1,八皇后問題

問題描述:八皇后問題是一個以國際象棋爲背景的問題:如何能夠在 8×8 的國際象棋棋盤上放置八個皇后,使得任何一個皇后都無法直接喫掉其他的皇后?爲了達到此目的,任兩個皇后都不能處於同一條橫行、縱行或斜線上。


轉化規則:其實八皇后問題可以推廣爲更一般的n皇后擺放問題:這時棋盤的大小變爲n×n,而皇后個數也變成n。當且僅當 n = 1 或 n ≥ 4 時問題有解。令一個一維數組a[n]保存所得解,其中a[i] 表示把第i個皇后放在第i行的列數(注意i的值都是從0開始計算的),下面就八皇后問題的約束條件。
(1)因爲所有的皇后都不能放在同一列,因此任意兩個a[0].....a[7]的值不能存在相同的兩個值。
(2)所有的皇后都不能在對角線上,那麼該如何檢測兩個皇后是否在同一個對角線上?我們將棋盤的方格成一個二維數組,如下:


假設有兩個皇后被放置在(i,j)和(k,l)的位置上,明顯,當且僅當|i-k|=|j-l| 時,兩個皇后纔在同一條對角線上。

窮舉法:

#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>

using namespace std;

//位置衝突算法 
bool isMeet(int a[], int n)//a[]位置數組,n皇后個數 
{
	for (int i = 2; i <= n; ++i)//i:位置 
	for (int j = 1; j < i; ++j)//j:位置 
	if ((a[i] == a[j]) || (abs(a[i] - a[j]) == i - j))//1:在一行;2:在對角線上 
		return false;   //衝突 
	return true;//不衝突 
}
//八皇后:枚舉算法 

//主函數 
int main()
{
	int a[9] = { 0 }; //用於記錄皇后位置:(第0行0列我們不用)。如:a[3] = 4;表示第3列第4行位置有皇后
	int  count = 0;  //用於計數 

	for (a[1] = 1; a[1] <= 8; ++a[1])
	for (a[2] = 1; a[2] <= 8; ++a[2])
	for (a[3] = 1; a[3] <= 8; ++a[3])
	for (a[4] = 1; a[4] <= 8; ++a[4])
	for (a[5] = 1; a[5] <= 8; ++a[5])
	for (a[6] = 1; a[6] <= 8; ++a[6])
	for (a[7] = 1; a[7] <= 8; ++a[7])
	for (a[8] = 1; a[8] <= 8; ++a[8])
	{
		if (!isMeet(a, 8))//如果衝突,則繼續枚舉 
			continue;
		else
			++count;
	}
	cout << count << endl;
	system("pause");
	return 0;
}


回溯法遞歸版:

見leecode,<LeetCode OJ> 52. N-Queens II

#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>

using namespace std;

int a[9] = { 0 };
int n = 8, cnt = 0;

//位置衝突算法 
bool isConflict(int a[], int n)//a[]位置數組,n皇后個數 
{
	int i = 0, j = 0;

	for (i = 2; i <= n; ++i)//i:位置 
	for (j = 1; j <= i - 1; ++j)//j:位置 
	if ((a[i] == a[j]) || (abs(a[i] - a[j]) == i - j))//1:在一行;2:在對角線上 
		return false;   //衝突 
	return true;//不衝突 
}

//八皇后問題:回溯算法(遞歸版) 
void Queens8(int k) //參數k:遞歸擺放第k個皇后 
{
	int i = 0;
	if (k > n)      //k>n:即k>8表示最後一個皇后擺放完畢 
	{
		printf("第%d種情況:", ++cnt);
		for (i = 1; i <= n; ++i)
			printf("%d ", a[i]);//打印情況 
		printf("\n");
	}
	else   //8個皇后未全部擺放完畢        
	{
		for (i = 1; i <= n; ++i)//擺放第k個皇后時(轉下一行) 
		{ //依次從列頂端開始搜索,一直到列底端,直到找到合適位置,如果未找到,自動返回上層遞歸(回溯) 
			a[k] = i;
			if (isConflict(a, k))
				Queens8(k + 1);//不衝突,遞歸擺放下一個皇后
		}
	}
	return;
}

//主函數 
int main()
{
	Queens8(1);//參數1:表示擺放第1個皇后 
	system("pause");
	return 0;
}


三,典型例題

在leetcode中,那些要求列舉所有情況,或者說所有情況都要探討一下的的例題,一般都可以考慮回溯法。

當遇到一個可以用到回溯法的時候需要按照如下步驟進行:
1. 確定問題的一個解空間樹, 這個解空間樹至少包含一個你需要的那個解, 否則這個樹就完全沒有意義了
2. 組織好這棵樹, 弄明白這棵樹的每一個節點代表什麼, 每一個分支代表什麼
3. 從這棵樹的根節點不斷的向下深搜, 當遇到不合適的節點的時候直接跳過以這個節點爲根的子樹
4. 當搜索到了葉子節點的時候就回溯
5. 不斷的重複這個3, 4步驟
附加: 根據具體的問題可以定義限界條件, 最優值條件, 根據這兩個條件可以剪枝了



以下是在leetcode中收集的典型例子。

<LeetCode OJ> 39 / 40 / 216 Combination Sum(I / II / III)

39題,翻譯題目:

給定一組候選集(C)和一個目標值T,在C的所有組合中,找出所有總和等於T的組合。

候選數組C中同一個數可以被選擇多次(不限次數)。

分析:

典型的回溯法應用。

對數組裏面的每個數,用遞歸的方式相加,每次遞歸將和sum與target作比較,若相等則加入結果vector,sum>target則捨棄,並返回false,若sum<target,則繼續進行遞歸。若sum>target,則回溯到上一層,重新以數組中的下一個數開始遞歸。

第一種sum=target的情況下,在加入結果vector後回溯(此時不應再累加),要將當前一種結果最後加入的元素pop_back(),並繼續對後面的元素進行遞歸;

第二種sum>target的情況下,則需要將當前結果的最後加入的元素pop_back(),並繼續對後面的元素進行遞歸。

第三種sum<target的情況下,直接以當前數繼續遞歸。

注意元素可以重複,所以下一次遞歸總是從當前遞歸元素開始。


40題,翻譯題目:

給定一組候選集(C)和一個目標值T,在C的所有組合中,找出所有總和等於T的組合。

候選數組C中每個數字只能使用一次。


分析:

典型的回溯法應用。

這道題跟上一道題基本一模一樣,唯一區別就是每個數只能用一次,因此代碼上只需要改一點點就行,即下一層遞歸

不能再從當前數開始,而要從下一個數開始了。


41題,翻譯題目:

找到有k個數組成的所有可能組合,加起來等於數n。
k個數取值1到9,每個數只能使用一次。確保在集合中的數字按順序排列。


<LeetCode OJ> 78 / 90 Subsets (I / II)

<LeetCode OJ> 46. / 47. Permutations (I / II)

<LeetCode OJ> 22. Generate Parentheses

<LeetCode OJ> 17. Letter Combinations of a Phone Number


注:本博文爲EbowTang原創,後續可能繼續更新本文。如果轉載,請務必複製本條信息!

原文地址:http://blog.csdn.net/ebowtang/article/details/38145433

原作者博客:http://blog.csdn.net/ebowtang


參考資源:

【1】寫的很不錯,很清楚,http://www.2cto.com/kf/201405/302318.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章