剪枝

87 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情

写在前面

剪枝,在算法搜索中其实非常常用,不管是之前的广度优先算法(BFS)还是深度优先算法(DFS)、抑或是其他搜索算法。

那为什么需要剪枝呢?

前两篇我们遍历二叉树的时候,不管用的是广度优先算法还是深度优先算法,都是逐一遍历树的每个节点,这对于数据量小的树来说,当然没有问题,但如果把数据量放大,再逐一遍历,再进行回溯,效率就会很慢。这个时候就需要我们先进行判断,不满足要求的分支直接剪掉,这就是剪枝。

有效数独

描述

如下图,是个数独格,里面填写了部分数字,但未必有效,对有效的评判标准有三个:

  1. 数字 1-9 在每一行只能出现一次
  2. 数字 1-9 在每一列只能出现一次
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次

image.png

分析

判断有效数独,其实就是一种剪枝的行为,当我们在完成数独之前,第一步就是要判断是否是有效数独,且后面都会不断进行校验。

既然对有效数独的判断标准有三个,那我们逐个在判断即可。

  1. 先定义每组数据是否有效的方法,在执行过程中,任何一组不符合,即返回false,都符合则返回true
  2. 行判断最简单,分别校验每行是否有效
  3. 列判断需要用到二层循环来倒序组装成新的一组数来执行判断
  4. 块判断,总共有9块,成组放入对象中

代码

/**
 * @param {character[][]} board
 * @return {boolean}
 */
var isValidSudoku = function(board) {
  let _block = {}
  for (let i = 0; i < 9; i++) {
    // 行校验
    if(!_valid(board[i])) return false
    let _column = []
    let _blockRow = Math.floor(i/3)
    for (let j = 0; j < 9; j++) {
      _column.push(board[j][i])
      let _blockColomn = Math.floor(j/3)
      const _index = _blockRow*3+_blockColomn
      if (!_block[_index]) {
        _block[_index] = []
      }
      _block[_index].push(board[i][j])
    }
    // 列校验
    if(!_valid(_column)) return false
  }
  // 块校验
  for (let k = 0; k < 9; k++) {
    if(!_valid(_block[k])) return false
  }
  function _valid (arr = []) {
    const filter = arr.filter(val => val !== '.')
    const set = new Set(filter)
    return filter.length === set.size
  }
  return true
};