Skip to content

力扣100题

1 两数之和-简单

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

方法1-暴力解法

利用两次循环,第一层遍历原数组,第二层从 i + 1 开始,依次遍历原数组,直到查找到结果

js
var twoSum = function (nums, target) {
  const result = [];
  for (let i = 0, len = nums.length; i < len; i++) {
    // 因为题目要求不能使用两次相同的元素,所以从 i + 1 可以理论上减少后续查找的次数
    for (let j = i + 1; j < len; j++) {
      if (nums[i] + nums[j] === target) {
        result.push(i, j);
        break;
      }
    }
  }
  return result;
};

方法2-优化算法

原理:利用map来存储遍历过的数字,然后利用目标值减去遍历的当前数字,得到的差去查找,找到返回

js
var twoSum = function (nums, target) {
  // 利用目标值减去当前数字的值 然后去map里面查找是否存在来判断
  const map = new Map();
  for (let i = 0; i < nums.length; i++) {
    const rest = target - nums[i];
    if (map.has(rest)) {
      return [map.get(rest), i];
    }
    map.set(nums[i], i);
  }
  return [-1, -1];
};

2 两数相加-中等

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

img
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

方法1-遍历

js
/**
 * Definition for singly-linked list.
 * }
 */
function ListNode(val, next) {
  this.val = val === undefined ? 0 : val;
  this.next = next === undefined ? null : next;
}

/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var addTwoNumbers = function (l1, l2) {
    let n1 = 0
    let n2 = 0
    let carry = 0 // 进位信息
    let head = null
    let tail = null
    while (l1 || l2) {
        n1 = l1 != null ? l1.val : 0
        n2 = l2 != null ? l2.val : 0
        // 求和
        let sum = n1 + n2 + carry

        if (!head) {
            // 第一个节点,头和尾节点是同一个
            head = tail = new ListNode(sum % 10)
        } else {
            // 尾结点的next指向新节点
            tail.next = new ListNode(sum % 10)
            // 然后将尾节点往后挪一个
            tail = tail.next
        }
        // 求余数并向下取整
        carry = Math.floor(sum / 10)
        // 两个节点同时往后挪
        if (l1) {
            l1 = l1.next
        }
        if (l2) {
            l2 = l2.next
        }
    }
    // 最后一个余数如果大于0需要创建一个新节点
    if (carry > 0) {
        tail.next = new ListNode(carry)
    }

    return head
};

3 无重复字符的最长子串-中等

给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

方法1-滑动窗口

双指针 + map/set表

image-20250321224701740

js
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function (s) {
  if (s.length === 0) {
    return 0;
  }

  // 最长子串的长度
  let result = 0;
  //  滑动窗口的左边界
  let left = 0;
  // 用于存储字符及其在字符串中的位置
  const charMap = {};
  for (let right = 0; right < s.length; right++) {
    const char = s[right];
    // 如果字符已在charMap中,并且其位置大于或等于当前窗口的左边界,
    // 则移动左边界到该字符的下一个位置
    if (charMap[char] >= left) {
      left = charMap[char] + 1;
    }
    // 更新字符在charMap中的位置
    charMap[char] = right;
    // 更新最长子串长度
    result = Math.max(result, right - left + 1);
  }
  return result;
};

Set优化版本

js

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function (s) {
  if (s.length === 0) {
    return 0;
  }

  let left = 0;
  let right = 0;
  let set = new Set();
  let max = 0;

  // 利用set进行优化
  while (right < s.length) {
    if (!set.has(s[right])) {
      set.add(s[right]);
      max = Math.max(max, set.size);
      right++;
    } else {
      set.delete(s[left]);
      left++;
    }
  }

  return max;
};

4 寻找两个正序数组的中位数-困难

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

方法1-暴力求解

最容易想到的:先合并数组,然后排序,利用数学公式--求中位数,很高效

