Leetcode: 0011-0020题速览

news/2024/12/22 22:54:11/

Leetcode: 0011-0020题速览

本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解

遵从开源协议为知识共享 版权归属-相同方式共享 4.0 国际 公共许可证

研一在读备战就业,制此集合便于空闲时间浏览,有任何疑惑问题欢迎讨论,共同进步

目录

  • Leetcode: 0011-0020题速览
    • [11. 盛最多水的容器](https://leetcode.cn/problems/container-with-most-water)
      • 题目描述
      • 解法
        • 方法一:双指针
          • Python3
          • Java
          • C++
    • [12. 整数转罗马数字](https://leetcode.cn/problems/integer-to-roman)
      • 题目描述
      • 解法
        • 方法一:贪心
          • Python3
          • Java
          • C++
    • [13. 罗马数字转整数](https://leetcode.cn/problems/roman-to-integer)
      • 题目描述
      • 解法
        • 方法一:哈希表 + 模拟
          • Python3
          • Java
          • C++
    • [14. 最长公共前缀](https://leetcode.cn/problems/longest-common-prefix)
      • 题目描述
      • 解法
        • 方法一:字符比较
          • Python3
          • Java
          • C++
    • [15. 三数之和](https://leetcode.cn/problems/3sum)
      • 题目描述
      • 解法
        • 方法一:排序 + 双指针
          • Python3
          • Java
          • C++
    • [16. 最接近的三数之和](https://leetcode.cn/problems/3sum-closest)
      • 题目描述
      • 解法
        • 方法一:排序 + 双指针
          • Python3
          • Java
          • C++
    • [17. 电话号码的字母组合](https://leetcode.cn/problems/letter-combinations-of-a-phone-number)
      • 题目描述
      • 解法
        • 方法一:遍历
          • Python3
          • Java
          • C++
    • [18. 四数之和](https://leetcode.cn/problems/4sum)
      • 题目描述
      • 解法
        • 方法一:排序 + 双指针
          • Python3
          • Java
          • C++
    • [19. 删除链表的倒数第 N 个结点](https://leetcode.cn/problems/remove-nth-node-from-end-of-list)
      • 题目描述
      • 解法
        • 方法一:快慢指针
          • Python3
          • Java
          • C++
    • [20. 有效的括号](https://leetcode.cn/problems/valid-parentheses)
      • 题目描述
      • 解法
        • 方法一:栈
          • Python3
          • Java
          • C++

leetcode.cn/problems/container-with-most-water" rel="nofollow">11. 盛最多水的容器

题目描述

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

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

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

说明:你不能倾斜容器。

 

示例 1:

装水

输入:[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

 

提示:

  • n == height.length
  • 2 <= n <= 105
  • 0 <= height[i] <= 104

难度:中等

标签:贪心,数组,双指针

解法

方法一:双指针

一开始,我们考虑相距最远的两个柱子所能容纳水的容量。水的宽度是两根柱子之间的距离,而水的高度取决于两根柱子之间较短的那个。

当前柱子是最两侧的柱子,水的宽度最大,其他的组合,水的宽度都比这个小。不妨假设左侧柱子的高度小于等于右侧柱子的高度,那么水的高度就是左侧柱子的高度。如果我们移动右侧柱子,那么水的宽度就减小了,而水的高度却不会增加,因此水的容量一定减少。所以我们移动左侧柱子,更新最大容量。

循环此过程,直到两个柱子相遇。

时间复杂度 O ( n ) O(n) O(n),其中 n n n 是数组 height 的长度。空间复杂度 O ( 1 ) O(1) O(1)

Python3
class Solution:def maxArea(self, height: List[int]) -> int:i, j = 0, len(height) - 1ans = 0while i < j:t = (j - i) * min(height[i], height[j])ans = max(ans, t)if height[i] < height[j]:i += 1else:j -= 1return ans
Java
class Solution {public int maxArea(int[] height) {int i = 0, j = height.length - 1;int ans = 0;while (i < j) {int t = Math.min(height[i], height[j]) * (j - i);ans = Math.max(ans, t);if (height[i] < height[j]) {++i;} else {--j;}}return ans;}
}
C++
class Solution {
public:int maxArea(vector<int>& height) {int i = 0, j = height.size() - 1;int ans = 0;while (i < j) {int t = min(height[i], height[j]) * (j - i);ans = max(ans, t);if (height[i] < height[j]) {++i;} else {--j;}}return ans;}
};

leetcode.cn/problems/integer-to-roman" rel="nofollow">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 © + 100 ©
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

难度:中等

标签:哈希表,数学,字符串

解法

方法一:贪心

我们可以先将所有可能的符号 c s cs cs 和对应的数值 v s vs vs 列出来,然后从大到小枚举每个数值 v s [ i ] vs[i] vs[i],每次尽可能多地使用该数值对应的符号 c s [ i ] cs[i] cs[i],直到数字 n u m num num 变为 0 0 0

时间复杂度为 O ( m ) O(m) O(m),空间复杂度为 O ( m ) O(m) O(m)。其中 m m m 为符号的个数。

Python3
class Solution:def intToRoman(self, num: int) -> str:cs = ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I')vs = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)ans = []for c, v in zip(cs, vs):while num >= v:num -= vans.append(c)return ''.join(ans)
Java
class Solution {public String intToRoman(int num) {List<String> cs= List.of("M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I");List<Integer> vs = List.of(1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1);StringBuilder ans = new StringBuilder();for (int i = 0, n = cs.size(); i < n; ++i) {while (num >= vs.get(i)) {num -= vs.get(i);ans.append(cs.get(i));}}return ans.toString();}
}
C++
class Solution {
public:string intToRoman(int num) {vector<string> cs = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};vector<int> vs = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};string ans;for (int i = 0; i < cs.size(); ++i) {while (num >= vs[i]) {num -= vs[i];ans += cs[i];}}return ans;}
};

leetcode.cn/problems/roman-to-integer" rel="nofollow">13. 罗马数字转整数

题目描述

罗马数字包含以下七种字符: I, V, X, LCD 和 M

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

例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做  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 。
  • 关于罗马数字的详尽书写规则,可以参考 罗马数字 - 百度百科。

难度:简单

标签:哈希表,数学,字符串

解法

方法一:哈希表 + 模拟

我们先用哈希表 d d d 记录每个字符对应的数值,然后从左到右遍历字符串 s s s,如果当前字符对应的数值小于右边字符对应的数值,则减去当前字符对应的数值,否则加上当前字符对应的数值。

时间复杂度 ( n ) (n) (n),空间复杂度 O ( m ) O(m) O(m)。其中 n n n m m m 分别为字符串 s s s 的长度和字符集的大小。

Python3
class Solution:def romanToInt(self, s: str) -> int:d = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}return sum((-1 if d[a] < d[b] else 1) * d[a] for a, b in pairwise(s)) + d[s[-1]]
Java
class Solution {public int romanToInt(String s) {String cs = "IVXLCDM";int[] vs = {1, 5, 10, 50, 100, 500, 1000};Map<Character, Integer> d = new HashMap<>();for (int i = 0; i < vs.length; ++i) {d.put(cs.charAt(i), vs[i]);}int n = s.length();int ans = d.get(s.charAt(n - 1));for (int i = 0; i < n - 1; ++i) {int sign = d.get(s.charAt(i)) < d.get(s.charAt(i + 1)) ? -1 : 1;ans += sign * d.get(s.charAt(i));}return ans;}
}
C++
class Solution {
public:int romanToInt(string s) {unordered_map<char, int> nums{{'I', 1},{'V', 5},{'X', 10},{'L', 50},{'C', 100},{'D', 500},{'M', 1000},};int ans = nums[s.back()];for (int i = 0; i < s.size() - 1; ++i) {int sign = nums[s[i]] < nums[s[i + 1]] ? -1 : 1;ans += sign * nums[s[i]];}return ans;}
};

leetcode.cn/problems/longest-common-prefix" rel="nofollow">14. 最长公共前缀

题目描述

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

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

 

示例 1:

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

示例 2:

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

 

提示:

  • 1 <= strs.length <= 200
  • 0 <= strs[i].length <= 200
  • strs[i] 仅由小写英文字母组成

难度:简单

标签:字典树,字符串

解法

方法一:字符比较

我们以第一个字符串 s t r s [ 0 ] strs[0] strs[0] 为基准,依次比较后面的字符串的第 i i i 个字符是否与 s t r s [ 0 ] strs[0] strs[0] 的第 i i i 个字符相同,如果相同则继续比较下一个字符,否则返回 s t r s [ 0 ] strs[0] strs[0] 的前 i i i 个字符。

遍历结束,说明所有字符串的前 i i i 个字符都相同,返回 s t r s [ 0 ] strs[0] strs[0] 即可。

时间复杂度 ( n × m ) (n \times m) (n×m),其中 n n n m m m 分别为字符串数组的长度以及字符串的最小长度。空间复杂度 O ( 1 ) O(1) O(1)

Python3
class Solution:def longestCommonPrefix(self, strs: List[str]) -> str:for i in range(len(strs[0])):for s in strs[1:]:if len(s) <= i or s[i] != strs[0][i]:return s[:i]return strs[0]
Java
class Solution {public String longestCommonPrefix(String[] strs) {int n = strs.length;for (int i = 0; i < strs[0].length(); ++i) {for (int j = 1; j < n; ++j) {if (strs[j].length() <= i || strs[j].charAt(i) != strs[0].charAt(i)) {return strs[0].substring(0, i);}}}return strs[0];}
}
C++
class Solution {
public:string longestCommonPrefix(vector<string>& strs) {int n = strs.size();for (int i = 0; i < strs[0].size(); ++i) {for (int j = 1; j < n; ++j) {if (strs[j].size() <= i || strs[j][i] != strs[0][i]) {return strs[0].substr(0, i);}}}return strs[0];}
};

leetcode.cn/problems/3sum" rel="nofollow">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

难度:中等

标签:数组,双指针,排序

解法

方法一:排序 + 双指针

我们注意到,题目不要求我们按照顺序返回三元组,因此我们不妨先对数组进行排序,这样就可以方便地跳过重复的元素。

接下来,我们枚举三元组的第一个元素 n u m s [ i ] nums[i] nums[i],其中 0 ≤ i < n − 2 0 \leq i \lt n - 2 0i<n2。对于每个 i i i,我们可以通过维护两个指针 j = i + 1 j = i + 1 j=i+1 k = n − 1 k = n - 1 k=n1,从而找到满足 n u m s [ i ] + n u m s [ j ] + n u m s [ k ] = 0 nums[i] + nums[j] + nums[k] = 0 nums[i]+nums[j]+nums[k]=0 j j j k k k。在枚举的过程中,我们需要跳过重复的元素,以避免出现重复的三元组。

具体判断逻辑如下:

如果 i > 0 i \gt 0 i>0 并且 n u m s [ i ] = n u m s [ i − 1 ] nums[i] = nums[i - 1] nums[i]=nums[i1],则说明当前枚举的元素与上一个元素相同,我们可以直接跳过,因为不会产生新的结果。

如果 n u m s [ i ] > 0 nums[i] \gt 0 nums[i]>0,则说明当前枚举的元素大于 0 0 0,则三数之和必然无法等于 0 0 0,结束枚举。

否则,我们令左指针 j = i + 1 j = i + 1 j=i+1,右指针 k = n − 1 k = n - 1 k=n1,当 j < k j \lt k j<k 时,执行循环,计算三数之和 x = n u m s [ i ] + n u m s [ j ] + n u m s [ k ] x = nums[i] + nums[j] + nums[k] x=nums[i]+nums[j]+nums[k],并与 0 0 0 比较:

  • 如果 x < 0 x \lt 0 x<0,则说明 n u m s [ j ] nums[j] nums[j] 太小,我们需要将 j j j 右移一位。
  • 如果 x > 0 x \gt 0 x>0,则说明 n u m s [ k ] nums[k] nums[k] 太大,我们需要将 k k k 左移一位。
  • 否则,说明我们找到了一个合法的三元组,将其加入答案,并将 j j j 右移一位,将 k k k 左移一位,同时跳过所有重复的元素,继续寻找下一个合法的三元组。

枚举结束后,我们即可得到三元组的答案。

时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( log ⁡ n ) O(\log n) O(logn)。其中 n n n 为数组的长度。

Python3
class Solution:def threeSum(self, nums: List[int]) -> List[List[int]]:nums.sort()n = len(nums)ans = []for i in range(n - 2):if nums[i] > 0:breakif i and nums[i] == nums[i - 1]:continuej, k = i + 1, n - 1while j < k:x = nums[i] + nums[j] + nums[k]if x < 0:j += 1elif x > 0:k -= 1else:ans.append([nums[i], nums[j], nums[k]])j, k = j + 1, k - 1while j < k and nums[j] == nums[j - 1]:j += 1while j < k and nums[k] == nums[k + 1]:k -= 1return ans
Java
class Solution {public List<List<Integer>> threeSum(int[] nums) {Arrays.sort(nums);List<List<Integer>> ans = new ArrayList<>();int n = nums.length;for (int i = 0; i < n - 2 && nums[i] <= 0; ++i) {if (i > 0 && nums[i] == nums[i - 1]) {continue;}int j = i + 1, k = n - 1;while (j < k) {int x = nums[i] + nums[j] + nums[k];if (x < 0) {++j;} else if (x > 0) {--k;} else {ans.add(List.of(nums[i], nums[j++], nums[k--]));while (j < k && nums[j] == nums[j - 1]) {++j;}while (j < k && nums[k] == nums[k + 1]) {--k;}}}}return ans;}
}
C++
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {sort(nums.begin(), nums.end());vector<vector<int>> ans;int n = nums.size();for (int i = 0; i < n - 2 && nums[i] <= 0; ++i) {if (i && nums[i] == nums[i - 1]) {continue;}int j = i + 1, k = n - 1;while (j < k) {int x = nums[i] + nums[j] + nums[k];if (x < 0) {++j;} else if (x > 0) {--k;} else {ans.push_back({nums[i], nums[j++], nums[k--]});while (j < k && nums[j] == nums[j - 1]) {++j;}while (j < k && nums[k] == nums[k + 1]) {--k;}}}}return ans;}
};

leetcode.cn/problems/3sum-closest" rel="nofollow">16. 最接近的三数之和

题目描述

给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。

返回这三个数的和。

假定每组输入只存在恰好一个解。

 

示例 1:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2)。

示例 2:

输入:nums = [0,0,0], target = 1
输出:0
解释:与 target 最接近的和是 0(0 + 0 + 0 = 0)。

 

提示:

  • 3 <= nums.length <= 1000
  • -1000 <= nums[i] <= 1000
  • -104 <= target <= 104

难度:中等

标签:数组,双指针,排序

解法

方法一:排序 + 双指针

我们将数组排序,然后遍历数组,对于每个元素 n u m s [ i ] nums[i] nums[i],我们使用指针 j j j k k k 分别指向 i + 1 i+1 i+1 n − 1 n-1 n1,计算三数之和,如果三数之和等于 t a r g e t target target,则直接返回 t a r g e t target target,否则根据与 t a r g e t target target 的差值更新答案。如果三数之和大于 t a r g e t target target,则将 k k k 向左移动一位,否则将 j j j 向右移动一位。

时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( log ⁡ n ) O(\log n) O(logn)。其中 n n n 为数组长度。

Python3
class Solution:def threeSumClosest(self, nums: List[int], target: int) -> int:nums.sort()n = len(nums)ans = inffor i, v in enumerate(nums):j, k = i + 1, n - 1while j < k:t = v + nums[j] + nums[k]if t == target:return tif abs(t - target) < abs(ans - target):ans = tif t > target:k -= 1else:j += 1return ans
Java
class Solution {public int threeSumClosest(int[] nums, int target) {Arrays.sort(nums);int ans = 1 << 30;int n = nums.length;for (int i = 0; i < n; ++i) {int j = i + 1, k = n - 1;while (j < k) {int t = nums[i] + nums[j] + nums[k];if (t == target) {return t;}if (Math.abs(t - target) < Math.abs(ans - target)) {ans = t;}if (t > target) {--k;} else {++j;}}}return ans;}
}
C++
class Solution {
public:int threeSumClosest(vector<int>& nums, int target) {sort(nums.begin(), nums.end());int ans = 1 << 30;int n = nums.size();for (int i = 0; i < n; ++i) {int j = i + 1, k = n - 1;while (j < k) {int t = nums[i] + nums[j] + nums[k];if (t == target) return t;if (abs(t - target) < abs(ans - target)) ans = t;if (t > target)--k;else++j;}}return ans;}
};

leetcode.cn/problems/letter-combinations-of-a-phone-number" rel="nofollow">17. 电话号码的字母组合

题目描述

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

电话机

 

示例 1:

输入:digits = “23”
输出:[“ad”,“ae”,“af”,“bd”,“be”,“bf”,“cd”,“ce”,“cf”]

示例 2:

输入:digits = “”
输出:[]

示例 3:

输入:digits = “2”
输出:[“a”,“b”,“c”]

 

提示:

  • 0 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。

难度:中等

标签:哈希表,字符串,回溯

解法

方法一:遍历

我们先用一个数组或者哈希表存储每个数字对应的字母,然后遍历每个数字,将其对应的字母与之前的结果进行组合,得到新的结果。

时间复杂度 O ( 4 n ) O(4^n) O(4n)。空间复杂度 O ( 4 n ) O(4^n) O(4n)。其中 n n n 是输入数字的长度。

Python3
class Solution:def letterCombinations(self, digits: str) -> List[str]:if not digits:return []d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]ans = [""]for i in digits:s = d[int(i) - 2]ans = [a + b for a in ans for b in s]return ans
Java
class Solution {public List<String> letterCombinations(String digits) {List<String> ans = new ArrayList<>();if (digits.length() == 0) {return ans;}ans.add("");String[] d = new String[] {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};for (char i : digits.toCharArray()) {String s = d[i - '2'];List<String> t = new ArrayList<>();for (String a : ans) {for (String b : s.split("")) {t.add(a + b);}}ans = t;}return ans;}
}
C++
class Solution {
public:vector<string> letterCombinations(string digits) {if (digits.empty()) {return {};}vector<string> d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};vector<string> ans = {""};for (auto& i : digits) {string s = d[i - '2'];vector<string> t;for (auto& a : ans) {for (auto& b : s) {t.push_back(a + b);}}ans = move(t);}return ans;}
};

leetcode.cn/problems/4sum" rel="nofollow">18. 四数之和

题目描述

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

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

 

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

 

提示:

  • 1 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

难度:中等

标签:数组,双指针,排序

解法

方法一:排序 + 双指针

我们注意到,题目中要求找到不重复的四元组,那么我们可以先对数组进行排序,这样就可以方便地跳过重复的元素。

接下来,我们枚举四元组的前两个元素 n u m s [ i ] nums[i] nums[i] n u m s [ j ] nums[j] nums[j],其中 i < j i \lt j i<j,在枚举的过程中,我们跳过重复的 n u m s [ i ] nums[i] nums[i] n u m s [ j ] nums[j] nums[j]。然后,我们用两个指针 k k k l l l 分别指向 n u m s [ i ] nums[i] nums[i] n u m s [ j ] nums[j] nums[j] 后面的两端,令 x = n u m s [ i ] + n u m s [ j ] + n u m s [ k ] + n u m s [ l ] x = nums[i] + nums[j] + nums[k] + nums[l] x=nums[i]+nums[j]+nums[k]+nums[l],我们将 x x x t a r g e t target target 比较,进行如下操作:

  • 如果 x < t a r g e t x \lt target x<target,则更新 k = k + 1 k = k + 1 k=k+1 以得到更大的 x x x
  • 如果 x > t a r g e t x \gt target x>target,则更新 l = l − 1 l = l - 1 l=l1 以得到更小的 x x x
  • 否则,说明找到了一个四元组 ( n u m s [ i ] , n u m s [ j ] , n u m s [ k ] , n u m s [ l ] ) (nums[i], nums[j], nums[k], nums[l]) (nums[i],nums[j],nums[k],nums[l]),将其加入答案,然后我们更新指针 k k k l l l,并跳过所有重复的元素,防止答案中包含重复的四元组,继续寻找下一个四元组。

时间复杂度为 O ( n 3 ) O(n^3) O(n3),空间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),其中 n n n 是数组的长度。

Python3
class Solution:def fourSum(self, nums: List[int], target: int) -> List[List[int]]:n = len(nums)ans = []if n < 4:return ansnums.sort()for i in range(n - 3):if i and nums[i] == nums[i - 1]:continuefor j in range(i + 1, n - 2):if j > i + 1 and nums[j] == nums[j - 1]:continuek, l = j + 1, n - 1while k < l:x = nums[i] + nums[j] + nums[k] + nums[l]if x < target:k += 1elif x > target:l -= 1else:ans.append([nums[i], nums[j], nums[k], nums[l]])k, l = k + 1, l - 1while k < l and nums[k] == nums[k - 1]:k += 1while k < l and nums[l] == nums[l + 1]:l -= 1return ans
Java
class Solution {public List<List<Integer>> fourSum(int[] nums, int target) {int n = nums.length;List<List<Integer>> ans = new ArrayList<>();if (n < 4) {return ans;}Arrays.sort(nums);for (int i = 0; i < n - 3; ++i) {if (i > 0 && nums[i] == nums[i - 1]) {continue;}for (int j = i + 1; j < n - 2; ++j) {if (j > i + 1 && nums[j] == nums[j - 1]) {continue;}int k = j + 1, l = n - 1;while (k < l) {long x = (long) nums[i] + nums[j] + nums[k] + nums[l];if (x < target) {++k;} else if (x > target) {--l;} else {ans.add(List.of(nums[i], nums[j], nums[k++], nums[l--]));while (k < l && nums[k] == nums[k - 1]) {++k;}while (k < l && nums[l] == nums[l + 1]) {--l;}}}}}return ans;}
}
C++
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {int n = nums.size();vector<vector<int>> ans;if (n < 4) {return ans;}sort(nums.begin(), nums.end());for (int i = 0; i < n - 3; ++i) {if (i && nums[i] == nums[i - 1]) {continue;}for (int j = i + 1; j < n - 2; ++j) {if (j > i + 1 && nums[j] == nums[j - 1]) {continue;}int k = j + 1, l = n - 1;while (k < l) {long long x = (long long) nums[i] + nums[j] + nums[k] + nums[l];if (x < target) {++k;} else if (x > target) {--l;} else {ans.push_back({nums[i], nums[j], nums[k++], nums[l--]});while (k < l && nums[k] == nums[k - 1]) {++k;}while (k < l && nums[l] == nums[l + 1]) {--l;}}}}}return ans;}
};

leetcode.cn/problems/remove-nth-node-from-end-of-list" rel="nofollow">19. 删除链表的倒数第 N 个结点

题目描述

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

 

示例 1:

链表

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

 

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

 

进阶:你能尝试使用一趟扫描实现吗?

难度:中等

标签:链表,双指针

解法

方法一:快慢指针

我们定义两个指针 fastslow,初始时都指向链表的虚拟头结点 dummy

接着 fast 指针先向前移动 n n n 步,然后 fastslow 指针同时向前移动,直到 fast 指针到达链表的末尾。此时 slow.next 指针指向的结点就是倒数第 n 个结点的前驱结点,将其删除即可。

时间复杂度 O ( n ) O(n) O(n),其中 n n n 为链表的长度。空间复杂度 O ( 1 ) O(1) O(1)

Python3
## Definition for singly-linked list.
## class ListNode:
##     def __init__(self, val=0, next=None):
##         self.val = val
##         self.next = next
class Solution:def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:dummy = ListNode(next=head)fast = slow = dummyfor _ in range(n):fast = fast.nextwhile fast.next:slow, fast = slow.next, fast.nextslow.next = slow.next.nextreturn dummy.next
Java
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {ListNode dummy = new ListNode(0, head);ListNode fast = dummy, slow = dummy;while (n-- > 0) {fast = fast.next;}while (fast.next != null) {slow = slow.next;fast = fast.next;}slow.next = slow.next.next;return dummy.next;}
}
C++
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* dummy = new ListNode(0, head);ListNode* fast = dummy;ListNode* slow = dummy;while (n--) {fast = fast->next;}while (fast->next) {slow = slow->next;fast = fast->next;}slow->next = slow->next->next;return dummy->next;}
};

