昨天挠了一下午头,好不容易做出了结果,看到别人的答案之后:I'm piece of ××××
题目:接收一个9×9的二维数组(参数包含9个数组项,每项又包含9个数字),创建一个函数doneOrNot
,判断每行、每列以及每个3×3区域内是否全部只包含一次数字 1-9
图例:
我的思路:
* 要分别校验行、列、块中是否符合条件,可以将整个数组按照行、列、块重新排列为三个不同的二维数组
* 暂时称之为行(row)、列(column)、块(block)。
* 声明一个新数组valid用于存放校验结果。
* 创建一个校验方法,分别校验行、列、块数组的各项中是否符合只包含一次数字1-9。
* 行、列、块数组将分别得到9次结果,将其结果作为Boolean保存到新数组valid中。
* 遍历valid,通过判断数组中是否全部为true来判断数独校验是否通过。
这次依然全部使用for循环来写,回头有空再优化一下吧。。。
那么首先,需要获取到行、列、块数组。因为行数组本来就是传入数组本身,所以只需要分别获取列、块即可。
function doneOrNot(board){
let column = []
let block = []
// 获取列数组 column
for(let i in board){
let arr = []
for(let j=1;j<10;j++){
arr.push(board[j-1][i])
// 只有 arr 中含有 9 项,才将其push到column中
if(j === 9){
column.push(arr)
}
}
}
// 获取块数组 block,四次循环,一个字:丑!
for(let n=0;n<3;n++){
for(let m=0;m<3;m++){
let arr = []
// x = 3*n 以及 y = 3*m,便是通过 n 和 m 将数组分割成了 3*3 的 9 块
// 此时,x 和 y 的范围就变成了 0-2,3-5,6-8
// 分别遍历这 9 块中的内容即可
for(let x=3*n;x<3*n+3;x++){
for(let y=3*m;y<3*m+3;y++){
arr.push(board[x][y])
// 只有 arr 中含有 9 项,才将其push到block中
if(arr.length === 9){
block.push(arr)
}
}
}
}
}
}
这样就获取到列以及块数组了,接下来需要一个校验方法:
关于校验方法我当时想的是用差运算(diff算法,因为刚写过不久,在上一篇中有总结)的来达到校验的目的,就是新加入一个数组valid = [1,2,3,4,5,6,7,8,9]
,用valid
和需要校验的数组做diff:用valid
减去需要校验的数组中所有的相同项。如果diff结束后valid
变成了一个空数组,证明需要校验的数组确实包含数字1-9,返回true
,否则返回false
。
当然还有更简单的方法: 直接校验传入数组中是否包含相同项,因为数组长度固定为9,如果出现了相同项,那么该数组肯定无法同时包含1-9中所有的数字,返回false
,否则返回true
。
这里为了复习就是用diff来做吧,这么些会导致方法更复杂。 (其实是当时写的时候没想到判断相同项)
function validator(arr){
let valid = [1,2,3,4,5,6,7,8,9]
function diff(arr,valid){
let diffArr = valid
for(let i in arr){
for(let j in diffArr){
if(diffArr[j] === arr[i]){
// 若发现相同项,则删除 diffArr中的该项,同时数组长度发生变化,需要重新进行diff
diffArr.splice(j,1)
diff(arr,diffArr)
}
}
}
// 判断 diffArr 即 valid 是否清空,若清空证明校验通过。
return diffArr.length===0?true:false
}
// 将diff的结果作为validator函数的返回值
return diff(arr,valid)
}
校验方法完成后将行、列、块元素传入其中进行校验
// 声明一个数组来存放行列块数组的校验结果,实在不知道叫什么,暂时叫做 rua
let rua = []
for(let i=0;i<9;i++){
// 因为只需要校验是不是有错不需要校验哪里错,所以顺序无所谓
rua.push(validator(board[i]))
rua.push(validator(column[i]))
rua.push(validator(block[i]))
}
rua
将会得到三个二维数组共计 27 次校验结果,最后遍历rua
。
for(let i in rua){
// 只要 rua 中任意一项不是true,就证明数独校验未通过,返回false作为结果
if(!rua[i]){
return false
}
}
// 全部为true,返回true。
return true
收工!
附上完整代码(仅供参考以及解释思路,请勿学习这种拉跨代码写法)
function doneOrNot(board){
let column = []
let block = []
// 获取列数组 column
for(let i in board){
let arr = []
for(let j=1;j<10;j++){
arr.push(board[j-1][i])
// 只有 arr 中含有 9 项,才将其push到column中
if(j === 9){
column.push(arr)
}
}
}
// 获取块数组 block,四次循环,一个字:丑!
for(let n=0;n<3;n++){
for(let m=0;m<3;m++){
let arr = []
// x = 3*n 以及 y = 3*m,便是通过 n 和 m 将数组分割成了 3*3 的 9 块
// 此时,x 和 y 的范围就变成了 0-2,3-5,6-8
// 分别遍历这 9 块中的内容即可
for(let x=3*n;x<3*n+3;x++){
for(let y=3*m;y<3*m+3;y++){
arr.push(board[x][y])
// 只有 arr 中含有 9 项,才将其push到block中
if(arr.length === 9){
block.push(arr)
}
}
}
}
}
function validator(arr){
let valid = [1,2,3,4,5,6,7,8,9]
function diff(arr,valid){
let diffArr = valid
for(let i in arr){
for(let j in diffArr){
if(diffArr[j] === arr[i]){
// 若发现相同项,则删除 diffArr中的该项,同时数组长度发生变化,需要重新进行diff
diffArr.splice(j,1)
diff(arr,diffArr)
}
}
}
// 判断 diffArr 即 valid 是否清空,若清空证明校验通过。
return diffArr.length===0?true:false
}
// 将diff的结果作为validator函数的返回值
return diff(arr,valid)
}
// 声明一个数组来存放行列块数组的校验结果,实在不知道叫什么,暂时叫做 rua
let rua = []
for(let i=0;i<9;i++){
// 因为只需要校验是不是有错不需要校验哪里错,所以顺序无所谓
rua.push(validator(board[i]))
rua.push(validator(column[i]))
rua.push(validator(block[i]))
}
for(let i in rua){
// 只要 rua 中任意一项不是true,就证明数独校验未通过,返回false作为结果
if(!rua[i]){
return false
}
}
// 全部为true,返回true。
return true
}
写在后面
是不是很丑?是不是很丑?是不是很丑?
虽然常言道“又不是不能用!”,但不管怎么说还是要经常优化一下代码,否则方法写的过长时再去优化,就会变得有些难以下手了。。。
那么到这里就是全部内容了,写的很乱,也有很多优化空间,后续慢慢优化后再来更新内容吧。
希望这个思路能成为某人解决某个问题时的参考,感谢你能看到这里,我就先run了。