xxleyi / loop_invariants

记录以及整理「循环不变式」视角下的算法题解

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

leetcode 542. 01 矩阵

xxleyi opened this issue · comments

题:

给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。

两个相邻元素间的距离为 1 。

示例 1:

输入:

0 0 0
0 1 0
0 0 0

输出:

0 0 0
0 1 0
0 0 0

示例 2:

输入:

0 0 0
0 1 0
1 1 1

输出:

0 0 0
0 1 0
1 2 1

注意:

给定矩阵的元素个数不超过 10000。
给定矩阵中至少有一个元素是 0。
矩阵中的元素只在四个方向上相邻: 上、下、左、右。

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


解:

这个题目,能一眼看出需要使用 bfs。但如何处理多个 0 的情况呢?自己并没有处理,导致复杂度陡增。此处涉及到一个巧妙的概念:超级起点。在之前的概念里,bfs 中初始化队列时,只会入队一个起点,但其实存在一种更为普遍的情况,就是存在超级起点:多个具体起点构成抽象意义上的单一起点

var updateMatrix = function(matrix) {
  // initialize loop invariant realted variable:
  // dist matrix filled with -1 (-1 means not seen)
  let m = matrix.length, n = matrix[0].length
  let dist = Array(m)
  for (let i = 0; i < m; i++) dist[i] = Array(n).fill(-1)

  // initialize queue and enqueue all zero positions with one helper loop
  // to maintain loop invariant before the main loop(bfs)
  let head = 0, tail = -1, queue = Array(m * n)
  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      if (matrix[i][j] === 0) {
        dist[i][j] = 0
        // update tail to maintain loop invariant
        queue[++tail] = [i, j]
      }
    }
  }

  // helper variable and function
  const dir = [[-1, 0], [1, 0], [0, -1], [0, 1]]
  const validIJ = (i, j) => i >= 0 && i < m && j >= 0 && j < n

  // main loop(bfs)
  while (queue[head] != null) {
    // dequeue one item
    let [i, j] = queue[head++]
    for (let [di, dj] of dir) {
      let [ni, nj] = [i + di, j + dj]
      // try to enqueue all valid positions which are unseen 
      if (validIJ(ni, nj) && dist[ni][nj] === -1) {
        // update dist
        dist[ni][nj] = dist[i][j] + 1
        // enqueue new item
        queue[++tail] = [ni, nj]
      }
    }
  }
  // loop termination: dist will be our answer
  return dist
}