JavaScript LeetCode Diary 1

230 阅读3分钟

2020-04-06 (Array)

从今天开始,打算刷一下 LeetCode,复习一下算法和数据结构的基础知识。最近写业务较多,感觉需要换换脑子。再者毕竟今年行情不好,有备无患。

节奏是打算每天刷3道 medium 难度,基本按照提交的通过率从高往低的来,或者是非常经典的题目。这样一开始刷的应该是相对来说比较容易的,可以循序渐进。计划是每个标签刷10道,这样下来一个月可以刷8个标签,可以把常见的数据结构都过一遍。

这里记录的是刷的过程中的一些心得,和大概的思路。具体的代码我也每日更新到我的 GitHub 仓库。

15. 3Sum

先 sort array, logn;然后从左边 i 开始,可以拿到 diff = 0 - num[i], 数组剩下的,就是2sum,任意两个数加和为diff。使用双指针 j, k 一头一尾开始遍历。有一个corner case 是 [0,0,0,0], 去重是关键,方法是判断,nums[i] 不等于前一个数 nums[i-1]。

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function (nums) {
  const res = [];
  const len = nums.length;
  const target = 0;
  nums = nums.sort((a, b) => a - b);

  for (let i = 0; i < len - 2; i++) {
    if (i === 0 || (i > 0 && nums[i] !== nums[i - 1])) {
      let j = i + 1;
      let k = len - 1;

      while (j < k) {
        const sum = nums[i] + nums[j] + nums[k];
        if (sum === target) {
          res.push([nums[i], nums[j], nums[k]]);
          // 判断毗邻两数字是否想等,相等的话继续滑动, 避免添加重复的答案
          while (j < k && nums[j] === nums[j + 1]) {
            j += 1;
          }
          while (j < k && nums[k] === nums[k - 1]) {
            k -= 1;
          }
          j += 1;
          k -= 1;
        }
        if (sum < target) {
          while (j < k && nums[j] === nums[j + 1]) {
            j += 1;
          }
          j += 1;
        } else if (sum > target) {
          while (j < k && nums[k] === nums[k - 1]) {
            k -= 1;
          }
          k -= 1;
        }
      }
    }
  }
  return res;
};

threeSum([1, -1, -1, 0]);
threeSum([0, 0, 0, 0]);
// threeSum([-1, 0, 1, 2, -1, -4])

1329. Sort the Matrix Diagonally

一开始题目理解错了,想的方法是先vertical对每个子数组排序,再横排。仔细看了例子,原来是只排对角线的。解题思路是把对角线存在新数组里,排序之后塞回去。关键点是对角线上,i-j 值恒定。i、j是遍历2维数组时的index。在通过 forEach 遍历 map,最后放回mat。 space O(mn) time O(mnlogd) d is the length of diagnoal with D = min(m,n)

/**
 * @param {number[][]} mat
 * @return {number[][]}
 */
var diagonalSort = function (mat) {
  const map = new Map();
  const m = mat.length;
  const n = mat[0].length;
  // 准备 map
  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      const curr = mat[i][j];
      // 在对角线上,i-j 的差值恒定,
      if (map.has(i - j)) {
        const arr = map.get(i - j);
        arr.push(curr);
        map.set(i - j, arr);
      } else {
        map.set(i - j, [curr]);
      }
    }
  }
  // sort map
  map.forEach((arr) => arr.sort((a, b) => a - b));

  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      mat[i][j] = map.get(i - j).shift();
    }
  }
  return mat;
};

1395. Count Number of Teams

刷完3sum之后碰到这题,感觉是类似的。3sum是要求三者之和等于0(变种可以是任何值),这里的判断条件是3个值满足升序或降序。后来发现不行。解题思路是dp,生成两个数组,记录每个index处,后面还有多少个比它大,比他小的数量。再次遍历时,加和。

/**
 * @param {number[]} rating
 * @return {number}
 */
var numTeams = function (rating) {
  if (rating.length < 3) return 0;

  let res = 0;
  const len = rating.length;
  const greaterThan = new Array(len).fill(0);
  const lessThan = new Array(len).fill(0);

  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      if (rating[i] < rating[j]) {
        greaterThan[i] += 1;
      } else {
        lessThan[i] += 1;
      }
    }
  }

  for (let i = 0; i < len - 2; i++) {
    for (let j = i + 1; j < len; j++) {
      // 判断是升序,则看 j 后面还有多少个比 j 更大的
      if (rating[i] < rating[j]) {
        res += greaterThan[j];
      } else if (rating[i] > rating[j]) {
        // 判断是降序,则看后面有多少比 j 更小的
        res += lessThan[j];
      }
    }
  }
  return res;
};

// numTeams([2,5,3,4,1])
numTeams([4, 7, 9, 5, 10, 8, 2, 1, 6]);