leetcode刷题日记

panda2022-11-13 20:4:1编程基础算法 leetcode

2022/11/13 #20 有效的括号

这道题目的核心思想理解栈的数据结构

var isValid = function (s) {
  const dict = new Map([
    ["(", ")"],
    ["{", "}"],
    ["[", "]"],
  ])
  const stack = []
  const pres = [...dict.keys()]
  const ends = [...dict.values()]
  for (let i = 0; i < s.length; i++) {
    const char = s[i]
    if (pres.includes(char)) {
      stack.push(char)
    } else if (ends.includes(char)) {
      if (dict.get(stack.pop()) !== char) return false
    }
  }
  return !stack.length
};

2022/11/14 #1 两数之和

两个数字相亲记~

var twoSum = function (nums, target) {
  const map = new Map()
  for (let i = 0; i < nums.length; i++) {
    const n = nums[i]
    const theHalf = target - n
    if (!map.has(theHalf)) {
      map.set(n, i)
    } else {
      return [map.get(theHalf), i]
    }
  }
};

2022/11/15 #9 回文数

其实就是两个指针靠近,直到两人亲上都还能没发生不同,那就是一个回文数

var isPalindrome = function (x) {
  const n = "" + x;
  if (n.length === 1) return true;
  let p1 = 0,
    p2 = n.length - 1;
  while (p1++ < p2--) {
    if (n[p1-1] !== n[p2+1]) return false;
  }
  return true;
};

2022/11/16 #13 罗马数字

这道题我最开始写出来是这样的,能明显感觉到不是很优雅

var romanToInt = function (s) {
  const dict = new Map([
    ['I', 1],
    ['V', 5],
    ['X', 10],
    ['L', 50],
    ['C', 100],
    ['D', 500],
    ['M', 1000],
  ]);
  let flag = false
  const nums = s.split('')
  return nums.reduce((p, n, i) => {
    if (dict.get(nums[i + 1]) > dict.get(n)) {
      flag = true
      return p + 0
    }
    if (flag) {
      flag = false
      return p + dict.get(n) - dict.get(nums[i - 1])
    }
    return p + dict.get(n)
  }, 0)
};

通过观察我们可以看出来,当右边不小于左边的时候是正常的的转换逻辑,当右边比左边大的就需要用右边减去左边,那我们可以先减再加,我们可以这样改

var romanToInt = function (s) {
  const dict = new Map([
    ['I', 1],
    ['V', 5],
    ['X', 10],
    ['L', 50],
    ['C', 100],
    ['D', 500],
    ['M', 1000],
  ]);
  return s.split('').reduce((p, n, i, ns) => {
    if (dict.get(ns[i + 1]) > dict.get(n)) {
      return p - dict.get(n)
    } else {
      return p + dict.get(n)
    }
  }, 0)
};

2022/11/17 #14 最长公共前缀

var longestCommonPrefix = function (strs) {
  let p = 0
  while (p > -1) {
    const uniPicks = [...new Set(strs.map((str) => str[p]))]
    if (uniPicks.includes(undefined) || uniPicks.length != 1) {
      return strs[0].substring(0, p)
    }
    p++
  }
};

2022/11/18 #26 删除有序数组中的重复项

var removeDuplicates = function (nums) {
  const uniNums = [...new Set(nums)]
  nums.length = uniNums.length
  uniNums.forEach((n, i) => {
    nums[i] = n
  })
  return nums.length
};

2022/11/19 #27 移除元素

var removeElement = function (nums, val) {
  let i = 0;
  while (i < nums.length) {
    if (nums[i] === val) {
      nums.splice(i, 1)
    } else {
      i++
    }
  }
  return nums.length
};

2022/11/21 #35 搜索插入位置 | #58 最后一个单词的长度

// #35 搜索插入位置
var searchInsert = function (nums, target) {
  for (let i = 0; i < nums.length; i++) {
    // case1 相等
    if (nums[i] === target) return i;
    // case2 在某个节点大于它之前
    if (nums[i] > target) return i;
  }
  // case3 插入末尾
  return nums.length;
};

