LeetCode 11-20题

 

正文

本博客记录的是 LeetCode 11 到 20 题的题解

之前很少使用的语法

ZIP函数, 返回的是对应参数同一个位上对应的元组列表
压缩两遍就是本身
注意二次压缩需要 加上 * ,是为了将元组、或者是列表打散

# zip 函数
>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b) # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
>>> zip(a,c) # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
>>> zip(*zipped) # *zipped 是将 zipped:[(1, 4), (2, 5), (3, 6)] -> (1, 4), (2, 5), (3, 6)
[(1, 2, 3), (4, 5, 6)]
# 第14题,绝绝子
ret = ""
for i in zip(*strs): # 将这个列表打散
if len(set(i)) == 1:
ret += i[0]
else:
return ret
# 第17题,绝绝子,真的迭代棒!
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
# iterative
if not digits: return []
dic = {'2':'abc','3':'def','4':'ghi','5':'jkl','6':'mno','7':'pqrs','8':'tuv','9':'wxyz'}
res = [""]
for i in digits:
res = [r + j for r in res for j in dic[i]] # 他在原本 r 的基础上进行了叠加,相当于DFS进行下一层
print(res)
return res
# 第19题,加入头结点 dummy
dummy = ListNode(val=-1, next=head)
# 注意区别
stk is not Nonenot stk有区别
not None --> True
not [] --> True

11. Container With Most Water

解决该问题,主要有两种思路,

  1. 双指针算法
  2. 滑动区间内 + 双指针

双指针算法

双指针算法特别简单,具体操作如下:
我们使用指针 i, j分别指向 height 数组的首尾两端,并计算 i, j 所构成容器的面积
然后选择高度小的指针指向内部,不断获得容器面积,更新 Res 答案。

算法正确性的证明:
我们假设 res_i, res_j 是我们最终产生最大容器的两个高度,我们使用双指针算法时候,一定会有一个指针率先指向 res_i或者是res_j,在这里,我们假设是 i率先指向了res_i
我们此时我们对 height[res_i] 和 height[res_j] 的情况进行讨论,看 i, j是否可以同时指向 res_i, res_j
第一种情况,height[res_i] < height[res_j]

指针 i不会一定,j不断左移必定会指向res_j,主要是 height[j] 必须小于 height[res_i],否则 res_i 与 res_j便不会是最终解,与前提矛盾。

第二种情况,height[res_i] > height[res_j]

指针 i不会一定,j不断左移必定会指向res_j,主要是 height[j] 必须小于 height[res_j],否则 res_i 与 res_j便不会是最终解,与前提矛盾。
那么也一定小于height[res_i]

总而言之,当指针i指向了 res_i, res_j之外的高度,一定要小于 height[res_i], height[res_j]的最小值,那么也就一定小于 height[res_i],也就会指针j最终指向res_j

c++ 代码

class Solution {
public:
int maxArea(vector<int>& height) {
int i = 0, j = height.size() - 1;
int res = 0;
while (i < j) {
res = max(res, min(height[i], height[j]) * (j - i));
if (height[i] < height[j]) {
i += 1;
} else {
j -= 1;
}
}
return res;
}
};

python 代码

class Solution:
def maxArea(self, height: List[int]) -> int:
i, j = 0, len(height) - 1
res = 0
while i < j:
res = max(res, min(height[i], height[j]) * (j - i))
if height[i] < height[j]:
i += 1
else:
j -= 1
return res

折半查找做法

c++代码和注解:

/*
我们把 i 当做右边界,而且是较小的右边界,那么我们只需要找到左侧大于等于 height[i] 的靠左的部分即可
主要是根据贪心的原则 如果 height[j] >= height[j + 1] 那么 j + 1根本就没有什么用处了,因为 j 这个位置比他高,还比他宽
*/
typedef pair<int, int> PII;
class Solution {
public:
int getMax(vector<int> v) {
vector<PII> a;
a.push_back(PII(v[0], 0));
int l, r, mid;
int res = 0;
for (int i = 1; i < v.size(); i ++ ) {
if (a.back().first < v[i]) {
a.push_back(PII(v[i], i));
} else {
// 在 a 递增数组中寻找大于等于 v[i] 的最小值
l = 0, r = a.size() - 1;
while (l < r) {
mid = l + r >> 1;
if (a[mid].first >= v[i]) {
r = mid;
} else {
l = mid + 1;
}
}
res = max(res, v[i] * (i - a[l].second));
}
}
return res;
}
int maxArea(vector<int>& height) {
return max(getMax(height), getMax(vector<int> (height.rbegin(), height.rend())));
}
};

