【算法基础】近期接触的一些JS基础算法题总结---数独校验

343 阅读5分钟

昨天挠了一下午头,好不容易做出了结果,看到别人的答案之后: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了。