LeetCode第37题思悟——解数独(sudoku-solver)

LeetCode第37题思悟——解数独(sudoku-solver)

知识点预告

  1. 多维数组的Map用法;
  2. 深度优先遍历算法的理解;
  3. 算法思维;

题目要求

编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则:

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例

一个数独。

一个示例

答案被标成红色。

Note:

给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我的思路

这道题实在是有很多话要说,想听听故事的伙伴可以继续,不愿意的伙伴可以直接进入下一节,因为对这道困难题我没有思路,只有回忆;

先谢谢你看到这里~。解数独是自己用Java语言编写的第一个程序,使用的编辑器是notepad++;当时我们的离散数学老师,布置了这样一道题,要求用Java语言实现,时间一周;

当时我们正在学C++,还没学Java,至于为啥要用Java,那是因为他下学期就要教我们Java啦;数独是个啥,那个时候还不知道,那怎么解题呢?当然是先了解一下数独是什么呀——于是,从图书馆借了一本关于数独的书,然后就走歪了,哈哈哈;

嗯,那本书讲解了不少关于求解数独的方法:行唯一法、列唯一法、块唯一法,这些解法好像统称为“唯余解法”;还有什么A、B小格都只能填1,2,那么1,2就被确定了,其余方格就不需要考虑1,2等逻辑技巧;可怕的是,我真的把这些技巧转换为了程序,然后我就得到了几个关于这些技巧的方法。那么怎么用它们解数独呢?我的方法是while循环,依次调用这些技巧,直到调用完没有数据更新,那么要么得到答案,要么技巧还不够多;

就这样,写了将近400多行代码,终于是把书上的题给解出来了;结果就是离散课的平时分得了100,然鹅也没什么用;

看到这道题,我立马把以前的代码翻了出来,结果自己都被震惊了:什么垃圾代码!!竟然用汉语拼音命名函数,for循环至少3层起步!变量也是i,j,k的;没错:当我写下那些代码的时候,只有我和上帝能看懂它,现在只有上帝能看懂了。

真的,没有夸张,谁都是从新人过来的嘛(虽然,我现在还是一只萌新,求关注?),耐着性子,把代码拷过来,然后提交,结果。。。。通不过,哈哈哈,只有多少用例,倒也记不清了,很快找到了原因:嗯,技巧不够多,如果把原来2阶的技巧扩展到3,4阶…那么还是有可能的;

既然没通过,题还是要写的,于是再次出征;这次倒是基于“唯余解法”和“尝试法”通过了测试,可是代码量仍然很多,300多行吧,我这里就不献丑了,哈哈哈;

废话到这里就说完了,该入正题啦;

优秀解法

boolean col[][];
boolean row[][];
boolean cell[][][];
public void solveSudoku(char[][] board) {
	col = new boolean[9][9];
	row = new boolean[9][9];
	cell = new boolean[3][3][9];
	for (int i = 0; i < 9; i++) {
		for (int j = 0; j < 9; j++) {
			if (board[i][j]!= '.') {
				int t = board[i][j] - '0'-1; 
				col[j][t] = row[i][t] = cell[i / 3][j / 3][t] = true;
			}
		}
	}
	dfs(board , 0 ,0 );
}
private boolean dfs(char[][] board, int x, int y) {
	if(y == 9 ) {x++;  y = 0;}
	if(x == 9) return true;
	if(board[x][y] != '.') return dfs(board , x , y+1);
	for(int i = 0 ; i < 9 ;i++) {
		if(!col[y][i]&&!row[x][i]&&!cell[x/3][y/3][i]) {
			board[x][y] = (char) ('1'+ i);
			col[y][i] = row[x][i] = cell[x / 3][y / 3][i] = true;
			if(dfs(board, x , y+1)) return true;
			board[x][y] = '.';
			col[y][i] = row[x][i] = cell[x / 3][y / 3][i] = false;
		}
	}
	return false;
}

差异分析

当发现自己费尽心思,写了三四百行代码,最终也解决了问题,而他人仅仅只需要50行不到,就解决了问题,而且运行更有效率时,肯定很震惊吧;没错,这就是知识的力量。

深度优先遍历算法,常见的树、图等数据结构的一种遍历方法;一种,不撞南墙不回头的解法;

leetcode讨论区中看到过这样的话:如果使用“笨办法”,也能解决问题,那也不错;但是如果只能使用“笨办法”,那么在算法这条路上是走不远的;送给正在码字的自己;

从开始总结以往解法开始,我就发现了自己的缺点:用人脑去思考问题,用人的思维解决问题;解决问题的手段太接近人,从而失去了算法的美感;

计算机有其运行的法则和特点,就像牵牛需要牵鼻子一样,要得其法;不得其法,就像前面提到的,走不远;自己之所以没有继续刷题,而是选择回顾总结,不是不想刷题,而是真真切切地感觉到了自己的不得其法,用个高大上点的词,就是没有算法思维。刷题事倍功半,倒不如夯实基础,这里的基础指的是思维模式和方法的基础;

好啦,来看看总结吧~

知识点小结

  1. 多维数组的Map用法:A[x] [y] [z] = k ,表示xyz经过A和k关联;在key的维度较高时,使用数组倒是很方便建立联系;
  2. 深度优先遍历算法的理解;
  3. 算法思维:这是一种看待问题、解决问题的角度;
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值