python 代码和注解

class Solution:
def getMax(self, h):
res = 0
a = []
for i, x in enumerate(h):
if i == 0 or a[-1][0] < x:
a.append([x, i])
else:
l, r = 0, len(a)
while l < r:
mid = (l + r) // 2
if a[mid][0] >= x:
r = mid
else:
l = mid + 1
res = max(res, x * (i - a[l][1]))
return res
def maxArea(self, height: List[int]) -> int:
return max(self.getMax(height), self.getMax(height[::-1]))

12. Integer to Roman

方法一,按位模拟

感觉这个问题直接按照他的规则进行模拟就好了,不过模拟的话建议使用循环进行模拟,因为这样的话修改起来代码也比较简单,
而且当 num = 1e10的时候,也不需要自己 if else 考虑每一个位

c++ 代码

/*
x = num / 1000 --> M 的个数
y = num % 1000 / 100 if y == 9: CM else if y >= 5: DC..C else if y == 4: CD else: C..C
z = num % 100 / 10 if z == 9: XC else if z >= 5: LX..X else if z == 4: XL else: X..X
w = num % 10 if w == 9: IX else if w >= 5: VI..I else if w == 4: IV else: I..I
*/
class Solution {
public:
string intToRoman(int num) {
string res = "", symble = " MDCLXVI";
int base = 1000;
int cur = 0, t;
while (num) {
t = num / base;
num %= base;
base /= 10;
if (t == 9) {
res += string("") + symble[cur + 1] + symble[cur - 1];
} else if (t >= 5) {
res += symble[cur];
for (int i = 6; i <= t; i ++ ) {
res += symble[cur + 1];
}
} else if (t == 4) {
res += string("") + symble[cur + 1] + symble[cur];
} else {
for (int i = 1; i <= t; i ++ ) {
res += symble[cur + 1];
}
}
cur += 2;
}
return res;
}
};

python 代码

class Solution:
def intToRoman(self, num: int) -> str:
symble, res = ' MDCLXVI', ''
base, cur = 1000, 0
while num != 0:
t = num // base
num %= base
base //= 10
if t == 9:
res += symble[cur + 1] + symble[cur - 1]
elif t >= 5:
res += symble[cur]
for i in range(5, t):
res += symble[cur + 1]
elif t == 4:
res += symble[cur + 1] + symble[cur]
else:
for i in range(1, t + 1):
res += symble[cur + 1]
cur += 2
return res

方法二 减法模拟

该写法就是,让数字 num,不断减去
1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1直到为 0
也算是一个模拟,不过具体的思路有所差异

python 代码

class Solution:
def intToRoman(self, nums: int) -> str:
nums_to_symbol = {
1000:'M', 900:'CM', 500:'D', 400:'CD', 100:'C',
90:'XC', 50:'L', 40:'XL', 10:'X',
9:'IX', 5:'V', 4:'IV', 1:'I'
}
res = ""
for x in nums_to_symbol:
res += nums // x * nums_to_symbol[x]
nums %= x
return res

13. Roman to Integer

这里直接加上一个字典,然后进行数字的相加
c++代码

class Solution {
public:
int romanToInt(string s) {
unordered_map<char, int> symbol_to_value;
symbol_to_value['I'] = 1;
symbol_to_value['V'] = 5;
symbol_to_value['X'] = 10;
symbol_to_value['L'] = 50;
symbol_to_value['C'] = 100;
symbol_to_value['D'] = 500;
symbol_to_value['M'] = 1000;
symbol_to_value[' '] = -10;
int n = s.size(), value1, value2, res = 0;
s = s + ' '; // 这样就不需要最后一个特判了
for (int i = 0; i < n; i ++ ) {
value1 = symbol_to_value[s[i]];
value2 = symbol_to_value[s[i + 1]];
if (value1 >= value2) {
res += value1;
} else {
res -= value1;
}
}
return res;
}
};

python 代码