image-20250321231514427
js
/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var findMedianSortedArrays = function (nums1, nums2) {
 let result = 0;
  // 合并数组并排序
  const nums = [...nums1, ...nums2].sort((a, b) => a - b);
  // 判断数组长度是否为偶数,如果是,则取中间两个数的平均值,否则取中间数的值
  const len = nums.length;
  if (len % 2 === 0) {
    // 偶数: 取中间两个数的平均值
    let center = Math.floor(len / 2);
    result = (nums[center] + nums[center - 1]) / 2;
  } else {
    // 奇数:取中间数的值
    result = nums[Math.floor(len / 2)];
  }
  // 保留5位小数
  return result;
};

方法2-二分查找

js

5 最长回文子串-中等

给你一个字符串 s,找到 s 中最长的 回文 子串。

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"
输出:"bb"

方法1-中心扩散

遍历每个字符串,分别以每个字符串为中心,向两边扩散,分别得到奇数和偶数的回文串

image-20250321234832420

js
/**
 * @param {string} s
 * @return {string}
 */
var longestPalindrome = function (s) {
  // 判断字符长度大于2直接返回s
  if (s.length < 2) {
    return s;
  }

  // 遍历字符串,每个字符中心扩散两次
  let ans = "";
  for (let i = 0; i < s.length; i++) {
    // 长度为奇数,从中心向两边扩展
    //   以当前字符为中心,分别扩散左右两个字符,"aba"
    let left = i - 1;
    let right = i + 1;
    while (left >= 0 && right < s.length && s[left] === s[right]) {
      left--;
      right++;
    }
    //  判断是否大于ans
    if (right - left - 1 > ans.length) {
      ans = s.slice(left + 1, right);
    }

    // 长度为偶数,从中心向两边扩展
    //   以当前字符和相邻字符为中心,分别扩散左右两个字符,"abba"
    left = i;
    right = i + 1;
    while (left >= 0 && right < s.length && s[left] === s[right]) {
      left--;
      right++;
    }
    if (right - left - 1 > ans.length) {
      ans = s.slice(left + 1, right);
    }
  }
  return ans;
};

6 Z 字形变换-中等

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

示例 1:

输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"

示例 2:

输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P     I    N
A   L S  I G
Y A   H R
P     I

示例 3:

输入:s = "A", numRows = 1
输出:"A"

题目分析

numRows:三行

分为4个字母一组,规律: (3 -1)X 2 = 4 》 (numRows - 1) x 2

image-20250321153943123

numRows:四行

分为6个字母一组,规律: (4 -1)X 2 = 6 》 (numRows - 1) x 2

image-20250321172546459

方法1-找规律

难点在于找到斜线方向的位置

js
/**
 * @param {string} s
 * @param {number} numRows
 * @return {string}
 */
var convert = function (s, numRows) {
  if (numRows == 1) {
    return s;
  }
  //convs[i]记录转换后第i行的字符串
  let convs = new Array(numRows).fill("");
  // 计算分组个数
  let groupNum = 2 * (numRows - 1);
  for (let i = 0; i < s.length; i++) {
    // 计算字母应该在的行数,即位置
    let rowsum = i % groupNum;
    if (rowsum < numRows) {
      // 如果0 ~ numRows - 1行,则直接加到对应位置
      convs[rowsum] += s[i];
    } else {
      // 难点:如果超过行数,就要找规律,然后发现 分组数量减去取模的余数,刚好就是对应的位置
      convs[groupNum - rowsum] += s[i];
    }
  }
  return convs.join("");
};

7 整数反转-中等

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

如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

示例 1:

输入:x = 123
输出:321

示例 2:

输入:x = -123
输出:-321

示例 3:

输入:x = 120
输出:21

示例 4:

输入:x = 0
输出:0

方法1-取模反转

拿这个整数的 末尾数字 以12345为例,先拿到5,再拿到4,之后是3,2,1,按这样的顺序就可以反向拼接处一个数字了,也就能达到 反转 的效果。 怎么拿末尾数字呢?好办,用取模运算就可以了

image-20250322101037820
js
/**
 * @param {number} x
 * @return {number}
 */
