日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

LeetCode算法题11:递归和回溯-解数独

發布時間:2025/6/17 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LeetCode算法题11:递归和回溯-解数独 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 解數獨
    • 回溯 :
    • 僅僅在實現方式上有區別
  • 總結


解數獨

??????題目鏈接:https://leetcode-cn.com/problems/sudoku-solver/

??????題目描述:編寫一個程序,通過填充空格來解決數獨問題。

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

數字 1-9 在每一行只能出現一次。
數字 1-9 在每一列只能出現一次。
數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。(請參考示例圖)
數獨部分空格內已填入了數字,空白格用 ‘.’ 表示。

回溯 :

??????思路,運用回溯算法的思想,在 N 皇后問題的基礎上主要解決一個問題:如何描述限制條件?每次在一個位置上放置元素時,需要滿足同行同列和所在九宮格內沒有重復元素,那么可以用 Set 集合來描述這個限制條件。
??????所以回溯類型的題目都不能判斷出當前解是否為最優解,而是只能得到一個合適的解,對于本題而言,已明確告訴僅有一個唯一解,所以直接結束即可。參考代碼如下(帶注釋):

boolean f=false;//這個全局變量是為了讓程序在得到唯一解的時候進行遞歸的一層層返回,不再進行多余的無關的操作。public void solveSudoku(char[][] board) {List<Set<Character>> rows = new ArrayList<>(9);// 9 個Set用來保存每一行已有的數,Set后面用來判斷后面是否有重復List<Set<Character>> columns = new ArrayList<>(9);// 同上,保存每一列已有的數List<Set<Character>> grid9 = new ArrayList<>(9);// 同上,保存每一個小九宮格中已有的數,九宮格從左到右,再從上到下為:0,1,2 3,4,5 6,7,8Set<Character> row = new HashSet<>();Set<Character> column = new HashSet<>();List<int[]> m = new ArrayList<>();//保存所有'.'的坐標,將所有待填充數的位置坐標保存到列表中。Set<Character> g1 = new HashSet<>();Set<Character> g2 = new HashSet<>();Set<Character> g3 = new HashSet<>();//對所有集合均進行初始化for (int i = 0; i < 9; i++) {//初始化 rows、columns 和 grid9 .for (int j = 0; j < 9; j++) {if (board[i][j] != '.') {row.add(board[i][j]);//初始化 rows//初始化 grid9if (j < 3)g1.add(board[i][j]);else if (j < 6)g2.add(board[i][j]);elseg3.add(board[i][j]);} elsem.add(new int[]{i, j});//初始化 mif (board[j][i] != '.')column.add(board[j][i]);//初始化 columns}if ((i + 1) % 3 == 0) {grid9.add(new HashSet<>(g1));grid9.add(new HashSet<>(g2));grid9.add(new HashSet<>(g3));g1.clear();g2.clear();g3.clear();}rows.add(new HashSet<>(row));columns.add(new HashSet<>(column));row.clear();column.clear();}//初始化完成solve(m, board, rows, columns, grid9, m.size(), 0);}public void solve(List<int[]> m, char[][] board, List<Set<Character>> rows, List<Set<Character>> columns, List<Set<Character>> grid9, int nums, int count) {if (count == nums) {//當最后一個位置上的數已經填充好之后,此時需要返回,標記f為true,表示已找到唯一解了f=true;return ;}int i = m.get(count)[0], j = m.get(count)[1];//得到當前需要填充的行列坐標for (Character k = '1'; k <= '9'; k++) {//依照題意,判斷當前數 k 是否合法,即集合中是否已經有重復的數if (rows.get(i).contains(k))continue;if (columns.get(j).contains(k))continue;int location = (i / 3)*3 + j / 3;//計算在 i,j處的元素處于哪個九宮格。if (grid9.get(location).contains(k))continue;//找到一個可行的 k 時,分別添加進三個集合rows.get(i).add(k);columns.get(j).add(k);grid9.get(location).add(k);board[i][j] = k;//設置 board[i][j] 的值為ksolve(m, board, rows, columns, grid9, nums, count + 1);//count加一,在下一個位置上進行填充/*if(!f){//如果找到了唯一解(f為true),solve 函數需要一層層直接返回,后面這些代碼不應該被執行。board[i][j] = '.'; //回退到上一個狀態,并從集合里移出該 k 值。rows.get(i).remove(k);columns.get(j).remove(k);grid9.get(location).remove(k);}*/if(f)break;board[i][j] = '.';rows.get(i).remove(k);columns.get(j).remove(k);grid9.get(location).remove(k);}}

?????? 有些東西需要說明:上面算法中,若是去掉全局變量 f 和相關的語句的話,程序執行完畢之后,board 的值就會又變為初始狀態,狀態變化為:初始狀態 -> 找到唯一解 -> 又回到初始狀態。并且程序在接下來做的工作為"試圖找到下一個可行的解“,但實際上并沒有可行的解了,所做的均是無用功。 本題和之前的回溯算法對比,比如 N-皇后問題,區別在于是否只有一個唯一解,但這也不是最大的區別,最大的區別在于 N-皇后這些問題在找到一個可能的解之后是將該解保存下來了,而本題沒有采用這種做法,雖然也可以這樣做,但沒必要(沒必要浪費時間)。

?????? 加上 f 之后,因為不再執行賦值語句了,所以 board 中的元素一直都保持唯一解不變,并且三個集合也不會在移出元素,所以后面的 for 循環會找不到任何一個可行的 k ,程序便會由此一步步退出。其實直接加上 break 語句更好,這樣后面的 for 循環直接得不到執行的機會了,更加省事,如上面代碼所示。

僅僅在實現方式上有區別

??????此文鏈接:https://mp.weixin.qq.com/s?__biz=MzAxODQxMDM0Mw==&mid=2247492800&idx=2&sn=47bf7232fe5fd533aaf683850f3bd5c7&scene=21#wechat_redirect 它和自己實現的代碼在思想基本差不多,但是其代碼實現方式可供參考借鑒。如下:

public void solveSudoku(char[][] board) {backtrack(board,0,0);//從位置(0,0)開始判斷}boolean backtrack(char[][] board, int i, int j) {int m = 9, n = 9;if (j == n) {// 窮舉到最后一列的話就換到下一行重新開始。return backtrack(board, i + 1, 0);}if (i == m) {// 找到一個可行解,觸發 base casereturn true;}if (board[i][j] != '.') {// 如果有預設數字,不用我們窮舉return backtrack(board, i, j + 1);}for (char ch = '1'; ch <= '9'; ch++) {// 如果遇到不合法的數字,就跳過if (!isValid(board, i, j, ch))continue;board[i][j] = ch;// 如果找到一個可行解,立即結束if (backtrack(board, i, j + 1)) {return true;}board[i][j] = '.';}// 窮舉完 1~9,依然沒有找到可行解,此路不通return false;}boolean isValid(char[][] board, int r, int c, char n) {for (int i = 0; i < 9; i++) {// 判斷行是否存在重復if (board[r][i] == n) return false;// 判斷列是否存在重復if (board[i][c] == n) return false;// 判斷 3 x 3 方框是否存在重復if (board[(r/3)*3 + i/3][(c/3)*3 + i%3] == n)return false;}return true;}

總結

??????對了,還有本題的官方題解可供參考,自己實現的代碼時間復雜度有點高…

總結

以上是生活随笔為你收集整理的LeetCode算法题11:递归和回溯-解数独的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。