leetcode刷题-BFS&DFS

358 阅读4分钟

leetcode刷题-BFS&DFS

1、完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

var numSquares = function(n) {
  const squares = getSquareNums(n);
  const queue = [];
  let layer = 1;
  const visited = new Array(n + 1).fill(0);
  visited[n] = 1;
  queue.push(n);

  while(queue.length !== 0) {
    let size = queue.length;

    while(size-- > 0) {
      const cur = queue.shift();
      
      for (const square of squares) {
        const next = cur - square;
        if (next < 0) break;
        if (next === 0) return layer;
        if (visited[next] === 0) {
          queue.push(next);
          visited[next] = 1;
        }
      }
    }

    layer++;
  }

  return layer;
};


// 获取小于等于n的完全平方数
function getSquareNums(n) {
  const squareArr = [];
  let square = 1;
  let diff = 3;

  while(square <= n) {
    squareArr.push(square);
    square += diff;
    diff += 2;
  }

  return squareArr;
}

2、单词接龙

字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:

序列中第一个单词是 beginWord 。 序列中最后一个单词是 endWord 。 每次转换只能改变一个字母。 转换过程中的中间单词必须是字典 wordList 中的单词。 给你两个单词 beginWord 和 endWord 和一个字典 wordList ,找到从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。

var ladderLength = function (beginWord, endWord, wordList) {
  const n = wordList.length;
  const begin = wordList.indexOf(beginWord);
  const end = wordList.indexOf(endWord);
  if (end === -1) return 0;

  const adjMatrix = generateAdjMatrix(wordList, n);

  const queue = [];
  const visited = new Array(n).fill(0);
  let layer = 2;
  for (let i = 0; i < n; i++) {
    const word = wordList[i];
    if (getDiffNumber(beginWord, word)) {
      queue.push(i);
      visited[i] = 1;
    }
  }

  while (queue.length !== 0) {
    let size = queue.length;

    while (size-- > 0) {
      const cur = queue.shift();
      if (cur === end) return layer;

      for (let i of adjMatrix[cur]) {
        if (visited[i] === 0) {
          queue.push(i);
          visited[i] = 1;
        }
      }
    }

    layer++;
  }

  return 0;
};

function generateAdjMatrix(wordList, n) {
  const arr = Array.from({ length: n }, () => new Array());
  for (let i = 0; i < n; i++) {
    for (let j = 0; j < n; j++) {
      if (i === j) continue;
      if (getDiffNumber(wordList[i], wordList[j])) {
        arr[i].push(j);
      }
    }
  }
  return arr;
}

function getDiffNumber(str1, str2) {
  const m = str1.length, n = str2.length;
  let left = 0, right = 0;
  let diff = 0;

  while (left < m && right < n) {
    if (str1[left] !== str2[right]) {
      diff++;
      if (diff > 1) return false;
    }
    left++;
    right++;
  }

  return diff === 1;
}

3、岛屿的最大面积

给定一个包含了一些 0 和 1 的非空二维数组 grid 。

一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)

let m = 0, n = 0;
const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];

var maxAreaOfIsland = function(grid) {
  if (grid.length === 0 || grid[0].length === 0) return 0;
  m = grid.length;
  n = grid[0].length;

  let maxArea = 0;

  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      const area = dfs(grid, i, j);
      maxArea = area > maxArea ? area : maxArea;
    }
  }

  return maxArea;
};

const dfs = function(grid, i, j) {
  if (i < 0 || i >= m || j < 0 || j >=n || grid[i][j] === 0) {
    return 0;
  }

  let area = 1;
  grid[i][j] = 0;

  for (let dir of dirs) {
    const x = i + dir[0];
    const y = j + dir[1];
    area += dfs(grid, x, y);
  }

  return area;
}

4、岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

let m = 0, n = 0;
const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];

var numIslands = function(grid) {
  m = grid.length;
  n = grid[0].length;
  let areaCount = 0;
  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      if (grid[i][j] === '1') {
        dfs(grid, i, j);
        areaCount++;
      }
    }
  }

  return areaCount;
};

const dfs = function(grid, i, j) {
  if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] === '0') {
    return;
  }
  grid[i][j] = '0';
  for (let dir of dirs) {
    dfs(grid, i + dir[0], j + dir[1]);
  }
}

5、省份数量

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

var findCircleNum = function(isConnected) {
  const n = isConnected.length;
  const visited = new Array(n).fill(0);
  let provinceNum = 0;

  for (let i = 0; i < n; i++) {
    if (visited[i] === 0) {
      provinceNum++;
      dfs(isConnected, i, visited, n);
    }
  }

  return provinceNum;
};

const dfs = function(isConnected, i, visited, n) {
  if (visited[i] === 1) {
    return;
  }

  visited[i] = 1;

  for (let j = 0; j < n; j++) {
    if (i === j) continue;
    if (isConnected[i][j] === 1) {
      dfs(isConnected, j, visited, n);
    }
  }
}

6、被围绕的区域

给你一个 m x n 的矩阵 board ,由若干字符 'X''O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O''X' 填充。

let m = 0, n = 0;
const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];

var solve = function(board) {
  m = board.length, n = board[0].length;

  for (let i = 0; i < m; i++) {
    if (board[i][0] === 'O') {
      dfs(board, i, 0);
    }
    if (board[i][n - 1] === 'O') {
      dfs(board, i, n - 1);
    }
  }

  for (let i = 0; i < n; i++) {
    if (board[0][i] === 'O') {
      dfs(board, 0, i);
    }
    if (board[m - 1][i] === 'O') {
      dfs(board, m - 1, i);
    }
  }

  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      if (board[i][j] === 'O') {
        board[i][j] = 'X';
      } else if (board[i][j] === 'y') {
        board[i][j] = 'O';
      }
    }
  }
};

function dfs(board, i, j) {
  if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] !== 'O') {
    return;
  }
  board[i][j] = 'y';

  for (let dir of dirs) {
    dfs(board, i + dir[0], j + dir[1]);
  }
}

7、太平洋大西洋水流问题

给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。

规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。

请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。

/**
 * @param {number[][]} heights
 * @return {number[][]}
 */

let m = 0, n = 0;
let canReachP = null; 
let canReachW = null;
const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];

var pacificAtlantic = function(heights) {
  m = heights.length;
  if (m === 0) return [];
  n = heights[0].length;

  init();

  for (let i = 0; i < m; i++) {
    dfs(heights, i, 0, canReachP);
    dfs(heights, i, n - 1, canReachW);
  }

  for (let i = 0; i < n; i++) {
    dfs(heights, 0, i, canReachP);
    dfs(heights, m - 1, i, canReachW);
  }

  const res = [];

  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      if (canReachP[i][j] === 1 && canReachW[i][j] === 1) {
        res.push([i, j]);
      }
    }
  }

  return res;

};

function init() {
  canReachP = Array.from({length: m}, () => new Array(n).fill(0));
  canReachW = Array.from({length: m}, () => new Array(n).fill(0));
}

function dfs(heights, i, j, canReachArr) {
  if (canReachArr[i][j] === 1) {
    return;
  }

  canReachArr[i][j] = 1;

  for (let dir of dirs) {
    const x = i + dir[0];
    const y = j + dir[1];

    if (isInArr(x, y) && heights[x][y] >= heights[i][j]) {
      dfs(heights, x, y, canReachArr);
    }        
  }
}

function isInArr(i, j) {
  return !(i < 0 || i >= m || j < 0 || j >= n);
}