leetcode.cn/problems/valid-parentheses" rel="nofollow">20. 有效的括号

题目描述

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

 

示例 1:

输入:s = "()"

输出:true

示例 2:

输入:s = "()[]{}"

输出:true

示例 3:

输入:s = "(]"

输出:false

示例 4:

输入:s = "([])"

输出:true

 

提示:

  • 1 <= s.length <= 104
  • s 仅由括号 '()[]{}' 组成

难度:简单

标签:栈,字符串

解法

方法一:栈

遍历括号字符串 s s s,遇到左括号时,压入当前的左括号;遇到右括号时,弹出栈顶元素(若栈为空,直接返回 false),判断是否匹配,若不匹配,直接返回 false

也可以选择遇到左括号时,将右括号压入栈中;遇到右括号时,弹出栈顶元素(若栈为空,直接返回 false),判断是否是相等。若不匹配,直接返回 false

两者的区别仅限于括号转换时机,一个是在入栈时,一个是在出栈时。

遍历结束,若栈为空,说明括号字符串有效,返回 true;否则,返回 false

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)。其中 n n n 为括号字符串 s s s 的长度。

Python3
class Solution:def isValid(self, s: str) -> bool:stk = []d = {'()', '[]', '{}'}for c in s:if c in '({[':stk.append(c)elif not stk or stk.pop() + c not in d:return Falsereturn not stk
Java
class Solution {public boolean isValid(String s) {Deque<Character> stk = new ArrayDeque<>();for (char c : s.toCharArray()) {if (c == '(' || c == '{' || c == '[') {stk.push(c);} else if (stk.isEmpty() || !match(stk.pop(), c)) {return false;}}return stk.isEmpty();}private boolean match(char l, char r) {return (l == '(' && r == ')') || (l == '{' && r == '}') || (l == '[' && r == ']');}
}
C++
class Solution {
public:bool isValid(string s) {string stk;for (char c : s) {if (c == '(' || c == '{' || c == '[')stk.push_back(c);else if (stk.empty() || !match(stk.back(), c))return false;elsestk.pop_back();}return stk.empty();}bool match(char l, char r) {return (l == '(' && r == ')') || (l == '[' && r == ']') || (l == '{' && r == '}');}
};