class Solution:
def romanToInt(self, s: str) -> int:
symbol_to_value = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000}
n = len(s)
res = 0
for i, ch in enumerate(s):
value = symbol_to_value[s[i]]
value_2 = symbol_to_value[s[i + 1]] if i + 1 < n else -100
if value >= value_2:
res += value
else:
res -= value
return res

14. Longest Common Prefix

真真的水题,直接模拟就好

直接模拟

c++ 代码

class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
int ret = 0;
string s = "";
char ch;
while (true) {
if (strs[0].size() > ret) {
ch = strs[0][ret];
} else {
return s;
}
for (int i = 1; i < strs.size(); i ++ ) {
if (strs[i].size() < ret || strs[i][ret] != ch) {
return s;
}
}
s += ch;
ret += 1;
}
return s;
}
};

python 代码

class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
ret = 0
while True:
if ret < len(strs[0]):
ch = strs[0][ret]
else:
return strs[0]
for my_str in strs:
if ret >= len(my_str) or my_str[ret] != ch:
return strs[0][:ret]
ret += 1
return strs[0]

使用python ZIP + SET!!!

class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
ret = ""
for i in zip(*strs): # 将这个列表打散
if len(set(i)) == 1:
ret += i[0]
else:
return ret
return ret

15. 3Sum

双指针算法

双指针算法往往可以将 O(N ^ 2)的复杂度简化到 O(N), O(N ^ 3) 的算法简化到 O(N ^ 2),如何将其应用到该问题呢?

我们不妨想一想该问题的暴力解法。
第一步,我们先对原数组从小到大进行排序,主要是方便我们减少结果的冗余度,然后,不断枚举 i, j, k,查看 nums[i] + nums[j] + nums[k]是否等于 0,同时,这里我们给出了如何解决出现冗余重复的问题。

class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int> > res;
int n = nums.size();
for (int i = 0; i < n; i ++ ) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < n; j ++ ) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
for (int k = j + 1; k < n; k ++ ) {
if (k > j + 1 && nums[k] == nums[k - 1]) {
continue;
}
if (nums[i] + nums[j] + nums[k] == 0) {
res.push_back(vector<int> {nums[i], nums[j], nums[k]});
}
}
}
}
return res;
}
};

那么如何解决这个 i, j, k三个维度同时循环的问题呢?
我们可以 在 j, k 这个维度进行处理。首先将原来的等式 nums[i] + nums[j] + nums[k] == 0,转换为条件表达式:
nums[i] + nums[j] + nums[k] >= 0, 对于每个 i 来说,查看 j, k 这个指针的问题。
对于每一个 j ,我们查看满足条件的最小 k,
那么对于 下一个 j, 我们的 k 就可以在上一次 k 的基础上进行移动。
看似 j, k 是两个变量,但是他们已经变成了相互关联的变量。
复杂度 由原来的的 O(N^3)转换为了 O(N^2)

这也也挺像是 折半查找的办法,确定了 i, j,然后折半查找满足条件的最小 k

c++ 代码如下所示:

class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int> > res;
int n = nums.size();
for (int i = 0; i < n; i ++ ) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1, k = n - 1; j < k; j ++ ) { // 注意这个 j < k
// 对于每一个 j 而言,找到满足 nums[i] + nums[j] + nums[k] >= 0,k的最小值
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
while (k - 1 > j && nums[i] + nums[j] + nums[k - 1] >= 0)
k --;
if (nums[i] + nums[j] + nums[k] == 0) {
res.push_back(vector<int> {nums[i], nums[j], nums[k]});
}
}
}
return res;
}
};

python对应的代码

class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
if not nums or n < 3:
return []
res = []
nums.sort()
for i in range(n):
if nums[i] > 0:
break
if i > 0 and nums[i] == nums[i - 1]:
continue
k = n - 1
for j in range(i + 1, n):
if j >= k:
break
if j > i + 1 and nums[j] == nums[j - 1]:
continue
while k - 1 > j and nums[i] + nums[j] + nums[k - 1] >= 0:
k -= 1
if nums[i] + nums[j] + nums[k] == 0:
res.append([nums[i], nums[j], nums[k]])
return res

使用 Hash 存储 a[i] + a[j]

这次我们预先存储 a[i] + a[j] 的数值,存储他们对应的 i, j下标。
然后我们枚举k,进行查找a[i] + a[j] 是否满足条件即可。
但是速度稍慢 O(N2logN)

