Js算法與數據結構拾萃(6.5):回溯法解決數獨問題

回顧N皇后問題的解決方案,並沒有採用二維數組。但實際上思路依然和所謂「回溯法通用解決模板」是一致的。

  • result = []function backtrack(路徑, 選擇列表){ if 滿足結束條件: result.add(路徑) return result for 選擇 in 選擇列表: 做選擇 backtrack(路徑, 選擇列表) 撤銷選擇}

案例: 數獨問題(sudoku-solver)

對應leetcode第37題,難度:困難 https://leetcode.com/problems/sudoku-solver/

Write a program to solve a Sudoku puzzle by filling the empty cells.

A sudoku solution must satisfy all of the following rules:

1.Each of the digits 1-9 must occur exactly once in each row.2.Each of the digits 1-9 must occur exactly once in each column.3.Each of the the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.

Empty cells are indicated by the character '.'.

編寫一個程序,通過已填充的空格來解決數獨問題。

一個數獨的解法需遵循如下規則:

•數字 1-9 在每一行只能出現一次。•數字 1-9 在每一列只能出現一次。•數字 1-9 在每一個以粗實線分隔的 3×3 宮內只能出現一次。•空白格用 '.' 表示。

一個數獨。

答案被標成紅色。

提示

•給定的數獨序列只包含數字 1-9 和字符 '.' 。•你可以假設給定的數獨只有唯一解。•給定數獨永遠是 9×9 形式的。

通用解法

數獨問題的解題思路和N皇后是一致的。

1.逐行逐列遍歷2.依次填入1-9:看此數字是否通過校驗。•校驗不通過則回退。•校驗通過,調用自身程序,看能否求出正解•能:輸出結果•不能:繼續填入下個數

  • var solveSudoku = function(board) { // 判斷能否求出正解 const sudoku = board => { for (let row = 0; row < 9; row++) { for (let col = 0; col < 9; col++) { let cell = board[row][col] // 判斷是否有數 if (cell != '.') continue // 開始判斷1-9 for (let i = 1; i <= 9; i++) { if (find(board, row, col, i)) { //如果成立 => 落子,繼續調用sudoku往下求解 board[row][col] = i +'' // leetcode限制填入必須為字符串而非數字 // 如果能求出正解。返回true if (sudoku(board)){ return true }else{ // 否則當前步驟回歸上一步,繼續遍歷下個數 board[row][col] = '.' } } } // 遍歷完當前行,即可認為無解。 return false } } return true } sudoku(board) return board}

好了,回溯法總的解決框架來說似乎還是比較簡單的。另外一個難點在於find方法。

此處可在一個循環中進行判斷。

  • const find = (board, row, col, index) => { for (let i = 0; i < 9; i++) { // 判斷行 if (board[row][i] == index) { return false } // 判斷列 if (board[i][col] == index) { return false } // 判斷九宮格 let _Row = parseInt(row / 3) * 3 + parseInt(i / 3) let _Col = parseInt(col / 3) * 3 + (i % 3) if (board[_Row][_Col] == index) { return false } } return true}

那麼問題就解決了。

當然算法的複雜度還是很高的。也許可以再針對測試用例進一步優化。