// #58 最后一个单词的长度
var lengthOfLastWord = function (s) {
  return s.trim().split(" ").at(-1).length;
};

2022/11/22 #66 加一

var plusOne = function (digits) {
  let carry = false;
  digits[digits.length - 1] = digits.at(-1) + 1;
  for (let i = digits.length - 1; i >= 0; i--) {
    let n = digits[i]
    if (carry) {
      n++;
      carry = false;
    }
    if (n >= 10) {
      carry = true;
      digits[i] = n % 10;
    } else {
      digits[i] = n;
    }
  }
  if (carry) {
    digits.unshift(1)
  }
  return digits;
};

想了想,其实所有工作都可以在for循环里完成

var plusOne = function (digits) {
  digits[digits.length - 1] = digits.at(-1) + 1;
  for (let i = digits.length - 1; i >= 0; i--) {
    if (digits[i] >= 10) {
      digits[i] = digits[i] % 10;
      // 防止下标越界
      if (i === 0) {
        digits.unshift(1);
      } else {
        digits[i - 1] = digits[i - 1] + 1;
      }
    }
  }
  return digits;
};

2022/11/23 #67 二进制求和

这道题和昨天的题神似,不过换成二进制的n+n,昨天是十进制的n+1

var addBinary = function (a, b) {
  const r = [];
  const sameLen = a.length === b.length;
  // 长组
  const l = sameLen ? a : a.length > b.length ? a : b
  const lt = l.split("");
  const li = lt.length - 1;
  // 短组
  const s = sameLen ? b : a.length < b.length ? a : b
  const st = s.split("");
  let si = st.length - 1;
  // 长短差
  const di = li - si;
  // 公共进制位
  while (si >= 0) {
    const n = +lt[di + si] + +st[si]
    // 需要进位
    if (n >= 2) {
      r.unshift(n % 2);
      // 相同长度下防止下标越界
      if (si === 0 && sameLen) {
        r.unshift(1)
      } else {
        lt[si + di - 1] = +lt[si + di - 1] + 1;
      }
    }
    // 无需进位
    else {
      r.unshift(n);
    }
    si--
  }
  // 长组余下进制位,其实和上边的逻辑相似,只是不用考虑加法
  if (di) {
    let i = di - 1;
    while (i >= 0) {
      const n = lt[i];
      if (n >= 2) {
        r.unshift(n % 2)
        if (i === 0) {
          r.unshift(1)
        } else {
          lt[i - 1] = +lt[i - 1] + 1;
        }
      } else {
        r.unshift(n)
      }
      i--
    }
  }
  return r.join('');
};

看到评论区有同学说,长度不一样的情况下可以直接补齐之后再计算,一想的确有道理啊,但是效率应该不会有多大提升,该循环的次数一次没少,不过简洁一些

var addBinary = function (a, b) {
  const r = [];
  const sameLen = a.length === b.length;
  // 长组
  const l = sameLen ? a : a.length > b.length ? a : b
  const lt = l.split("");
  let li = lt.length - 1;
  // 短组
  const s = sameLen ? b : a.length < b.length ? a : b
  const st = s.split("");
  const si = st.length - 1;
  // 长短差
  const di = li - si;
  // 补齐短组
  const ft = sameLen ? st : [...new Array(di).fill(0), ...st];
  // 公共进制位
  while (li >= 0) {
    const n = +lt[li] + +ft[li]
    // 需要进位
    if (n >= 2) {
      r.unshift(n % 2);
      // 相同长度下防止下标越界
      if (li === 0) {
        r.unshift(1)
      } else {
        lt[li - 1] = +lt[li - 1] + 1;
      }
    }
    // 无需进位
    else {
      r.unshift(n);
    }
    li--
  }
  return r.join('');
};

确实少了很多代码哈.

