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

【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第1张图片

文章目录

  • 数字类
    • 7. 整数反转(简单)
      • 【解法一】反转字符串
      • 【解法二】商与余数
        • 【技巧】`~~`取整(舍去小数位)
        • 【技巧】`Math.floor()` 向下取整
    • 13. 罗马数字转整数(简单)
      • 【解法一】Map
      • 【解法二】switch
        • 【技巧】巧用`switch`语句
    • 50. Pow(x, n) (中等)
      • 【解法一】快速幂前处理
      • 【解法二】快速幂后处理
  • 数组类
    • 1. 两数之和(简单)
      • 【解法一】两层循环 - 暴力查找
      • 【解法二】一层循环 - Map
        • 【技巧】map
        • 【坑】注意题目条件 不可以取两次自己的下标
    • 11. 盛最多水的容器(中等)
      • 【解法】双指针
    • 26. 删除有序数组中的重复项【简单】
      • 【解法一】快慢指针
      • 【解法二】双指针
    • 27. 移除元素【简单】
      • 【解法】快慢指针
      • 【解法二】对撞指针
    • 53. 最大子序和【简单】
      • 【解法】动态规划
    • 54. 螺旋矩阵
    • 75. 颜色分类【中等】
      • 【解法一】两次遍历
      • 【解法二】两层循环 冒泡排序
      • 【解法三】遍历一次 + 循环不变量【重点】
    • 80. 删除有序数组中的重复项 II【中等】
      • 【解法】快慢指针
    • 88. 合并两个有序数组【简单】
    • 118. 杨辉三角
      • 【解法】双层循环
    • 121. 买卖股票的最佳时机
      • 【解法一】暴力解法 不通过
      • 【解法二】类似动态规划
    • 167. 两数之和 II - 输入有序数组【简单】
      • 【解法一】两层循环-暴力查找
      • 【解法二】头尾指针 - 对撞指针
    • 215. 数组中的第K个最大元素【中等】
      • 【解法】划分 + 二分查找
    • 283. 移动零【简单】
      • 【解法一】快慢指针 + 两次遍历
      • 【解法二】快慢指针 + 一次遍历 + 交换元素
    • 704. 二分查找
      • 【解法】头尾指针
    • 278. 第一个错误的版本
      • 【解法】二分查找
    • 剑指 Offer 09. 用两个栈实现队列
    • 989. 数组形式的整数加法
    • 1480. 一维数组的动态和
    • 1588. 所有奇数长度子数组的和
  • 字符串类
    • 3. 无重复字符的最长子串
    • 【解法一】暴力遍历 + Set
    • 【解法二】滑动窗口
    • 【解法三】双指针 - 滑动窗口(这种写法通俗易懂)
    • 14. 最长公共前缀
      • 【解法一】横向扫描
      • 【解法二】纵向扫描
      • 【解法三】分治法
    • 20. 有效的括号
      • 【解法一】暴力 栈
      • 【解法二】用map
      • 【解法三】用swtich语句
    • 165. 比较版本号
    • 345. 反转字符串中的元音字母
    • 415. 字符串相加
    • 剑指 Offer 50. 第一个只出现一次的字符
    • 1221. 分割平衡字符串

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

数字类

7. 整数反转(简单)

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

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

【解法一】反转字符串

这题一拿到手,最先也是最容易想到的就是将数字传转换成字符串然后进行反转
数字 ——> 字符串 ——> 数组 ——> 反转 ——> 字符串 ——> 数字
在这里插入图片描述
注意分清楚正负号,还有题目的条件,即可得到答案

var reverse = function(x) {
   
  let res = 0;
  if (x >= 0) {
   
    res = +String(x).split("").reverse().join("");
  } else {
   
    x = -x;
    res = -String(x).split("").reverse().join("");
  }
  if(res > 2**31 - 1 || res < -(2**31)) return 0;
  return res;
};

【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第2张图片

再想着用数学的方式来试试,不需要开辟新的空间来进行反转

【解法二】商与余数

整数除以10 余数为其个位数 为去除个位数的剩余数字 (需要取整)
【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第3张图片

整体思路就是遍历(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;
};

【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第4张图片

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

~按位取反

对于整数相当于取反减一

~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

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;
};

【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第5张图片

【解法二】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;
  }
}

50. Pow(x, n) (中等)

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即, x n x^n xn)。

【解法一】快速幂前处理

/**
 * @param {number} x
 * @param {number} n
 * @return {number}
 */
var myPow = function(x, n) {
   
    let result = 1

    if(n < 0){
   
        x = parseFloat(1/x)
        n = -n
    }

    while (n > 0) {
   
        if ((n & 1) === 1) result *= x;
        x *= x
        n >>>= 1;
    }
    
    return result
};

【解法二】快速幂后处理

也可以这样最后处理

var myPow = function(x, n) {
   
    let result = 1

    let flag = false
    if(n < 0){
   
        flag = true
        n = -n
    }

    while (n > 0) {
   
        if ((n & 1) === 1) result *= x
        x *= x
        n >>>= 1
    }
    if(flag){
   
        result = parseFloat(1/result)
    }
    return result
};

【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第6张图片
在这里插入图片描述

数组类

1. 两数之和(简单)

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

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

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

拿到题最容易想到的就是两层循环遍历,固定一个元素,查找另一个元素
【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第7张图片

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 - 1; i++) {
   
    for (let j = i + 1; j < nums.length; j++) {
   
      if (nums[j] === target - nums[i]) {
   
        return [i, j];
      }
    }
  }
  return [];
};

【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第8张图片

【解法二】一层循环 - 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++){
   
	    let value = target - nums[i]
	    if(map.has(value)){
   
	        return [map.get(value), i]
	    }else {
   
	    	// 找不到就插入到 map中
	        map.set(nums[i], i)
	    }
    }
    return [];
};

【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第9张图片

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

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

11. 盛最多水的容器(中等)

https://leetcode-cn.com/problems/container-with-most-water/

给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。
在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第10张图片

【解法】双指针

初始时,两个指针分别指向数组的两端,得到当前可以容纳的水的容量
每次移动一个指针,就是指向的值小的那个指针
这道题操作起来其实很简单,然而其中的重点是这种方法的【正确性】,我们在移动指针的时候抛弃了很多的解,但是这些解是可以抛弃的解,不会影响结果的解

将本题的搜索空间用矩阵表示出来就是这样的【白色区域】
【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第11张图片
如果遍历所有的解【所有小方块】,就需要O(N^2)的复杂度
通过本题的双指针来缩减搜索空间

双指针最先得到的解是右上方的解
【LeetCode】经典题分类(数学 - 数组 - 字符串)精选 - JavaScript - ES6 - 技巧总结_第12张图片
假设左边的 0 号柱子较短。0 号柱子目前的水面高度已经到了上限。
由于 7 号柱子已经是离 0 号柱子最远的了,水的宽度也最大,如果换其他的柱子和 0 号柱子配对,水的宽度只会更小,高度也不会增加,容纳水的面积只会更小。
也就是说,0 号柱子和6,5,4,3,2,1号柱子的配对都可以排除掉了。
记录了 (0,7) 这组柱子的结果之后,就可以排除 0 号柱子了。
这相当于 i=0 的情况全部被排除。
对应于双指针解法的代码࿰

你可能感兴趣的:(算法,leetcode,javascript,算法)