深度優先 搜索(DFS, Depth First Search)
從一個頂點v出發,首先將v標記為已遍歷的頂點,然后選擇一個鄰接於v的尚未遍歷的頂點u,如果u不存在,本次搜素終止。如果u存在,那么從u又開始一次DFS。如此循環直到不存在這樣的頂點。
算法核心代碼如下:
void dfs(int step){
// 判斷邊界是否成立
// 嘗試每一種可能
for(int i=0;i<n;i++){
//
// 繼續執行下一步
dfs(step + 1)
// 取消已被使用標記
}
}
全排列
下面我們利用一個簡單基本案例來學習全排列
A 手中有3張牌,分別是1,2,3 那么請問這三張牌能組成多少位不重復三位數字?
DFS算法分析
-
首先我們假設有三個桶,桶里面可以存放牌,那么我們來到第一個桶,我們手里有3張牌,按照順序,我我們可以放入1,然后完成當前操作,標記第一張牌已經被使用,來到第二個桶里面,開始嘗試,嘗試放入第一張牌,此時1已經被使用了,所以我們嘗試2,那么第二個桶也已經被放入數據了,2被標記為使用,接着來到第3個桶,此時,我們嘗試放入1,1被使用,無法放入,嘗試放入2,2也被使用,嘗試放入3,OK,3 放入成功,此時三個桶放入完成,完成一次全排列,即
1,2,3 -
好,回到第3個桶,的時候我們沒有其他牌可以放了,1,2已經被使用,3正在桶里呢,我們繼續回到2號桶,同時標記3號牌未被使用,回到2號桶,此時2號桶已經嘗試了1,2 那么我們繼續嘗試3號牌,3號牌剛剛被收回,可以放入,此時2號桶放入3號牌,繼續第3個桶,同樣的依次嘗試所有的可能,1號牌不行,2號可以,此時完成了全排列
1,3,2 -
繼續,回到3號桶,沒有可用的了,回到2號桶,3號牌也被用了,也沒有了,繼續回到1號桶,此時1號桶放入的是1,那么我們收回,繼續放入2號牌,來到2號桶,此時手中有1,3號,我們放入1,來到3號桶,1,2已被使用,我們只能放入3,又完成一次全排列
2,1,3 -
依次類推....
那么我們看下代碼,這里提供了C語言版本的和Java語言版本的,原理是一樣的
C語言版本
代碼
#include <stdio.h>
// 定義撲克牌長度 3
#define PLAY_CARD_SIZE 3
// 定義數組
int numbers[] = {1,2,3};
// 標記數字是否被使用
int status[] = {0, 0, 0};
// 定義位置
int location[] = {0,0,0};
/**
* 聲明dfs方法
* @param step 當然位置
*/
void dfs(int step);
int main(){
dfs(0);
return 0;
}
void dfs(int step){
// 判斷搜索臨界條件
if (step == 3){
for (int i = 0; i < 3; ++i) {
printf("%d,",location[i]);
}
printf("\n");
// 完成此次全排列
return;
}
for (int j = 0; j < 3; ++j) {
if(status[j] == 0){
location[step] = numbers[j];
status[j] = 1;
dfs(step + 1);
status[j] = 0;
}
}
}
輸出結果
1,2,3,
1,3,2,
2,1,3,
2,3,1,
3,1,2,
3,2,1,
Java語言版本
代碼
import java.util.ArrayList;
import java.util.List;
/**
* 深度優先搜尋算法
*/
public class DFS2 {
// 定義撲克牌的數量
static int PLAY_CARD_SIZE = 3;
// 存放撲克牌的集合
static List<PlayCard> playCards = new ArrayList<>();
// 存放撲克牌的位置
static String[] numbers = new String[PLAY_CARD_SIZE];
// 初始化撲克牌 1,2,3
static {
for (int i = 0; i < PLAY_CARD_SIZE; i++) {
playCards.add(new PlayCard(String.valueOf(i + 1), false));
}
}
// 程序入口
public static void main(String[] args) {
dfs(0);
}
private static void dfs(int startIndex) {
if (startIndex == playCards.size()){
for (int i = 0;i<numbers.length;i++){
System.out.print(numbers[i]+",");
}
System.out.println("");
System.out.println("-------------");
return;
}
for (int i = 0; i < playCards.size(); i++) {
if(!playCards.get(i).used){
playCards.get(i).used = true;
numbers[startIndex] = playCards.get(i).code;
dfs(startIndex + 1);
playCards.get(i).used = false;
}
}
}
// 封裝的實體類,為了方便定義為public
static class PlayCard {
public PlayCard(String code, boolean used) {
this.code = code;
this.used = used;
}
// 撲克牌編號 ,即1,2,3
public String code;
// 撲克牌是否已被使用
public boolean used;
}
}
輸出結果
1,2,3,
1,3,2,
2,1,3,
2,3,1,
3,1,2,
3,2,1,
等式求解
想起來以前有個題目,計算恆等式,題目是a[0] * 100 + a[1] * 10 + a[2] +a[3] * 100 + a[4] * 10 + a[5] == a[6] * 100 + a[7] * 10 +a[8] 問a的組合有多少種?
Ps:a是0-9組成的,不可重復
下面我們有DFS來實現這個題目,記得在以前,肯定是寫九個for循環嵌套,現在我們嘗試利用上面的全排列來判斷,此時的輸出(邊界條件)修改為上面的等式,代碼不做過多的闡述了。沒有了9層循環的樣子。。。。
代碼
public class DFS3 {
static int PLAY_CARD_SIZE = 9;
static List<Number> playCards = new ArrayList<>();
static int[] a = new int[PLAY_CARD_SIZE];
static {
for (int i = 1; i <= PLAY_CARD_SIZE; i++) {
playCards.add(new Number(i, false));
}
}
public static void main(String[] args) {
dfs(0);
}
private static void dfs(int startIndex) {
if (startIndex == playCards.size()) {
if (checkNumber()) {
for (int i=0;i<PLAY_CARD_SIZE;i++){
System.out.print(a[i]+",");
}
System.out.println();
}
return;
}
for (int i = 0; i < playCards.size(); i++) {
if (!playCards.get(i).used) {
playCards.get(i).used = true;
a[startIndex] = playCards.get(i).code;
dfs(startIndex + 1);
playCards.get(i).used = false;
}
}
}
/**
* 判斷搜索邊界
*
* @return
*/
private static boolean checkNumber() {
if(a[0] * 100 + a[1] * 10 + a[2] +a[3] * 100 + a[4] * 10 + a[5] == a[6] * 100 + a[7] * 10 +a[8])
return true;
return false;
}
static class Number {
public Number(int code, boolean used) {
this.code = code;
this.used = used;
}
public int code;
public boolean used;
}
}
輸出結構
輸出結構也是蠻多的,這里摘錄幾個,可以自己測試下
1,2,4,6,5,9,7,8,3,
...
2,1,4,5,6,9,7,8,3,
...
3,1,4,6,5,8,9,7,2,
...
4,1,5,2,7,8,6,9,3,
...
5,9,6,2,4,1,8,3,7,
...
6,9,5,1,4,2,8,3,7,
...
7,8,4,1,5,2,9,3,6,
總結
DFS 是一個非常有意思的算法,在圖解中和BFS也屬於非常重要的算法了,多多理解,多多學習
