LeetCode经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结

Posted YK菌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结相关的知识,希望对你有一定的参考价值。

序号为LeetCode的题序,语言选择的是javascript

数字类

7. 整数反转

https://leetcode-cn.com/problems/reverse-integer/

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

【技巧】~~取整(舍去小数位)

~按位取反

对于整数相当于取反减一

~0 === -1
~1 === -2
~-1 === 0
~-2 === 1

对于小数相当于舍去小数位再取反减一

~0.3 === -1
~1.7 === -2
~-0.3 === -1
~-1.2 === 0
~-2.9 === 1

~~按位取反再取反

对于整数还是自身

~~1 === 1
~~-1 === -1
~~0 === 0

对于小数,等于舍去小数位
相当于正数向下取整,负数向上取整

~~1.1 === 1
~~1.9 === 1
~~-1.1 === -1
~~-1.9 === -1

【技巧】Math.floor() 向下取整

Math.floor()向下取整

Math.floor(1.1) === 1
Math.floor(1.9) === 1
Math.floor(-1.1) === -2
Math.floor(-1.9) === -2

【解法】商与余数

整数除以10 余数为其个位数 为去除个位数的剩余数字

整体思路就是遍历(x / 10商)数字x,每次拿数字的个位(x % 10 余数),直到拿完。
每次遍历都将x的个位 拼接到result的新腾出的个位上(result = result * 10 + (x % 10);

/**
 * @param {number} x
 * @return {number}
 */
var reverse = function (x) {
  let result = 0;
  while (x) {
    result = result * 10 + (x % 10);
    if (result > 2 ** 31 - 1 || result < -(2 ** 31)) return 0;
    x = ~~(x / 10);
  }
  return result;
};

13. 罗马数字转整数

https://leetcode-cn.com/problems/roman-to-integer/

【解法一】Map

  1. 创建map映射
  2. 遍历字符串,在map中根据key取value (map.get(s[i])

正常情况下 小的数在大的数的右边 直接累加

要先排除特殊情况 小的数在大的数的左边,那就给它前面加一个负号

/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function (s) {
  let map = new Map();
  map
    .set("I", 1)
    .set("V", 5)
    .set("X", 10)
    .set("L", 50)
    .set("C", 100)
    .set("D", 500)
    .set("M", 1000);

  let result = 0;
  for (let i = 0; i < s.length; i++) {
    let value = map.get(s[i]);
    if (i < s.length - 1 && value < map.get(s[i + 1])) {
      result -= value;
    } else {
      result += value;
    }
  }
  return result;
};

【解法二】switch

【技巧】巧用switch语句

有限种确定情况,完全可以用switch语句

/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function (s) {
  let result = 0;
  for (let i = 0; i < s.length; i++) {
    let value = getValue(s[i]);
    if (i < s.length - 1 && value < getValue(s[i + 1])) {
      result -= value;
    } else {
      result += value;
    }
  }
  return result;
};

function getValue(s) {
  switch (s) {
    case "I":
      return 1;
    case "V":
      return 5;
    case "X":
      return 10;
    case "L":
      return 50;
    case "C":
      return 100;
    case "D":
      return 500;
    case "M":
      return 1000;
    default:
      return 0;
  }
}

数组类

1. 两数之和

https://leetcode-cn.com/problems/two-sum/

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

【解法一】两层循环 - 暴力查找

i遍历一遍数组;
ji+1开始遍历剩余部分,看是否能找到等于 target-nums[i] 的元素, 找到返回下标

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
  for (let i = 0; i < nums.length; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[j] === target - nums[i]) {
        return [i, j];
      }
    }
  }
  return [];
};

【解法二】一层循环 - Map

复习一下ES6的Map

【技巧】map

map会维护插入时的顺序

// 定义空map
let map = new Map();
// 添加元素
map.set("key1","value1").set("key2", "value2")
// 查询元素
map.has("key1") // true
map.get("key1") // "value1"
map.size === 2

构建映射【值:下标】
遍历nums构建map,之后只需在map中查找元素,而不需要每次都遍历剩余数组了

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let map = new Map();
    for(let i = 0; i < nums.length; i++){
        if(map.has(target-nums[i])){
            return [map.get(target-nums[i]), i]
        }
        map.set(nums[i], i)
    }
    return []
};

【坑】注意题目条件 不可以取两次自己的下标

注意这里有一个坑,往map中存数据(set)操作要在判断语句之后!
因为不可以重复取两次自己,所以要在之前存入的元素中查找!!!

11. 盛最多水的容器