/*
本题是寻找 a[i] + a[j] + a[k] == 0 && i != j && i != k && j != k 的所有三元组
可以将 a[i] + a[j] 的所有结果存下来,然后反向 a[k] 查找 对应的 i,j
对于一些特判问题,可以考虑 [0, 0, 0, 0, 0]这种情况
*/
const int BASE = 10000;
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
// 首先应当对 nums 排序
sort(nums.begin(), nums.end());
// 这里加入特判去除冗余
int n = nums.size();
unordered_map<int, vector<int> > heap;
for (int i = 0; i < n; i ++ ) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < n; j ++ ) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
heap[nums[i] + nums[j]].push_back(i + j * BASE);
}
}
vector<vector<int> > res;
int i, j;
for (int k = 2; k < n; k ++ ) {
// 这个 k 从大往小选,因为我们 i, j是按照小的选,防止 k 和 j 冲突
if (k < n - 1 && nums[k] == nums[k + 1]) {
continue;
}
if (heap.count(-nums[k])) {
for (auto x : heap[-nums[k]]) {
i = x % BASE, j = x / BASE;
if (k > j) { // i < j < k
res.push_back(vector<int> {nums[i], nums[j], nums[k]});
}
}
}
}
return res;
}
};

python 就不这样写了,可以使用字典当做 hash,不过效率太低,可能过不了

16. 3Sum Closest

双指针算法

思路和 15 题的双指针算法一模一样

c++代码:

class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int res = 1e9, n = nums.size();
sort(nums.begin(), nums.end());
for (int i = 0; i < n; i ++ ) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int k = n - 1;
for (int j = i + 1; j < k; j ++ ) {
while (k - 1 > j && nums[i] + nums[j] + nums[k - 1] >= target) {
k -= 1;
}
if (abs(res - target) > abs(nums[i] + nums[j] + nums[k] - target)) {
res = nums[i] + nums[j] + nums[k];
}
if (k - 1 > j && abs(res - target) > abs(nums[i] + nums[j] + nums[k - 1] - target)) {
res = nums[i] + nums[j] + nums[k - 1];
}
}
}
return res;
}
};

python 代码:

class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
n = len(nums)
nums.sort()
res = 1e9
for i in range(n):
if i > 0 and nums[i] == nums[i - 1]:
continue
k = n - 1
for j in range(i + 1, n):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
if j >= k:
break
while k - 1 > j and nums[i] + nums[j] + nums[k - 1] >= target:
k -= 1
if abs(res - target) > abs(nums[i] + nums[j] + nums[k] - target):
res = nums[i] + nums[j] + nums[k]
if k - 1 > j and abs(res - target) > abs(nums[i] + nums[j] + nums[k - 1] - target):
res = nums[i] + nums[j] + nums[k - 1]
return res

17. Letter Combinations of a Phone Number

一个简单的 DFS 问题

class Solution {
public:
vector<string> digits_to_symbol = {
"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"
};
vector<string> res;
void dfs(string &s, int ptr, string path) {
if (ptr == s.size()) {
res.push_back(path);
return;
}
for (auto ch : digits_to_symbol[s[ptr]-'0']) {
dfs(s, ptr + 1, path + ch);
}
}
vector<string> letterCombinations(string digits) {
if (digits == "") {
return res;
}
dfs(digits, 0, "");
return res;
}
};

python

class Solution:
def letterCombinations(self, digits: str) -> List[str]:
# iterative
if not digits: return []
dic = {'2':'abc','3':'def','4':'ghi','5':'jkl','6':'mno','7':'pqrs','8':'tuv','9':'wxyz'}
res = [""]
for i in digits:
res = [r + j for r in res for j in dic[i]] # 他在原本 r 的基础上进行了叠加,相当于DFS进行下一层
print(res)
return res

注意这个 self.res = []清空,防止leetcode调用

class Solution:
res = []
dic = {'2':'abc','3':'def','4':'ghi','5':'jkl','6':'mno','7':'pqrs','8':'tuv','9':'wxyz'}
def dfs(self, digits, ptr, s):
if ptr == len(digits):
self.res.append(s)
return
for ch in self.dic[digits[ptr]]:
self.dfs(digits, ptr + 1, s + ch)
def letterCombinations(self, digits: str) -> List[str]:
self.res = []
if not digits:
return []
self.dfs(digits, 0, "")
return self.res

