乘风破浪:LeetCode真题_037_Sudoku Solver
乘风破浪:LeetCode真题_037_Sudoku Solver
一、前言
这次我们对于上次的模型做一个扩展并求解。
二、Sudoku Solver
2.1 问题




2.2 分析与解决
这道题让我们按照规则,填写数独表上的内容,并且已知假设答案是唯一的。这里我们直到一个3*3的方格内的数字不能重复,因此须要填写完成,就需要所有的数字,因此我们可以尝试使用图遍历中的深度优先和广度优先遍历来不断地试探,直到得到最后的结果。同样的递归也能达成上面的要求。
class Solution {
int[][] hCounts;
int[][] vCounts;
int[][][] sqCounts;
public void solveSudoku(char[][] board) {
hCounts = new int[9][9];
vCounts = new int[9][9];
sqCounts = new int[3][3][9];
for (int row=0;row<9;row++) {
for (int col=0;col<9;col++)
if (board[row][col] != '.')
set(board, row, col, board[row][col]);
}
solve(board, 0, 0);
}
private boolean solve(char[][] board, int row, int col) {
if (row == board.length)
return true;
if (col == board[0].length)
return solve(board, row+1, 0);
if (board[row][col] != '.')
return solve(board, row, col+1);
for (int i=1;i<10;i++) {
char n = (char)('0' + i);
if (canAdd(row, col, n)) {
set(board, row, col, n);
if (solve(board, row, col+1))
return true;
unset(board, row, col, n);
}
}
return false;
}
private boolean canAdd(int row, int col, char c) {
int n = c-'0'-1;
return hCounts[row][n] == 0 && vCounts[col][n] == 0 && sqCounts[row/3][col/3][n] == 0;
}
private void set(char[][] board, int row, int col, char c) {
board[row][col] = c;
int n = c-'0'-1;
hCounts[row][n]++;
vCounts[col][n]++;
sqCounts[row/3][col/3][n]++;
}
private void unset(char[][] board, int row, int col, char c) {
board[row][col] = '.';
int n = c-'0'-1;
hCounts[row][n]--;
vCounts[col][n]--;
sqCounts[row/3][col/3][n]--;
}
}
这道题的算法可以说是非常经典的,进行了一些抽象,首先是初始化,通过三个数组来检验能不能添加元素进去。方法也很简单,如果存在过某个数值,就加一,然后再添加的时候需要判断一下是不是能够加入进去。
private boolean canAdd(int row, int col, char c) {
int n = c-'0'-1;
return hCounts[row][n] == 0 && vCounts[col][n] == 0 && sqCounts[row/3][col/3][n] == 0;
}
private void set(char[][] board, int row, int col, char c) {
board[row][col] = c;
int n = c-'0'-1;
hCounts[row][n]++;
vCounts[col][n]++;
sqCounts[row/3][col/3][n]++;
}
之后我们从最开始的[0,0]按行遍历,通过递归算法,如果能将字符加入进去就加入,继续调用,不能的话就撤回,然后继续遍历,先对每一行的每一列的元素进行遍历,当完成之后遍历下一行,直至结束。
private boolean solve(char[][] board, int row, int col) {
if (row == board.length) //已经遍历到表外了,结束,成功。
return true;
if (col == board[0].length) //开始下一行
return solve(board, row+1, 0);
if (board[row][col] != '.') //不为.则继续下一列。
return solve(board, row, col+1);
for (int i=1;i<10;i++) {
char n = (char)('0' + i);
if (canAdd(row, col, n)) {
set(board, row, col, n);
if (solve(board, row, col+1))
return true;
unset(board, row, col, n);//不成功,回退
}
}
return false; //最后都没成功,返回false
}
三、总结
涉及到图的问题,让我们想到了八皇后问题,同样的联想到这个题的解法。
乘风破浪:LeetCode真题_037_Sudoku Solver的更多相关文章
- 乘风破浪:LeetCode真题_041_First Missing Positive
乘风破浪:LeetCode真题_041_First Missing Positive 一.前言 这次的题目之所以说是难,其实还是在于对于某些空间和时间的限制. 二.First Missing Posi ...
- 乘风破浪:LeetCode真题_040_Combination Sum II
乘风破浪:LeetCode真题_040_Combination Sum II 一.前言 这次和上次的区别是元素不能重复使用了,这也简单,每一次去掉使用过的元素即可. 二.Combination Sum ...
- 乘风破浪:LeetCode真题_039_Combination Sum
乘风破浪:LeetCode真题_039_Combination Sum 一.前言 这一道题又是集合上面的问题,可以重复使用数字,来求得几个数之和等于目标. 二.Combination Sum ...
- 乘风破浪:LeetCode真题_038_Count and Say
乘风破浪:LeetCode真题_038_Count and Say 一.前言 这一道题目,很类似于小学的问题,但是如果硬是要将输入和结果产生数值上的联系就会产生混乱了,因此我们要打破思维定势. ...
- 乘风破浪:LeetCode真题_036_Valid Sudoku
乘风破浪:LeetCode真题_036_Valid Sudoku 一.前言 有的时候对于一些基础知识的掌握,对我们是至关重要的,比如ASCII重要字符的表示,比如一些基本类型的长度. 二.Valid ...
- 乘风破浪:LeetCode真题_035_Search Insert Position
乘风破浪:LeetCode真题_035_Search Insert Position 一.前言 这次的问题比较简单,也没有限制时间复杂度,但是要注意一些细节上的问题. 二.Search Insert ...
- 乘风破浪:LeetCode真题_034_Find First and Last Position of Element in Sorted Array
乘风破浪:LeetCode真题_034_Find First and Last Position of Element in Sorted Array 一.前言 这次我们还是要改造二分搜索,但是想法却 ...
- 乘风破浪:LeetCode真题_033_Search in Rotated Sorted Array
乘风破浪:LeetCode真题_033_Search in Rotated Sorted Array 一.前言 将传统的问题进行一些稍微的变形,这个时候我们可能无所适从了,因此还是实践出真知, ...
- 乘风破浪:LeetCode真题_032_Longest Valid Parentheses
乘风破浪:LeetCode真题_032_Longest Valid Parentheses 一.前言 这也是非常有意思的一个题目,我们之前已经遇到过两个这种括号的题目了,基本上都要用到堆栈来解决,这次 ...
随机推荐
- JBOSS Spring Web
jndi: <datasources> <xa-datasource> <jndi-name>jdbc/sss-local</jndi-name> &l ...
- Java 集合并交补
示例 package com.example; import java.util.ArrayList; import java.util.Arrays; import java.util.Collec ...
- C#Redis 常用key操作
一.前戏 在该系列的前几篇博客中,主要讲述的是与Redis数据类型相关的命令,如String.List.Set.Hashes和Sorted-Set.这些命令都具有一个共同点,即所有的操作都是针对与Ke ...
- 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 热更新Live2D
live2D是一个很强大的2D动画组件.我们可以使用AS3脚本对它进行热更新. live2D在Unity中的使用请看这里: 如何获取Live2D 总得来说,我们可以先去live2D官网下载它的Unit ...
- .34-浅析webpack源码之事件流make(3)
新年好呀~过个年光打游戏,function都写不顺溜了. 上一节的代码到这里了: // NormalModuleFactory的resolver事件流 this.plugin("resolv ...
- Spring基础(5): 构造函数注入无法处理循环依赖
public class Person{ public Leader leader; public Person(Leader l){ this.leader= l; } } public class ...
- Windows程序
Windows程序是事件驱动的,对于一个窗口,它的大部分例行维护是由系统维护的.每个窗口都有一个消息处理函数.在消息处理函数中,对传入的消息进行处理.系统内还有它自己的缺省消息处理函数. 客户 ...
- C# 异常语句 跳转语句 while循环 穷举法 迭代法
一 异常语句 ♦ try.....catch....finally 结构形式 try{ 可能会出错的代码语句 如果这里出错了,那么不会在继续下面的代码,而是直接进入catch中处理异常}catc ...
- PHP中按值传递和引用传递的区别
有次跟朋友讨论对象传值的方式时提到引用传值时,在大脑中搜索五秒钟,果断确定在这两个项目当中并没有用到.今天去问了一下度娘,顺便做了个小测试: 按值传递: 引用传递: 按值传递中原来参数的值在调用其他函 ...
- H5自适应屏幕分辨率大小
说明: ①:H5自适应不同分辨率的设备,其实主要就一句 <meta name="viewport" content="width=device-width,init ...