http://www.ppmy.cn/news/1537481.html

相关文章

大数据面试-笔试SQL

一个表table: c_id u_id score&#xff1b;用SQL计算每个班级top5学生的平均分&#xff08;腾讯&#xff09; select class_id,avg(score) as score_avg from (select *,row_number() over(partition by class_id order by score desc) as score_rank from table ) t1 where t…

倪师学习笔记-天纪-斗数简介

一、学习过程 学习->验证->思考 二、算命方法 算命方法特点铁板神数适合核对六亲子平法准确度一般紫微斗数天文地理融合最好&#xff0c;批六亲不准&#xff0c;配合相可以提升准确率 三、果 天地人三者一起影响果&#xff0c;天时地利人和促成成功1/31/31/31算命部…

字节跳动青训营开始报名了!

关于青训营&#xff1a; 青训营是字节跳动技术团队发起的技术系列培训 &人才选拔项目;面向高校在校生&#xff0c;旨在培养优秀且具有职业竞争力的开发工程师。 本次技术训练营由掘金联合豆包MarsCode 团队主办课程包含前端、后端和 A 方向&#xff0c;在这个飞速发…

深度学习--------------------------------使用注意力机制的seq2seq

目录 动机加入注意力Bahdanau注意力的架构 总结Bahdanau注意力代码带有注意力机制的解码器基本接口实现带有Bahdanau注意力的循环神经网络解码器测试Bahdanau注意力解码器该部分总代码 训练从零实现总代码简洁实现代码 将几个英语句子翻译成法语该部分总代码 将注意力权重序列进…