18. 4Sum

双指针算法

注意整数溢出,以及 c < d这个条件特判

// 双指针算法,优化到 O(N ^ 3)
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, long long target) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int> > res;
for (int a = 0; a < n; a ++ ) {
if (a && nums[a] == nums[a - 1])
continue;
for (int b = a + 1; b < n; b ++ ) {
if (b > a + 1 && nums[b] == nums[b - 1])
continue;
int d = n - 1;
for (int c = b + 1; c < d; c ++ ) { // 注意这个特判是 c < d
if (c > b + 1 && nums[c] == nums[c - 1])
continue;
while (d - 1 > c && (long long)(nums[a]) + nums[b] + nums[c] + nums[d - 1] >= target)
d -= 1;
if ((long long)(nums[a]) + nums[b] + nums[c] + nums[d] == target) {
res.push_back(vector<int> {nums[a], nums[b], nums[c], nums[d]});
}
}
}
}
return res;
}
};

python代码

class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
n = len(nums)
res = []
for a in range(n):
if a > 0 and nums[a] == nums[a - 1]:
continue
for b in range(a + 1, n):
if b > a + 1 and nums[b] == nums[b - 1]:
continue
d = n - 1
for c in range(b + 1, n):
if c >= d:
break
if c > b + 1 and nums[c] == nums[c - 1]:
continue
while d - 1 > c and nums[a] + nums[b] + nums[c] + nums[d - 1] >= target:
d -= 1
if nums[a] + nums[b] + nums[c] + nums[d] == target:
res.append([nums[a], nums[b], nums[c], nums[d]])
return res

19. Remove Nth Node From End of List

直接就是换一下指针就可以了

不适用头指针

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 *cur = head;
int sz = 0;
while (cur) {
cur = cur->next;
sz += 1;
}
int target = sz + 1 - n;
if (target == 1) {
return head->next;
} else {
cur = head;
for (int i = 1; i < target - 1; i ++ ) {
cur = cur->next;
}
cur->next = cur->next->next;
return head;
}
}
};

python代码

# 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: ListNode, n: int) -> ListNode:
sz = 0
cur = head
while cur is not None:
sz += 1
cur = cur.next
target = sz + 1 - n
cur = head
if target == 1:
return head.next
for i in range(1, target - 1):
cur = cur.next
cur.next = cur.next.next
return head

方便处理边界情况,加头结点

# 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: ListNode, n: int) -> ListNode:
sz = 0
cur = head
while cur is not None:
sz += 1
cur = cur.next
dummy = ListNode(val=-1, next=head)
target = sz + 1 - n
cur = dummy
for i in range(1, target):
cur = cur.next
cur.next = cur.next.next
return dummy.next

20. Valid Parentheses

使用栈的方式,来判断括号匹配是否合法
c++代码

class Solution {
public:
bool isValid(string s) {
vector<char> v;
int n = s.size();
for (int i = 0; i < n; i ++ ) {
if (s[i] == '(') {
v.push_back('(');
} else if (s[i] == ')') {
if (v.empty() || v.back() != '(') {
return false;
} else {
v.pop_back();
}
} else if (s[i] == '[') {
v.push_back('[');
} else if (s[i] == ']') {
if (v.empty() || v.back() != '[') {
return false;
} else {
v.pop_back();
}
} else if (s[i] == '{') {
v.push_back('{');
} else if (s[i] == '}') {
if (v.empty() || v.back() != '{') {
return false;
} else {
v.pop_back();
}
}
}
return v.size() == 0;
}
};

python代码

class Solution:
def isValid(self, s: str) -> bool:
my_stack = []
for ch in s:
if ch == '(':
my_stack.append('(')
elif ch == ')':
if len(my_stack) == 0 or my_stack[-1] != '(':
return False
my_stack.pop()
elif ch == '{':
my_stack.append('{')
elif ch == '}':
if len(my_stack) == 0 or my_stack[-1] != '{':
return False
my_stack.pop()
elif ch == '[':
my_stack.append('[')
elif ch == ']':
if len(my_stack) == 0 or my_stack[-1] != '[':
return False
my_stack.pop()
return len(my_stack) == 0
posted @   lucky_light  阅读(96)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示