对撞指针

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
    let i = 0;
    let j = height.length - 1;
    let maxA = 0;
    while(i<j){
        let nowA = Math.min(height[i],height[j]) * (j - i);
        maxA = Math.max(maxA, nowA)
        if(height[i] <= height[j]){
            i++;
        }else{
            j--;
        }
    }
    return maxA;
};

26. 删除有序数组中的重复项

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

【解法】快慢指针

快指针表示遍历数组到达的下标位置【遍历数组】
慢指针表示下一个不同元素要填入的下标位置【维持条件】

快指针 i 用来遍历一遍数组
慢指针 j 用来维护数组
注意这里的判断条件,满足条件【元素不重复】,慢指针j才走

在这里插入图片描述

/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    let j = 0
    for(let i = 0; i < nums.length - 1; i++){
        if(nums[i] !== nums[i+1]){
            j++;
            nums[j] = nums[i+1];
        }
    }
    return j + 1;
};

27. 移除元素

https://leetcode-cn.com/problems/remove-element/

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

【解法】快慢指针

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    let j = 0;
    for(let i = 0; i< nums.length; i++){
        if(nums[i] !== val){
            nums[j] = nums[i];
            j++;
        }
    }
    return j;
};

53. 最大子序和

https://leetcode-cn.com/problems/maximum-subarray/

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

【解法】动态规划

thisSum 维护一个 向右累加 子序和 如果之前和子序和都没有第i个元素大,就从i开始重新维护一个 累加子序和

maxSum 保存遍历过程中的最大子序和

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    let thisSum = 0
    let maxSum = nums[0]
    for(let i = 0; i<nums.length; i++){
        if(thisSum + nums[i] < nums[i]){
            thisSum = nums[i]
        }else{
            thisSum += nums[i]
        }
        if(thisSum > maxSum){
            maxSum = thisSum
        }
    }
    return maxSum
};

精简一下代码

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    let thisSum = 0
    let maxSum = nums[0]
    for(let i = 0; i< nums.length; i++){
        thisSum = Math.max(thisSum + nums[i], nums[i]);
        maxSum = Math.max(maxSum, thisSum);
    }
    return maxSum;
};

75. 颜色分类

https://leetcode-cn.com/problems/sort-colors

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

【解法一】两次遍历

一种很朴素的解法遍历两边,正着找0放在最前面,倒着找2放在最后面【双指针】

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var sortColors = function(nums) {
    let first = 0;
    let last = nums.length - 1;
    
    for(let i = 0; i < nums.length; i++){
        if(nums[i] === 0){
            swap(nums, i, first);
            first++;
        }
    }
    for(let i = nums.length - 1; i >= 0; i--){
        if(nums[i] === 2){
            swap(nums, i, last);
            last--;
        }
    }
};

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

也可以按顺序找,0放前面,1放中间,一样一样的【单指针】

var sortColors = function(nums) {
    let first = 0
    for(let i = 0; i < nums.length; i++){
        if(nums[i] === 0){
            swap(nums, i, first);
            first++;
        }
    }
    for(let i = first; i < nums.length; i++){
        if(nums[i] === 1){
            swap(nums, i, first);
            first++;
        }
    }
};

【解法二】两层循环 冒泡排序

很多原地排序算法都可以【冒泡】【选择】【插入】等

更多关于排序算法可以参考这篇博文【算法】经典排序算法总结-JavaScript描述-图解-复杂度分析

var sortColors = function(nums) {
  for (let i = 0; i < nums.length; i++) {
    for (let j = 0; j < nums.length - i -1; j++) {
      if (nums[j] > nums[j + 1]) {
        swap(nums, j, j + 1);
      }
    }
  }
};

【解法三】遍历一次 + 循环不变量【重点】

头尾指针ij,遇到0nums[i]交换,遇到2nums[j]交换,遇到1就跳过啥也不做

定义循环不变量

[0, i) === 0
(i, k) === 1
(j, nums.length-1] === 2

终止条件是k===j

var sortColors = function(nums) {
    let i = 0;
    let j = nums.length - 1;
    for(let k = 0; k <= j; k++){
        if(nums[k] === 0){
            swap(nums, k, i);
            i++;
        }else if(nums[k] === 2){
            swap(nums, k, j);
            j--;
            k--;
        }
    }
};

80. 删除有序数组中的重复项 II

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

【解法】快慢指针

/**
 * @param {number[]} nums
 * @return {number}
 */
var LeetCode经典题分类(链表 - 树 - 图)精选 - JavaScript - ES6 - 技巧总结

一文通数据结构与算法之——数组+常见题型与解题策略+Leetcode经典题

一文通数据结构与算法之——贪心算法+常见题型与解题策略+Leetcode经典题

LeetCode刷题日记精选例题-双指针经典问题总结

Leetcode分类刷题答案&心得

LeetCode 经典】MedianSortedArrays