9月算法打卡

2022/12/17 算法

# 2022-09-01

# 外观数列

在这里插入图片描述

后一项是前一项的描述

获取第 n 项数列,需要获取第 n-1 项数列。由此得出要采用递归回溯的形式

/**
 * @param {number} n
 * @return {string}
 */
var countAndSay = function (n) {
  if (n == 1) return "1";
  // 上一串字符串
  let prevSeq = countAndSay(n - 1);

  // 返回结果
  let res = "";

  // 上一个数字
  let lastNum = prevSeq.charAt(0);
  // 出现的次数
  let numCount = 0;

  for (let i = 0; i < prevSeq.length; i++) {
    if (prevSeq.charAt(i) == lastNum) {
      ++numCount;
    } else {
      res += numCount;
      res += lastNum;

      // 更新上一个数字
      lastNum = prevSeq.charAt(i);
      numCount = 1;
    }

    // 特殊情况
    if (i == prevSeq.length - 1) {
      res += numCount;
      res += lastNum;
    }
  }
  return res;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 组合总合

在这里插入图片描述

关键点:无重复同一个数字可以无限制重复被选取

采用递归回溯的形式

1、将数组进行升序排序

2、遍历数组进行递归

  • 关键:每次递归前需要判断当前数值是否小于目标值,小于则才可以添加进去
  • 关键:当前数值进行递归结束后,需要将当前数值进行回溯(即去除该值)

3、递归结束的标志:目标值为 0

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum = function (candidates, target) {
  // 升序
  candidates.sort((a, b) => a - b);
  // 结果
  let res = [];

  let dfs = function (start, target, comine) {
    // 满足条件
    if (target == 0) {
      res.push([...comine]);
    }

    for (let i = start; i < candidates.length; i++) {
      // 当前值大于目标值,无需添加
      if (target < candidates[i]) return;
      // 该值可能满足条件
      comine.push(candidates[i]);
      dfs(i, target - candidates[i], comine);
      comine.pop(); // 回溯
    }
  };
  dfs(0, target, []);
  return res;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 2022-09-02

# 组合总和 II

在这里插入图片描述

关键点:每一个数字只能使用一次

实现逻辑跟上面的一致,只不过在递归前需要添加一个判断条件

当前位置的数值等于前一个数值时,并且当前数值位置要大于 start,则该值不能添加进去

if (i > start && candidates[i] == candidates[i - 1]) continue
1
/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum2 = function (candidates, target) {
  candidates.sort((a, b) => a - b);
  let res = [];
  let dfs = function (start, target, combin) {
    // 剪枝
    if (target == 0) res.push([...combin]);

    for (let i = start; i < candidates.length; i++) {
      if (candidates[i] > target) break;
      // 打头元素不能相同,不然重复
      if (i > start && candidates[i] === candidates[i - 1]) continue;

      combin.push(candidates[i]);
      dfs(i + 1, target - candidates[i], combin);
      combin.pop();
    }
  };
  dfs(0, target, []);
  return res;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 组合总和 III

在这里插入图片描述

实现逻辑跟组合总和 III 一致

/**
 * @param {number} k
 * @param {number} n
 * @return {number[][]}
 */
var combinationSum3 = function (k, n) {
  let candidates = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  let res = [];
  let dfs = function (start, target, combin) {
    if (combin.length == k && target == 0) {
      res.push([...combin]);
      return;
    }

    for (let i = start; i < candidates.length; i++) {
      if (candidates[i] > target) break;

      if (i > start && candidates[i] == candidates[i - 1]) continue;

      combin.push(candidates[i]);
      dfs(i + 1, target - candidates[i], combin);
      combin.pop();
    }
  };
  dfs(0, n, []);
  return res;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 2022-09-03

# 解数独

在这里插入图片描述

1、需要实现 isValid() 判断当前填入值后,当前数独是否有效。

2、从 1 到 9 不断去尝试填入的值是否有效

/**
 * @param {character[][]} board
 * @return {void} Do not return anything, modify board in-place instead.
 */
var solveSudoku = function (board) {
  function isValid(row, col, val, board) {
    // 行不能重复
    for (let i = 0; i < 9; i++) {
      if (board[row][i] === val) return false;
    }
    // 列不能重复
    for (let i = 0; i < 9; i++) {
      if (board[i][col] === val) return false;
    }
    let boxRow = Math.floor(row / 3) * 3;
    let boxCol = Math.floor(col / 3) * 3;

    for (let i = boxRow; i < boxRow + 3; i++) {
      for (let j = boxCol; j < boxCol + 3; j++) {
        if (board[i][j] === val) return false;
      }
    }

    return true;
  }

  let helper = function () {
    for (let i = 0; i < 9; i++) {
      for (let j = 0; j < 9; j++) {
        if (board[i][j] !== ".") continue;
        // 不断尝试
        for (let val = 1; val <= 9; val++) {
          if (isValid(i, j, `${val}`, board)) {
            board[i][j] = `${val}`;
            if (helper()) return true;
            // 当前位置 board[i][j] 不能填入数字 val
            board[i][j] = ".";
          }
        }
        return false;
      }
    }
    return true;
  };
  helper();
  return board;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 接雨水

在这里插入图片描述

1、左右指针,左指针在最左边,右指针在最右边,两者不断向中间靠拢

2、移动前需要判断 当前左指针指向的位置 height[l],与当前保存的左边界最大值 lmax 进行比较,并保存最大值

3、右指针同理

4、此时若 左边界(lmax)小于右边界(rmax),则左边界向中间移动。反之,右向中间移动

5、移动过程中计算当前位置接到的雨水

  • res += (lmax - height[l]) lmax < rmax
  • res += (rmax - height[r]) rmax <= lmax
/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function (height) {
  let res = 0;
  let l = 0;
  let r = height.length - 1;

  let lmax = 0; // 左边最大值
  let rmax = 0; // 右边最大值

  while (l < r) {
    // 更新最大值
    lmax = Math.max(lmax, height[l]);
    rmax = Math.max(rmax, height[r]);

    if (lmax < rmax) {
      // 左边短于右边
      res += lmax - height[l];
      l++;
    } else {
      // 右边短于左边
      res += rmax - height[r];
      r--;
    }
  }
  return res;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 2022-09-04

# 缺失的第一个正数

在这里插入图片描述

题意是根据所给数组中没有出现的最小正整数。

解法:

  • 创建 nums.length 个桶,每一个桶对应存发该位置应该存放的数字(i+1 == num
  • 最后遍历所有桶,那个桶为空,说明缺少的正整数为i + 1

交换的必须满足以下几个条件:

  • 交换值 val 必须在val > 0 && val <= len;
/**
 * @param {number[]} nums
 * @return {number}
 */
var firstMissingPositive = function (nums) {
  let len = nums.length;
  let swap = function (nums, i, j) {
    let temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
  };

  // 将每一个值找到对应桶的位置
  for (let i = 0; i < len; ) {
    let val = nums[i];
    if (val > 0 && val <= len && val != i + 1 && nums[val - 1] != val) {
      swap(nums, i, val - 1);
    } else {
      i++;
    }
  }

  // 找每一个桶是否位置对应
  for (let i = 0; i < len; i++) {
    if (nums[i] != i + 1) return i + 1;
  }
  return len + 1;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 字符串相乘

在这里插入图片描述

字符串相乘

关键点:

  • 对应位置相乘的值对应放入的位置为(i + j),进位为(i + j + 1)
  • 计算结束后,需要判断结果数组第一位 res[0]是否为 0,为 0 需要删除
/**
 * @param {string} num1
 * @param {string} num2
 * @return {string}
 */
var multiply = function (num1, num2) {
  if (num1 == "0" || num2 == "0") return "0";

  let len1 = num1.length;
  let len2 = num2.length;

  // 结果数组
  let res = new Array(len1 + len2).fill(0);

  for (let i = len1 - 1; i >= 0; i--) {
    for (let j = len2 - 1; j >= 0; j--) {
      const mul = num1[i] * num2[j];
      // 乘积在结果数组的位置
      const p1 = j + i;
      // 进位位置
      const p2 = i + j + 1;

      const sum = res[p2] + mul;
      res[p1] += Math.floor(sum / 10);
      res[p2] = sum % 10;
    }
  }

  if (res[0] == 0) res.shift();
  return res.join("");
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 2022-09-05

# 跳跃游戏

在这里插入图片描述

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canJump = function (nums) {
  let len = nums.length;
  // 距离
  let distance = len - 1;
  // 倒数第二个开始
  let last = len - 2;

  for (let i = last; i >= 0; i--) {
    // nums[i] + i 代表当前位置(i)能够到达最远右边的距离
    if (nums[i] + i >= distance) {
      // 进入到这里,说明当前 i 可以到达最右边

      // 接着distance更新为(最左边到达i的距离)
      distance = i;

      // 判断i之前有哪些点可以到达 i,由此循环
    }
  }
  // 遍历完数组, distance 为 0 说明可以进行跳跃
  return distance === 0;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 跳跃游戏 II

在这里插入图片描述

/**
 * @param {number[]} nums
 * @return {number}
 */
var jump = function (nums) {
  // 最远距离
  let maxPos = 0;
  // 最远点
  let end = 0;
  // 步长
  let ans = 0;
  for (let i = 0; i < nums.length - 1; i++) {
    maxPos = Math.max(maxPos, nums[i] + i);
    if (i == end) {
      // 到达最远边界
      end = maxPos;
      ans++;
    }
  }
  return ans;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 2022-09-06

# 全排列

在这里插入图片描述

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permute = function (nums) {
  let res = [];
  let dfs = function (nums, tmp, visited) {
    if (tmp.length == nums.length) {
      res.push([...tmp]);
      return;
    }

    for (let i = 0; i < nums.length; i++) {
      if (visited[i] == 1) continue;
      visited[i] = 1;

      tmp.push(nums[i]);

      dfs(nums, tmp, visited);

      visited[i] = 0;
      tmp.pop();
    }
  };
  dfs(nums, [], []);
  return res;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 全排列 II

在这里插入图片描述

var permuteUnique = function (nums) {
  const ans = [];
  const vis = new Array(nums.length).fill(false);
  const dfs = (size, tmp) => {
    if (size === nums.length) {
      ans.push([...tmp]);
      return;
    }

    for (let i = 0; i < nums.length; ++i) {
      if (vis[i] || (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1])) {
        continue;
      }
      tmp.push(nums[i]);
      vis[i] = true;
      dfs(size + 1, tmp);
      vis[i] = false;
      tmp.pop();
    }
  };
  nums.sort((a, b) => a - b);
  dfs(0, []);
  return ans;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 2022-09-07

# 旋转图像

在这里插入图片描述

在这里插入图片描述

/**
 * @param {number[][]} matrix
 * @return {void} Do not return anything, modify matrix in-place instead.
 */
var rotate = function (matrix) {
  // 对角翻转
  for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < i; j++) {
      let temp = matrix[i][j];
      matrix[i][j] = matrix[j][i];
      matrix[j][i] = temp;
    }
  }
  // 左右交换
  for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < Math.ceil(matrix.length / 2); j++) {
      let temp = matrix[i][j];
      matrix[i][j] = matrix[i][matrix.length - j - 1];
      matrix[i][matrix.length - j - 1] = temp;
    }
  }
  return matrix;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 字母异位分组

在这里插入图片描述

/**
 * @param {string[]} strs
 * @return {string[][]}
 */
var groupAnagrams = function (strs) {
  let map = new Map();
  for (let i = 0; i < strs.length; i++) {
    // acb => abc 把 abc当作map的key值
    let key = strs[i].split("").sort().join("");

    if (!map.has(key)) map.set(key, []);

    map.get(key).push(strs[i]);
  }
  return [...map.values()];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2022-09-08

# pow(x, n)

在这里插入图片描述

var myPow = function (x, n) {
  if (n == 0 || n == 1) {
    return n == 0 ? 1 : x;
  } else if (n < 0) {
    return myPow(1 / x, Math.abs(n));
  } else {
    return n % 2 == 0
      ? myPow(x * x, n / 2)
      : myPow(x * x, Math.floor(n / 2)) * x;
  }
};
1
2
3
4
5
6
7
8
9
10
11

# 最大子数组和

在这里插入图片描述

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function (nums) {
  let ans = nums[0];
  let sum = 0;
  for (const num of nums) {
    if (sum > 0) {
      sum += num;
    } else {
      sum = num;
    }
    ans = Math.max(ans, sum);
  }
  return ans;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2022-09-09

# 螺旋矩阵

在这里插入图片描述

在这里插入图片描述

/**
 * @param {number[][]} matrix
 * @return {number[]}
 */
var spiralOrder = function (matrix) {
  let res = [];
  let m = matrix.length;
  let n = matrix[0].length;

  // 上边界
  let up = 0;
  // 下边界
  let down = m - 1;
  // 左边界
  let left = 0;
  // 右边界
  let right = n - 1;

  // 遍历
  while (true) {
    // 从左到右
    for (let i = left; i <= right; i++) res.push(matrix[up][i]);
    // 上边界下移
    if (++up > down) break;

    // 从上到下
    for (let i = up; i <= down; i++) res.push(matrix[i][right]);
    // 右边界左移
    if (--right < left) break;

    // 从右到左
    for (let i = right; i >= left; i--) res.push(matrix[down][i]);
    // 下边界上移
    if (--down < up) break;

    // 从下到上
    for (let i = down; i >= up; i--) res.push(matrix[i][left]);
    // 左指针右移
    if (++left > right) break;
  }
  return res;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 螺旋矩阵 II

在这里插入图片描述

/**
 * @param {number} n
 * @return {number[][]}
 */
var generateMatrix = function (n) {
  let res = Array(n)
    .fill()
    .map(() => Array(n));

  // 上边界
  let up = 0;
  // 下边界
  let down = n - 1;
  // 左边界
  let left = 0;
  // 右边界
  let right = n - 1;

  let cur = 1;
  // 遍历
  while (true) {
    // 从左到右
    for (let i = left; i <= right; i++) res[up][i] = cur++;
    // 上边界下移
    if (++up > down) break;

    // 从上到下
    for (let i = up; i <= down; i++) res[i][right] = cur++;
    // 右边界左移
    if (--right < left) break;

    // 从右到左
    for (let i = right; i >= left; i--) res[down][i] = cur++;
    // 下边界上移
    if (--down < up) break;

    // 从下到上
    for (let i = down; i >= up; i--) res[i][left] = cur++;
    // 左指针右移
    if (++left > right) break;
  }
  return res;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# 2022-09-10

# N 皇后

在这里插入图片描述

var solveNQueens = function (n) {
  // 当前填入的结果
  let tmp = new Array(n).fill(-1);

  // 最终返回的结果 res[i] = j 代表 在第 i 行 第 j 列放入皇后(行与列从0开始计算)
  let res = [];
  // 记录当前哪些位置不能放入皇后
  let cache = [];

  /**
   *
   * @param {number} index 当前皇后在第几行
   * @param {*} tmp 当前填入的结果
   * @param {*} cache
   * @returns
   */
  let dfs = function (index, tmp, cache) {
    // 最后一个皇后(结束)
    if (index == n) {
      res.push(tmp);
      return;
    }

    for (let i = 0; i < n; i++) {
      //该位置不能放皇后
      if (cache.indexOf(index + "," + i) > -1) continue;

      let arr = [...tmp];
      // 放入皇后(在 第 index 行 第 i 列)
      arr[index] = i;

      // 判断填入的皇后是否符合条件
      let f = 0;
      for (let p = 0; p < n; p++) {
        for (let q = 0; q < n; q++) {
          if (cache.indexOf(p + "," + q) == -1) {
            // 计算当 在 第 index 行 第 i 列放入皇后后,其他哪些位置不能放入皇后
            if (
              index == p ||
              i == q ||
              p - index == q - i ||
              p + q == index + i
            ) {
              cache.push(p + "," + q);
              f++;
            }
          }
        }
      }
      dfs(index + 1, arr, cache);
      while (f--) cache.pop(); // 删除标记
    }
  };
  dfs(0, tmp, cache);
  return res.map((item) => {
    let r = [];
    item.map((a) => {
      let str = "";
      for (let i = 0; i < n; i++)
        if (a === i) str += "Q";
        else str += ".";
      r.push(str);
    });
    return r;
  });
};
console.log(solveNQueens(4));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# N 皇后 II

在这里插入图片描述

/**
 * @param {number} n
 * @return {number}
 */
var totalNQueens = function (n) {
  // 当前填入的结果
  let tmp = new Array(n).fill(-1);

  let nums = 0;
  // 记录当前哪些位置不能放入皇后
  let cache = [];

  /**
   *
   * @param {number} index 当前皇后在第几行
   * @param {*} tmp 当前填入的结果
   * @param {*} cache
   * @returns
   */
  let dfs = function (index, tmp, cache) {
    // 最后一个皇后(结束)
    if (index == n) {
      ++nums;
      return;
    }

    for (let i = 0; i < n; i++) {
      //该位置不能放皇后
      if (cache.indexOf(index + "," + i) > -1) continue;

      let arr = [...tmp];
      // 放入皇后(在 第 index 行 第 i 列)
      arr[index] = i;

      // 判断填入的皇后是否符合条件
      let f = 0;
      for (let p = 0; p < n; p++) {
        for (let q = 0; q < n; q++) {
          if (cache.indexOf(p + "," + q) == -1) {
            // 计算当 在 第 index 行 第 i 列放入皇后后,其他哪些位置不能放入皇后
            if (
              index == p ||
              i == q ||
              p - index == q - i ||
              p + q == index + i
            ) {
              cache.push(p + "," + q);
              f++;
            }
          }
        }
      }
      dfs(index + 1, arr, cache);
      while (f--) cache.pop(); // 删除标记
    }
  };
  dfs(0, tmp, cache);
  return nums;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# 2022-09-11

# 不同路径

在这里插入图片描述

/**
 * i 为行 j 为列
 * 从左上角 -> 右下角 由于每次移动只能 向右(j + 1) 或者 向下 (i + 1)
 * 因此到达 dp[i][j] 取决于
 *     (1)从右方向过来的(即从 dp[i][j - 1] 过来)
 *     (2)从下方向过来的(即从 dp[i - 1][j] 过来)
 * 故路径总和 dp[i][j] = dp[i][j - 1] + dp[i -1][j];
 */
var uniquePaths = function (m, n) {
  let dp = new Array(m).fill(new Array(n).fill(1));

  for (let i = 1; i < m; i++) {
    for (let j = 1; j < n; j++) {
      dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
    }
  }

  return dp[m - 1][n - 1];
};
console.log(uniquePaths(3, 7));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 不同路径 II

在这里插入图片描述

// 道理跟上述一致,只不过需要判断该位置是否有障碍,若有障碍 则 dp[i][j] = 0;
var uniquePathsWithObstacles = function (obstacleGrid) {
  let m = obstacleGrid.length;
  let n = obstacleGrid[0].length;
  let dp = Array(m)
    .fill()
    .map((item) => Array(n).fill(0));

  //初始化
  for (let i = 0; i < m && obstacleGrid[i][0] === 0; i++) {
    dp[i][0] = 1;
  }
  for (let j = 0; j < n && obstacleGrid[0][j] === 0; j++) {
    dp[0][j] = 1;
  }
  for (let i = 1; i < m; i++) {
    for (let j = 1; j < n; j++) {
      if (obstacleGrid[i][j] === 0) {
        dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
      } else {
        dp[i][j] = 0;
      }
    }
  }
  return dp[m - 1][n - 1];
};
console.log(uniquePathsWithObstacles([[0], [1]]));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 2022-09-12

# 简化路径

在这里插入图片描述

var simplifyPath = function (path) {
  const arr = path.split("/");
  // 栈
  const stack = [];

  const len = arr.length;

  for (let i = 0; i < len; i++) {
    if (arr[i] === "" || arr[i] === ".") {
      // 当前目录
      continue;
    } else if (arr[i] === "..") {
      // 上一层目录
      stack.pop();
    } else {
      // 下一层目录
      stack.push(arr[i]);
    }
  }
  return "/" + stack.join("/");
};

console.log(simplifyPath("/home/"));
console.log(simplifyPath("/../"));
console.log(simplifyPath("/a/./b/../../c/"));
console.log(simplifyPath("/home//foo/"));
console.log(simplifyPath("/a/../../b/../c//.//"));
console.log(simplifyPath("/a//bc/d//././/.."));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 矩阵置零

在这里插入图片描述

var setZeroes = function (matrix) {
  let row = matrix.length;
  let col = matrix[0].length;

  let rowArr = new Array(row).fill(false);
  let colArr = new Array(col).fill(false);

  // 将 matrix 中存在 0 的位置记录到 行数组(rowArr) 列数组(colArr)
  for (let i = 0; i < row; i++) {
    for (let j = 0; j < col; j++) {
      if (matrix[i][j] == 0) {
        rowArr[i] = true;
        colArr[j] = true;
      }
    }
  }

  // 更新
  for (let i = 0; i < row; i++) {
    for (let j = 0; j < col; j++) {
      if (rowArr[i] || colArr[j]) {
        matrix[i][j] = 0;
      }
    }
  }

  return matrix;
};
console.log(
  setZeroes([
    [1, 1, 1],
    [1, 0, 1],
    [1, 1, 1],
  ])
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 2022-09-13

# 合并区间

在这里插入图片描述

function getMerge(arr) {
  arr.sort((a, b) => {
    if (a[0] !== b[0]) {
      return a[0] - b[0];
    }
    return a[1] - b[1];
  });

  let len = arr.length;
  let ans = [];

  let start;
  let end;

  for (let i = 0; i < len; i++) {
    let s = arr[i][0];
    let e = arr[i][1];

    if (start == undefined) {
      start = s;
      end = e;
    } else if (s <= end) {
      // 当前的起始值在区间【start, end】 中
      end = Math.max(e, end);
    } else {
      // 创建新的区间
      let part = [start, end];
      ans.push(part);

      // 更新
      start = s;
      end = e;
    }
  }

  if (start !== undefined) {
    let part = [start, end];
    ans.push(part);
  }
  return ans;
}
let data = [
  [1, 3],
  [2, 6],
  [8, 10],
  [15, 18],
];
console.log(getMerge(data));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 旋转链表

在这里插入图片描述

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode}
 */
var rotateRight = function (head, k) {
  if (!head) return null;

  let cur = head;

  // 链表头尾相连
  let n = 1;
  while (cur.next) {
    cur = cur.next;
    n++;
  }
  cur.next = head;

  // 指针后移
  for (let i = 0; i < n - (k % n); i++) {
    cur = cur.next;
  }
  head = cur.next;

  // 断开链表
  cur.next = null;
  return head;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 2022-09-14

# 最小路径和

在这里插入图片描述

/**
 * i 为行 j 为列
 * 从左上角 -> 右下角 由于每次移动只能 向右(j + 1) 或者 向下 (i + 1)
 * 因此到达 dp[i][j] 取决于
 *     (1)从右方向过来的(即从 dp[i][j - 1] 过来)
 *     (2)从下方向过来的(即从 dp[i - 1][j] 过来)
 * 故路径总和 dp[i][j] = dp[i][j - 1] + dp[i -1][j];
 *
 * 而针对此题需要改变的地方在于
 * (1)需要从最右下角 -> 左上角 进行动态规划,由此起始位置为 qp[grid.length - 1][grid[0].length - 1]
 * (2)dp[i][j] = dp[i][j + 1] + dp[i + 1][j];
 * (3)最终返回结果 dp[0][0]
 */
var minPathSum = function (grid) {
  let m = grid.length;
  let n = grid[0].length;

  let dp = new Array(m).fill(new Array(n).fill(1));

  for (let i = m - 1; i >= 0; i--) {
    for (let j = n - 1; j >= 0; j--) {
      if (i === m - 1) {
        // 最后一行
        if (j === n - 1) {
          // 到了右下角
          dp[i][j] = grid[i][j];
          console.log(dp);
        } else {
          // 只能向右走 j + 1;
          dp[i][j] = grid[i][j] + dp[i][j + 1];
        }
      } else if (j === n - 1) {
        // 最后一列
        // 只能向下走
        dp[i][j] = grid[i][j] + dp[i + 1][j];
      } else {
        dp[i][j] = grid[i][j] + Math.min(dp[i][j + 1], dp[i + 1][j]);
      }
    }
  }
  return dp[0][0];
};
console.log(
  minPathSum([
    [1, 2, 3],
    [4, 5, 6],
  ])
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 插入区间

在这里插入图片描述

/**
 * @param {number[][]} intervals
 * @param {number[]} newInterval
 * @return {number[][]}
 */
var insert = function (intervals, newInterval) {
  let n = intervals.length;
  let i = 0;
  let ans = [];

  // 将左侧没有包含新区间的加入到结果集中
  while (i < n && intervals[i][1] < newInterval[0]) {
    // 结束位小于新区间的开始位
    ans.push(intervals[i]);
    i++;
  }

  // 将包含区间合并
  if (i < n) {
    // 找到最小起始位
    newInterval[0] = Math.min(intervals[i][0], newInterval[0]);
    while (i < n && intervals[i][0] <= newInterval[1]) {
      newInterval[1] = Math.max(intervals[i][1], newInterval[1]);
      i++;
    }
  }
  ans.push(newInterval);

  // 将右侧区间填入
  while (i < n) {
    ans.push(intervals[i]);
    i++;
  }

  return ans;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 2022-09-15

# 搜索二维矩阵

在这里插入图片描述

var searchMatrix = function (matrix, target) {
  let row = matrix.length;
  let col = matrix[0].length;

  let startRow = row - 1;
  let startCol = 0;

  while (startRow >= 0 && startCol <= col) {
    if (matrix[startRow][startCol] == target) {
      return true;
    } else if (matrix[startRow][startCol] > target) {
      // 大于目标值 向上移
      startRow--;
    } else {
      // 小于目标值 向右移
      startCol++;
    }
  }

  return false;
};
let matrix = [
  [1, 4, 7, 11, 15],
  [2, 5, 8, 12, 19],
  [3, 6, 9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30],
];
console.log(searchMatrix(matrix, 8));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 颜色分类

在这里插入图片描述

var sortColors = function (nums) {
  let swap = function (nums, i, j) {
    let temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
  };

  let n = nums.length;
  let p0 = 0;
  let p1 = 0;

  for (let i = 0; i < n; i++) {
    if (nums[i] === 1) {
      swap(nums, i, p1);
      p1++;
    } else if (nums[i] === 0) {
      swap(nums, i, p0);

      if (p0 < p1) swap(nums, i, p1);

      p0++;
      p1++;
    }
  }
  return nums;
};
let nums = [1, 2, 0, 2, 0, 1, 2, 1, 1, 0, 2, 1, 1, 1, 1, 2];
console.log(sortColors(nums));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 2022-09-16

# 组合

在这里插入图片描述

var combine = function (n, k) {
  let res = [];
  /**
   *
   * @param {number} cur 起始数值
   * @param {*} n 结束数值
   * @param {*} k 第几次
   */
  var dfs = function (cur, n, k, temp) {
    // 剪枝
    if (temp.length == k) {
      res.push([...temp]);
      return;
    }
    for (let i = cur; i <= n; i++) {
      temp.push(i);
      dfs(i + 1, n, k, temp);
      temp.pop();
    }
  };
  dfs(1, n, k, []);
  return res;
};
console.log(combine(6, 3));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 子集

在这里插入图片描述

var subsets = function (nums) {
  // 当前结果集
  let res = [[]];
  for (let num of nums) {
    let tmp = [];
    // 遍历之前存在的结果集
    for (let before of res) {
      tmp.push(before.concat(num));
    }

    res = res.concat(tmp);
    console.log("从集合中取出数值", num, "此时集合为", tmp, "进行合并后", res);
  }
  return res;
};
console.log(subsets([3, 2, 1]));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2022-09-19

# 排列序列

在这里插入图片描述

比如 n = 4,那么以 1 开头的单词有 3 _ 2 _ 1 个;n = 3,以 1 开头的单词有 2 * 1 个,依次类推,只要得出 k 是当前阶乘的多少倍,那么就获取剩下的当中的第几个单词。

/**
 * @param {number} n
 * @param {number} k
 * @return {string}
 */
var getPermutation = function (n, k) {
  let m = 1;
  k = k - 1;
  for (let i = 2; i < n; i++) m *= i;

  let a = [];
  for (let i = 1; i <= n; i++) a.push(i);

  let s = [];
  for (let i = 0; i < n; i++) {
    let t = (k / m) | 0;
    s[i] = a[t];
    a.splice(t, 1);
    k %= m;
    m /= n - i - 1;
  }
  return s.join("");
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 单词搜索

在这里插入图片描述

/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
var exist = function (board, word) {
  let row = board.length;
  let col = board[0].length;

  let dfs = (i, j, len) => {
    if (len == word.length) return true;
    // 越界
    if (i >= row || j >= col || j < 0 || i < 0) return false;

    if (board[i][j] !== word.charAt(len)) return false;

    let pre = board[i][j];
    board[i][j] = "."; // 记录改为已经被匹配过了

    // 从四个方向继续查找
    let res =
      dfs(i + 1, j, len + 1) ||
      dfs(i, j + 1, len + 1) ||
      dfs(i - 1, j, len + 1) ||
      dfs(i, j - 1, len + 1);
    board[i][j] = pre;
    return res;
  };

  for (let i = 0; i < row; i++) {
    for (let j = 0; j < col; j++) {
      if (dfs(i, j, 0)) return true;
    }
  }
  return false;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 2022-09-20

# 删除有序数组中的重复项||

在这里插入图片描述

let removeDuplicates = function (nums) {
  if (nums.length <= 2) return nums.length;

  let len = 2;
  for (let i = 2; i < nums.length; i++) {
    if (nums[i] != nums[len - 2]) {
      nums[len] = nums[i];
      len++;
    }
  }
  return len;
};
console.log(removeDuplicates([1, 1, 1, 2, 2, 3]));
1
2
3
4
5
6
7
8
9
10
11
12
13

# 子集 II

在这里插入图片描述

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var subsetsWithDup = function (nums) {
  const result = [];
  nums.sort((a, b) => {
    return a - b;
  });

  let backtrack = (start, curr) => {
    result.push([...curr]);
    for (let i = start; i < nums.length; i++) {
      if (i > start && nums[i] == nums[i - 1]) {
        continue;
      }

      curr.push(nums[i]);
      backtrack(i + 1, curr);
      curr.pop();
    }
  };
  backtrack(0, []);
  return result;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 2022-09-21

# 删除排序链表中的重复元素 II

在这里插入图片描述

var deleteDuplicates = function (head) {
  if (!head) return head;

  // 记录链表头结点地址
  const dummy = new ListNode(0, head);

  let cur = dummy;
  // 第一个结点和第二个结点
  while (cur.next && cur.next.next) {
    // 第一个结点和第二结点值相等
    if (cur.next.val == cur.next.next.val) {
      const x = cur.next.val;

      // 指针指向下个与 x 值不不相等的值地址
      while (cur.next && cur.next.val === x) {
        cur.next = cur.next.next;
      }
    } else {
      // 指针继续向前
      cur = cur.next;
    }
  }
  // dummy.next 指向的是 head 的地址
  return dummy.next;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 单词子集

在这里插入图片描述

/**
 * @param {string[]} words1
 * @param {string[]} words2
 * @return {string[]}
 */
var wordSubsets = function (words1, words2) {
  const B = new Array(26).fill(0);
  words2.forEach((w) => {
    const tmp = new Array(26).fill(0);
    for (const c of w) {
      const idx = c.charCodeAt() - 97;
      if (tmp[idx]++ === B[idx]) B[idx]++;
    }
  });
  return words1.filter((w) => {
    const tmp = B.slice();
    for (const c of w) {
      const idx = c.charCodeAt() - 97;
      if (tmp[idx] > 0) tmp[idx]--;
    }
    return tmp.every((cnt) => cnt === 0);
  });
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 2022-09-22

# 柱状图中最大的矩阵

在这里插入图片描述

var largestRectangleArea = function (heights) {
  let large = 0;
  let shift = [{ index: -1, height: -1 }];
  for (let i = 0; i < heights.length; i++) {
    // 队列中最后一个元素为非初始化元素
    while (
      shift[shift.length - 1].height !== -1 &&
      shift[shift.length - 1].height > heights[i]
    ) {
      const pop = shift.pop();
      large = Math.max(
        large,
        pop.height * (i - 1 - shift[shift.length - 1].index)
      );
    }
    shift.push({ index: i, height: heights[i] });
  }
  const top = shift[shift.length - 1].index;
  // 队列中最后一个元素为非初始化元素
  while (shift[shift.length - 1].height !== -1) {
    const pop = shift.pop();
    large = Math.max(large, pop.height * (top - shift[shift.length - 1].index));
  }
  return large;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 单词拆分

在这里插入图片描述

var wordBreak = function (s, wordDict) {
  const slen = s.length;
  const set = new Set(wordDict);
  let arr = new Array(slen + 1).fill(false);

  arr[0] = true;

  for (let i = 1; i <= slen; i++) {
    for (let j = 0; j < i; j++) {
      if (arr[j] && set.has(s.substr(j, i - j))) {
        arr[i] = true;
        break;
      }
    }
  }
  return arr[slen];
};
let s = `applepenapple`;
let wordDict = ["apple", "pen"];
console.log(wordBreak(s, wordDict));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20