var reverse = function (x) {
  let res = 0;
  while (x != 0) {
    // 取模,留下个位数
    const digit = x % 10;
    // 乘以10 + 取模的个位数
    res = res * 10 + digit;
    // 余数除以10 正数向下取整----负数需要向上取整,比如-3726/10 = -372
    // x = ~~(x / 10)
    x = x > 0 ? Math.floor(x / 10) : Math.ceil(x / 10);

    // 判断一下边界条件
    if (res < Math.pow(-2, 31) || res > Math.pow(2, 31) - 1) {
      return 0;
    }
  }
  return res;
};

8 字符串转换整数 (atoi)-中等

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数。

函数 myAtoi(string s) 的算法如下:

  1. **空格:**读入字符串并丢弃无用的前导空格(" "
  2. **符号:**检查下一个字符(假设还未到字符末尾)为 '-' 还是 '+'。如果两者都不存在,则假定结果为正。
  3. **转换:**通过跳过前置零来读取该整数,直到遇到非数字字符或到达字符串的结尾。如果没有读取数字,则结果为0。
  4. **舍入:**如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被舍入为 −231 ,大于 231 − 1 的整数应该被舍入为 231 − 1

返回整数作为最终结果。

示例 1:

**输入:**s = "42"

**输出:**42

**解释:**加粗的字符串为已经读入的字符,插入符号是当前读取的字符。

带下划线线的字符是所读的内容,插入符号是当前读入位置。
第 1 步:"42"(当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第 3 步:"42"(读入 "42")
           ^

示例 2:

**输入:**s = " -042"

输出:-42

解释:

第 1 步:"   -042"(读入前导空格,但忽视掉)
            ^
第 2 步:"   -042"(读入 '-' 字符,所以结果应该是负数)
             ^
第 3 步:"   -042"(读入 "042",在结果中忽略前导零)
               ^

示例 3:

**输入:**s = "1337c0d3"

**输出:**1337

解释:

第 1 步:"1337c0d3"(当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"1337c0d3"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第 3 步:"1337c0d3"(读入 "1337";由于下一个字符不是一个数字,所以读入停止)
             ^

示例 4:

**输入:**s = "0-1"

**输出:**0

解释:

第 1 步:"0-1" (当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"0-1" (当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第 3 步:"0-1" (读入 "0";由于下一个字符不是一个数字,所以读入停止)
          ^

示例 5:

**输入:**s = "words and 987"

**输出:**0

解释:

读取在第一个非数字字符“w”处停止。

提示:

  • 0 <= s.length <= 200
  • s 由英文字母(大写和小写)、数字(0-9)、' ''+''-''.' 组成

方法1- 取巧方法

题目正好和前端的 parseInt 方法规则一致,直接使用

js
console.log(parseInt("123")); // 输出: 123
console.log(parseInt("  -42")); // 输出: -42
console.log(parseInt("4.8")); // 输出: 4,因为解析停止在'.'
console.log(parseInt("words and 987")); // 输出: NaN,因为无法解析为整数
js
/**
 * @param {string} s
 * @return {number}
 */
var myAtoi = function (s) {
  const number = parseInt(s, 10);

  if (isNaN(number)) {
    return 0;
  } else if (number < Math.pow(-2, 31) || number > Math.pow(2, 31) - 1) {
    return number < Math.pow(-2, 31) ? Math.pow(-2, 31) : Math.pow(2, 31) - 1;
  } else {
    return number;
  }
};

方法2-状态机

js
/**
 * @param {string} s
 * @return {number}
 */
var myAtoi = function (s) {
  // 结果
  let result = 0;
  // 符号标识
  let sign = 1;
  // 索引序号
  let index = 0;
  // 空格
  let blank = " ";

  // 忽略前导空白字符
  while (blank.includes(s[index])) {
    index++;
  }

  // 处理正负号
  if (s[index] === "-") {
    sign = -1;
    index++;
  } else if (s[index] === "+") {
    index++;
  }

  // 解析数字部分
  while (index < s.length && /[0-9]/.test(s[index])) {
    // 将字符转换为数字并累加到结果中
    result = result * 10 + (s[index] - "0") * sign;
    index++;
    // 判断边界条件
    if (result <= Math.pow(-2, 31)) {
      return Math.pow(-2, 31);
    }
    console.log(result);
    if (result >= Math.pow(2, 31) - 1) {
      return Math.pow(2, 31) - 1;
    }
  }

  return result; // 应用符号并返回结果
};

方法3-利用正则匹配

js
/**
 * @param {string} s
 * @return {number}
 */
var myAtoi = function (s) {
  //利用正则匹配
    const str = s.trim().match(/^[-|+]{0,1}[0-9]+/);
    // 范围判断
    if (str === null) {
      return 0;
    }
    const result = Number(str[0]);
    if (result >= Math.pow(2, 31) - 1) {
      return Math.pow(2, 31) - 1;
    } else if (result <= Math.pow(-2, 31)) {
      return Math.pow(-2, 31);
    }
    return result;
};

9 回文数-简单

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

  • 例如,121 是回文,而 123 不是。

示例 1:

输入:x = 121
输出:true

示例 2:

输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。

方法1-直接求解

字符串分割 + 反转 + 拼接 然后与原字符对比

js
/**
 * @param {number} x
 * @return {boolean}
 */
var isPalindrome = function (x) {
  if (x < 0) return false;
  var strx = String(x);
  return strx.split("").reverse().join("") === strx ? true : false;
};

10 正则表达式匹配

11 盛最多水的容器-中等

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

**说明:**你不能倾斜容器。

示例 1:

img
输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例 2:

输入:height = [1,1]
输出:1

方法1-双指针

分解从左右两边向中间靠近,分别计算长度 X 高度

js
/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function (height) {
  if (height.length === 0) {
    return 0;
  }

  let left = 0;
  let right = height.length - 1;
  let result = 0;

  // 双指针,截取中间的字符排序后对比
  // 然后左右指针同步移动到,右指针等于字符串长度即可
  while (left <= right) {
    let s = (right - left) * Math.min(height[left], height[right]);
    result = Math.max(s, result);

    if (height[left] <= height[right]) {
      left++;
    } else {
      right--;
    }
  }

  return result;
};

12 整数转罗马数字-简单

七个不同的符号代表罗马数字,其值如下:

符号
I1
V5
X10
L50
C100
D500
M1000

罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则:

  • 如果该值不是以 4 或 9 开头,请选择可以从输入中减去的最大值的符号,将该符号附加到结果,减去其值,然后将其余部分转换为罗马数字。
  • 如果该值以 4 或 9 开头,使用 减法形式,表示从以下符号中减去一个符号,例如 4 是 5 (V) 减 1 (I): IV ,9 是 10 (X) 减 1 (I):IX。仅使用以下减法形式:4 (IV),9 (IX),40 (XL),90 (XC),400 (CD) 和 900 (CM)。
  • 只有 10 的次方(I, X, C, M)最多可以连续附加 3 次以代表 10 的倍数。你不能多次附加 5 (V),50 (L) 或 500 (D)。如果需要将符号附加4次,请使用 减法形式

给定一个整数,将其转换为罗马数字。

示例 1:

**输入:**num = 3749

输出: "MMMDCCXLIX"

解释:

3000 = MMM 由于 1000 (M) + 1000 (M) + 1000 (M)
 700 = DCC 由于 500 (D) + 100 (C) + 100 (C)
  40 = XL 由于 50 (L) 减 10 (X)
   9 = IX 由于 10 (X) 减 1 (I)
注意:49 不是 50 (L) 减 1 (I) 因为转换是基于小数位

示例 2:

**输入:**num = 58

输出:"LVIII"

解释:

50 = L
 8 = VIII

示例 3:

**输入:**num = 1994

输出:"MCMXCIV"

解释:

1000 = M
 900 = CM
  90 = XC
   4 = IV

提示:

  • 1 <= num <= 3999

方法1-穷举-取模法-硬编码

数字小于3999,可以把个位 、十位、百位、千位的整数都列一面,然后取模匹配

第一步:梳理题目 得到字母与数字的对应关系

fig3

第二步:根据以上13个符号,分别凑出一张硬编码的数字表,0对应空字符

fig4

第三步:利用模运算和除法运算,我们可以得到 num 每个位上的数字:

js
thousands_digit = num / 1000
hundreds_digit = (num % 1000) / 100
tens_digit = (num % 100) / 10
ones_digit = num % 10

第四步:根据 num 每个位上的数字,在硬编码表中查找对应的罗马字符,并将结果拼接在一起,即为 num 对应的罗马数字。

js
/**
 * @param {number} num
 * @return {string}
 */
var intToRoman = function (num) {
  const thousands = ["", "M", "MM", "MMM"];
  const hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"];
  const tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"];
  const ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"];

  const roman = [];
  // 千位
  roman.push(thousands[Math.floor(num / 1000)]);
  // 百位
  roman.push(hundreds[Math.floor((num % 1000) / 100)]);
  // 十位
  roman.push(tens[Math.floor((num % 100) / 10)]);
  // 个位
  roman.push(ones[num % 10]);
  return roman.join("");
};

13 罗马数字转整数-简单

罗马数字包含以下七种字符: IVXLCDM

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。

示例 1:

输入: s = "III"
输出: 3

示例 2:

输入: s = "IV"
输出: 4

示例 3:

输入: s = "IX"
输出: 9

示例 4:

输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

提示:

  • 1 <= s.length <= 15
  • s 仅含字符 ('I', 'V', 'X', 'L', 'C', 'D', 'M')
  • 题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999]
  • 题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
  • IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。

方法1- 暴力求解

js
/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function (s) {
  const symbolMap = {
    I: 1,
    V: 5,
    X: 10,
    L: 50,
    C: 100,
    D: 500,
    M: 1000,
  };

  let ans = 0;
  const len = s.length;
  for (let i = 0; i < len; i++) {
    debugger;
    if (i + 1 < len && symbolMap[s[i]] < symbolMap[s[i + 1]]) {
      // 小数在左,大数在右,需要减去小数
      ans -= symbolMap[s[i]];
    } else {
      // 大数在左,小数在右,需要加上小数
      ans += symbolMap[s[i]];
    }
  }
  return ans;
};

14 最长公共前缀-简单

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例 1:

输入:strs = ["flower","flow","flight"]
输出:"fl"

示例 2:

输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。

提示:

  • 1 <= strs.length <= 200
  • 0 <= strs[i].length <= 200
  • strs[i] 如果非空,则仅由小写英文字母组成

方法1- 横向比较

js
/**
 * @param {string[]} strs
 * @return {string}
 */
var longestCommonPrefix = function (strs) {
  if (strs.length == 0) return "";

  let prefix = strs[0];
  for (let i = 1; i < strs.length; i++) {
    // 如果没有包含第一个字符串,就遍历后面的所有字符串,每个字符串都截取一个相同的字符串
    while (!strs[i].startsWith(prefix)) {
      // 每次从末尾截掉一个字符串,逐个往前进行截取
      prefix = prefix.substring(0, prefix.length - 1);
      if (prefix.length === 0) return "";
    }
  }
  return prefix;
};

方法2-纵向比较

js
/**
 * @param {string[]} strs
 * @return {string}
 */
var longestCommonPrefix = function (strs) {
  if (strs.length === 0) return "";

  // 纵向比较,先假定第一个是最长的公共串
  for (let i = 0; i < strs[0].length; i++) {
    const char = strs[0][i];
    //  从下标为1的字符开始,纵向比对每一个字符串的对应位置的字符
    for (let j = 1; j < strs.length; j++) {
      // i === strs[j].length 如果后续字符串中有比第一个长度小的,就以端的为主返回
      // strs[j][i] !== char 纵向比对每一个字符串
      if (i === strs[j].length || strs[j][i] !== char) {
        return strs[0].substring(0, i);
      }
    }
  }
  // 只有一个就返回
  return strs[0];
};

15 三数之和-中等

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

方法1-双指针

js

Released under the MIT License.