深度優先算法(dfs)
概念:進行某種數據查找時,使用遞歸的思想對當前節點的每一種可能性進行逐個嘗試,如果當前節點是末端節點且包含滿足的條件,則逐層返回成功結束,如果當前節點滿足條件,並且還有下掛節點時,則根據下掛節點執行的情況判斷當前節點數據是否合適,如果當前節點沒有滿足條件則需要回退當前節點修改的信息,並返回失敗,讓上一個節點執行其餘場景。
比例:有一個全二叉樹,每一個節點都有一個value,請獲取一條和爲10的分支,全二叉樹如下圖所示:
如果使用深度優先的搜索方式,此時的邏輯大概如下;
a.開始節點時node1,vaule是5,我們優先選擇左側子樹進行遍歷
b.現在的節點是(node1-node2),value是8,此時還不是末端節點,我們選擇node2的左側子樹
c.現在的節點是(node1-node2-node4),value是14,此時node4是末端節點,但是value不等於10,因此需要將node4移除,現在的節點是(node1-node2),value是8,此時選擇node2的右側子樹
d.現在的節點是(node1-node2-node5),vaule是9,此時node5是末端節點,但是value不等於10,因此需要將node5移除,現在的節點是(node1-node2),value是8.
e.因爲node2下的所有節點都不滿足要求,因此需要將node2移除,現在的節點是(node1),value5,此時選擇node1的右側子樹
f.現在的節點是(node1-node3),vaule是7,此時還不是末端節點,我們選擇node3左側的子樹
g.現在的節點是(node1-node3-node6),vaule是10,因爲node6是葉子節點,並且value是10已經滿足要求,此時直接結束
題:給出一個9*9的數獨,請使用深度優先搜索的方式計算出位置位置的數字(未知數字用0表示)
package com.anran.example.test;
import java.util.*;
/**
* 數獨
*
* 樣例1
* 輸入:
* 9 1 0 0 7 4 0 0 0
* 0 0 0 6 0 0 2 4 0
* 4 0 2 0 0 0 0 0 6
* 0 0 0 5 0 0 0 3 0
* 2 4 8 0 0 0 1 0 5
* 7 0 0 8 4 0 0 6 0
* 0 0 3 0 8 2 0 0 1
* 1 0 0 0 0 5 8 0 0
* 0 2 4 0 6 0 3 0 0
*
* 輸出:
* 9 1 6 2 7 4 5 8 3
* 3 5 7 6 1 8 2 4 9
* 4 8 2 9 5 3 7 1 6
* 6 9 1 5 2 7 4 3 8
* 2 4 8 3 9 6 1 7 5
* 7 3 5 8 4 1 9 6 2
* 5 7 3 4 8 2 6 9 1
* 1 6 9 7 3 5 8 2 4
* 8 2 4 1 6 9 3 5 7
*
* 樣例2
* 輸入:
* 5 3 9 1 0 0 0 0 0
* 0 0 0 9 0 5 0 6 0
* 8 0 0 4 0 0 0 9 1
* 0 5 0 0 8 0 2 4 0
* 0 0 6 0 3 0 8 0 0
* 0 0 8 2 0 0 6 0 0
* 9 0 0 0 4 7 0 0 0
* 0 0 0 0 0 2 0 3 7
* 3 4 0 0 0 0 0 0 2
*
* 輸出:
* 5 3 9 1 6 8 7 2 4
* 1 7 4 9 2 5 3 6 8
* 8 6 2 4 7 3 5 9 1
* 7 5 3 6 8 1 2 4 9
* 2 9 6 7 3 4 8 1 5
* 4 1 8 2 5 9 6 7 3
* 9 2 5 3 4 7 1 8 6
* 6 8 1 5 9 2 4 3 7
* 3 4 7 8 1 6 9 5 2
*/
public class Main5 {
//邏輯:從前往後每一個節點如果不確定,每一種場景都進行匹配操作,如果後續節點出現無法滿足要去的數據,則回到上一個節點,執行
// 其餘場景,如果滿足要求則直接結束
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
// 每一個while處理一下9*9的數據
while(sc.hasNext()){
//存放所有數據
int[][] datas = new int[9][9];
// 輸入
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
datas[i][j] = sc.nextInt();
}
}
if (dfs(0, 0, datas)) {
// 輸出
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
System.out.print(datas[i][j] + " ");
}
System.out.println("");
}
} else {
System.out.println("Error");
}
}
sc.close();
}
private static boolean dfs(int line, int row, int[][] datas) {
if (line == 9 && row == 0) {
return true;
}
if (datas[line][row] != 0) {
// 已經明確的信息,直接跳過
return dfs(getNextLine(line, row), getNextRow(row), datas);
} else {
for (int i = 1; i <= 9; i++) {
// 判斷行、列、3*3單元格是否包含這個數字
if (checkNum(i, line, row, datas)) {
// 當前數字未使用,添加到datas中
datas[line][row] = i;
//使用遞歸計算後面的
if (dfs(getNextLine(line, row), getNextRow(row), datas)) {
// 如果後面的滿足要求,則直接結束
return true;
}
// 如果沒有結束,當前節點需要還原成原始信息
datas[line][row] = 0;
}
}
return false;
}
}
private static boolean checkNum(int currentNum, int line, int row, int[][] datas) {
// 判斷行和列中是否包含數字
for (int i = 0; i < 9; i++) {
if (currentNum == datas[line][i] || currentNum == datas[i][row]) {
return false;
}
}
// 判斷3*3單元格中是否包含數字
for (int i = line/3*3; i < line/3*3+3; i++) {
for (int j = row/3*3; j < row/3*3+3; j++){
if (currentNum == datas[i][j]) {
return false;
}
}
}
return true;
}
private static int getNextLine(int line, int row) {
return row == 8 ? line + 1 : line;
}
private static int getNextRow(int row) {
return row == 8 ? 0 : row + 1;
}
}
未完待續 。。。