2022/11/27 #69 X的平方根 | #70 爬楼梯 | # 83 删除排序链表中的重复元素

// #69
var mySqrt = function (x) {
  if (x == 0 || x == 1) return x;
  let left = 1, right = x;
  // 只要左边没超过右边就需要一直去查找
  while (left < right) {
    // 如果只相隔1,直接返回左边
    if (right - left == 1) return left;
    // 向下取整中位数
    let mid = left + Math.floor((right - left) / 2);
    // 除以中位数
    let res = x / mid;
    // 如果刚好结果相等,中位数就是想要的数字
    if (res === mid) return mid;
    // 如果结果小于中位数,说明应该向左缩小范围
    if (res < mid) {
      right = mid;
    }
    // 如果结果大于中位数,说明应该向右缩小范围
    else {
      left = mid;
    }
  }
};

// #70 这是一道经典的动态规划的算法题目,你需要做的最关键的一点是总结出前后节点的规律,或者说是一个公式
var climbStairs = function (n) {
  if (n < 2) return 1;
  // 用来保存每个台阶的方法数目
  const dp = [1, 1];
  for (let i = 2; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2];
  }
  return dp[n];
};

// #83
var deleteDuplicates = function (head) {
  let p = head;
  while (p && p.next) {
    // 下个节点
    const next = p.next;
    // 如果重复,将当前节点的next指向下个节点的next
    if (next.val === p.val) {
      p.next = next.next;
    } else {
      // 将当前指针往后移动
      p = p.next;
    }
  }
  return head;
};

2023/1/19 #88 合并两个有序数组

这个题依然算是简单的指针题,两个指针在自己的合适的情况前进,直到走到目标位置即可

不知道为啥今天leetcode抽疯测试和提交出问题了,但是在本地测试是没问题的,没有测试所有测试用例,可能会有边角料问题,小case~

var merge = function (nums1, m, nums2, n) {
  const res = [];
  let i = 0, j = 0;
  while (i < m || j < n) {
    const n1 = nums1[i]
    const n2 = nums2[j]
    // 是否越界
    if (i >= m) {
      res.push(n2)
      j++
    }
    // 是否越界
    else if (j >= n) {
      res.push(n1)
      i++
    }
    // 判断大小
    else if (n1 < n2) {
      res.push(n1)
      i++
    } else if (n1 > n2) {
      res.push(n2)
      j++
    }
    // 相等 
    else {
      res.push(n1, n2)
      i++
      j++
    }
  }
  return res
};

原来leetcode那天并没有抽风,其实是我没注意到题目,是合并到num1数组,不是返回一个新数组 =.=

var merge = function (nums1, m, nums2, n) {
  // 从后往前遍历,避免覆盖
  let i = m - 1, j = n - 1, k = m + n - 1;
  while (i >= 0 && j >= 0) {
    if (nums1[i] > nums2[j]) {
      nums1[k--] = nums1[i--];
    } else {
      nums1[k--] = nums2[j--];
    }
  }
  // nums2还有剩余
  while (j >= 0) {
    nums1[k--] = nums2[j--];
  }
  return nums1;
};

2023/1/19 #112 合并两个有序数组

2023/4/11 #104 二叉树的最大深度

var maxDepth = function (root) {
  if (root === null) return 0;
  return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
};

20230/6/7

189 轮转数组

var rotate = function (nums, k) {
  for (let i = 0; i < k; i++) {
    nums.unshift(nums.pop());
  }
};

724 寻找数组的中心下标

这道题目难度是中等?我感觉就很简单啊~

var pivotIndex = function (nums) {
  let index = -1;
  for (let i = 0; i < nums.length; i++) {
    const leftSum = nums.slice(0, i).reduce((a, b) => a + b, 0);
    const rightSum = nums.slice(i + 1).reduce((a, b) => a + b, 0);
    if (leftSum === rightSum) {
      index = i;
      break;
    }
  }
  return index
};
Last Updated 2023-06-08 16:25:57