Observer(观察者模式)

1. 意图 定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 在观察者模式中&#xff0c;有两类对象&#xff1a;被观察者&#xff08;Subject&#xff09;和观察者&#xff08;Observer&#xf…

C# 中抽象类与接口的异同

一、引言 在 C# 编程中&#xff0c;抽象类和接口都是实现代码复用和多态性的重要工具&#xff0c;但它们在很多方面存在差异。理解这些异同&#xff0c;有助于开发者在实际项目中做出恰当的选择&#xff0c;以构建高效且可维护的软件系统。 二、抽象类 &#xff08;一&#…

数据结构前置知识(下)

1. 包装类 Java为了让基本数据类型也能够继承Object,因此给每个基本数据类型提供了包装类, 这样就可以和平常的引用数据类型一样使用了,并且也可以应用在泛型上(后续讲) 基本数据类型包装类byteByteshortShortintIntergerlongLongfloatFloatdoubleDoublecharCharacterboolean…

k8s 的网络通信

目录 1 k8s通信整体架构 2 flannel 网络插件 2.1 flannel 插件组成 2.2 flannel 插件的通信过程 2.3 flannel 支持的后端模式 3 calico 网络插件 3.1 calico 简介 3.2 calico 网络架构 3.3 部署 calico 1 k8s通信整体架构 k8s通过CNI接口接入其他插件来实现网络通讯。目前比较…