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 None 和 not stk有区别 not None --> True not [] --> True
11. Container With Most Water
解决该问题,主要有两种思路,
- 双指针算法
- 滑动区间内 + 双指针
双指针算法
双指针算法特别简单,具体操作如下:
我们使用指针 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] 是否满足条件即可。
但是速度稍慢
/* 本题是寻找 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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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)