专栏——LeetCode
11. 盛最多水的容器
题目描述
给你 n 个非负整数 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,每个数代表坐标中的一个点 ( i , a i ) (i, a_i) (i,ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 ( i , a i ) 和 ( i , 0 ) (i, a_i) 和 (i, 0) (i,ai)和(i,0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明: 你不能倾斜容器,且 n 的值至少为 2。
示例:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
题解:
(指针扫描)O(n)
在初始时,左右指针分别指向数组的左右两端,此时我们需要移动一个指针。移动哪一个呢?
直觉告诉我们,应该移动对应数字较小的那个指针(即此时的左指针)。这是因为,由于容纳的水量是由两个指针指向的数字中较小值*指针之间的距离
决定的。如果我们移动数字较大的那个指针,那么「两个指针指向的数字中较小值」不会增加,「指针之间的距离」会减小,那么这个乘积会减小。
因此,我们移动数字较大的那个指针是不合理的。因此,我们移动 数字较小的那个指针。
然后不断计算两个指针指向的数字中较小值*指针之间的距离,去max即可
c++版
class Solution {
public:
int maxArea(vector<int>& height) {
int ans = 0;
for(int i = 0, j = height.size() - 1; i != j;){
ans = max(ans, min(height[i], height[j]) * (j - i));
if(height[i] > height[j])j--;
else i++;
}
return ans;
}
};
python版
class Solution:
def maxArea(self, height: List[int]) -> int:
ans = 0
i, j = 0, len(height) - 1
while(i != j):
ans = max(ans, min(height[i], height[j]) * (j - i))
if(height[i] > height[j]):
j -= 1
else:
i += 1
return ans;
12. 整数转罗马数字
题目描述
罗马数字包含以下七种字符: I, V, X, L,C,D 和 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 到 3999 的范围内。
示例 1:
输入: 3
输出: "III"
示例 2:
输入: 4
输出: "IV"
示例 3:
输入: 9
输出: "IX"
示例 4:
输入: 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.
示例 5:
输入: 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.
题解:
我们需要先了解罗马数字的计数方法:
基本字符 I V X L C D M
阿拉伯数字 1 5 10 50 100 500 1000
1.相同的数字连写,所表示的数等于这些数字相加得到的数,如:III=3;
2.小的数字在大的数字的右边,所表示的数等于这些数字相加得到的数,如:VIII=8, XII=12;
3.小的数字在大的数字的左边(限于 IV、IX、XL、XC、CD和CM),所表示的数等于大数减小数得到的数,如:IV=4, IX=9;
4.正常使用时,连写的数字重复不得超过三次;
我们可以将所有减法操作看做一个整体,当成一种新的单位。从大到小整理所有单位得到:
M CM D CD C XC L XL X IX V IV I
1000 900 500 400 100 90 50 40 10 9 5 4 1
此时我们可以将目标整数看成这些单位值的加和,且同一种单位不能使用超过3次。
所以我们尽可能优先使用值较大的单位即可。
时间复杂度分析:计算量与最终罗马数字的长度成正比,对于每一位阿拉伯数字,罗马数字最多用4个字母表示(比如VIII=8),
所以罗马数字的长度和阿拉伯数字的长度是一个数量级的,而阿拉伯数字的长度是 O(logn),因此时间复杂度是 O(logn)。
c++版
class Solution {
public:
string intToRoman(int num) {
int value[] = {
1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
string reps[] = {
"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
string ans = "";
for(int i = 0; i < 13; i++){
while(num >= value[i]){
num -= value[i];
ans += reps[i];
}
}
return ans;
}
};
python版
class Solution:
def intToRoman(self, num: int) -> str:
value = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
reps = ["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
ans = ""
for i in range(13):
while num >= value[i]:
num -= value[i]
ans += reps[i]
return ans
13. 罗马数字转整数
题目描述
罗马数字包含以下七种字符: I, V, X, L,C,D 和 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 到 3999 的范围内。
示例 1:
输入: "III"
输出: 3
示例 2:
输入: "IV"
输出: 4
示例 3:
输入: "IX"
输出: 9
示例 4:
输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
题解:
1.定义映射,将单一字母映射到数字。
2.从前往后扫描,如果发现 s[i+1] 的数字比 s[i] 的字大,那么累计 s[i+1]-s[i] 差值即可,
并将 i 多向后移动一位;否则直接累计 s[i] 的值。
时间复杂度
仅遍历一次整个字符串,故时间复杂度为 O(n)。
c++版
class Solution {
public:
int romanToInt(string s) {
unordered_map<char, int> mp;
mp['I'] = 1,mp['V'] = 5,mp['X'] = 10;
mp['L'] = 50,mp['C'] = 100,mp['D'] = 500;
mp['M'] = 1000;
int ans = 0;
int n = s.size();
for(int i = 0; i < n; i++){
if(i != n - 1 && mp[s[i + 1]] > mp[s[i]]){
ans += (mp[s[i + 1]] - mp[s[i]]);
i++;
}
else
ans += mp[s[i]];
}
return ans;
}
};
python版
class Solution:
def romanToInt(self, s: str) -> int:
mp = {
}
mp['I'], mp['V'], mp['X'] = 1, 5, 10
mp['L'], mp['C'], mp['D'] = 50, 100, 500
mp['M'] = 1000
ans, i = 0, 0
n = len(s)
while(i < n):
if i != n - 1 and mp[s[i + 1]] > mp[s[i]]:
ans += (mp[s[i + 1]] - mp[s[i]])
i += 1
else:
ans += mp[s[i]]
i += 1
return ans
14. 最长公共前缀
题目描述
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入: ["flower","flow","flight"]
输出: "fl"
示例 2:
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z 。
算法:
(暴力枚举) O(nm)
1.暴力枚举方法很简单:首先得到第一个字符串的长度 m,然后从长度 1 到 m 依次枚举判断是否所有字符串的前缀是否都相等。
2.注意输入可能为空数组。
c++版
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
string ans = "";
if(strs.size() == 0) return ans;
int n = strs[0].size();
if(n == 0) return ans;
for(int i = 0; i < n; i++){
for(auto str : strs)
if(str.size() <= i || str[i] != strs[0][i])
return ans;
ans += strs[0][i];
}
return ans;
}
};
python版
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
ans = ""
if len(strs) == 0:
return ans
n = len(strs[0])
if(n == 0):
return ans
for i in range(n):
for str in strs:
if len(str) <= i or str[i] != strs[0][i]:
return ans
ans += strs[0][i]
return ans
15. 三数之和
题目描述
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
题解:
排序 + 双指针 O ( n 2 ) O(n^2) O(n2)
先把数组从小到大排序
确定三数中的第一个数a[i],然后用两个指针,分别指向这个数后面的序列的头和尾。
由于已经排好序了,如果和比0大就把右指针向左移动,如果和比0小就把左指针向右移动一位
注意:碰到重复元素需要跳过,避免重复计数
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; i++){
if(i && nums[i] == nums[i - 1])continue;
for(int j = i + 1, k = n - 1; j < k; j++){
if(j > i + 1 && nums[j] == nums[j - 1])continue;
while(j < k - 1 && nums[i] + nums[j] + nums[k] > 0) k--;
if(nums[i] + nums[j] + nums[k] == 0)
ans.push_back({
nums[i], nums[j], nums[k]});
}
}
return ans;
}
};
python版
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
l = []
n = len(nums)
for i in range(n):
if i 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
while j < k and nums[i] + nums[j] + nums[k] > 0:
k -= 1
if j == k:
break
if nums[i] + nums[j] + nums[k] == 0:
l.append([nums[i], nums[j], nums[k]])
return l
16. 最接近的三数之和
题目描述
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
- 3 <= nums.length <= 10^3
- -10^3 <= nums[i] <= 10^3
- -10^4 <= target <= 10^4
题解:
排序 + 双指针
本题目因为要计算三个数,如果靠暴力枚举的话时间复杂度会到 O(n^3)需要降低时间复杂度
首先进行数组排序,时间复杂度 O(nlogn)
在数组 nums 中,进行遍历,每遍历一个值利用其下标i,形成一个固定值 nums[i]
再使用前指针指向 start = i + 1 处,后指针指向 end = nums.length - 1 处,也就是结尾处
根据 sum = nums[i] + nums[start] + nums[end] 的结果,判断 sum 与目标 target 的距离,如果更近则更新结果 ans
同时判断 sum 与 target 的大小关系,因为数组有序,如果 sum > target 则 end–,
如果 sum < target 则 start++,如果 sum == target 则说明距离为 0 直接返回结果
整个遍历过程,固定值为 n 次,双指针为 n 次,时间复杂度为 O(n^2)
总时间复杂度:O(nlogn) + O(n^2) = O(n^2)
c++版
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int ans = 1e9;
sort(nums.begin(),nums.end());
int n = nums.size();
for(int i = 0; i < n; i++){
if(i != 0 && nums[i] == nums[i - 1]) //去掉重复元素
continue;
int j = i + 1;
int k = n - 1;
while(j < k){
int sum = nums[i] + nums[j] + nums[k];
if(sum == target)
return sum;
if(abs(sum - target) < abs(ans - target))
ans = sum;
if(sum > target)k--;
if(sum < target)j++;
}
}
return ans;
}
};
python版
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
ans = 1e4
nums.sort()
n = len(nums)
for i in range(n):
if i != 0 and nums[i] == nums[i - 1]:
continue
j = i + 1
k = n - 1
while j < k:
num = nums[i] + nums[j] + nums[k]
if num == target:
return num
if abs(num - target) < abs(ans - target):
ans = num
if num > target:
k -= 1
if num < target:
j += 1
return ans
17. 电话号码的字母组合
题目描述
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
题解
(递归) O ( 4 l ) O(4^l) O(4l)
可以通过手工或者循环的方式预处理每个数字可以代表哪些字母。
通过递归尝试拼接一个新字母。
递归到目标长度,将当前字母串加入到答案中。
注意,有可能数字串是空串,需要特判。
c++版
class Solution {
public:
string mp[10] = {
"","","abc","def",
"ghi","jkl","mno",
"pqrs","tuv","wxyz"};
vector<string> ans;
vector<string> letterCombinations(string digits) {
if(digits.empty()) return ans;
dfs(digits, 0, "");
return ans;
}
void dfs(string digits, int u, string path){
if(u == digits.size()) ans.push_back(path);
else
for(auto c : mp[digits[u] - '0'])
dfs(digits, u + 1, path + c);
}
};
python版
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
mp = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
ans = []
def dfs(strs, u, path):
if u == len(strs):
ans.append(path)
return
index = int(strs[u])
for c in mp[index]:
dfs(strs,u + 1, path + c)
if len(digits) == 0:
return ans
dfs(digits, 0, "")
return ans
18. 四数之和
题目描述
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
题解
排序+双指针
跟15题的解题思路一样,只不过这里要多循环一层
c++版
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
int n = nums.size();
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;
int u = n - 1;
while(k < u){
int num = nums[i] + nums[j] + nums[k] + nums[u];
if(num > target){
while(k < u && nums[u] == nums[u - 1])u--;//去掉重复元素
u--;
}
else if(num < target) {
while(k < u && nums[k] == nums[k + 1])k++;//去掉重复元素
k++;
}
else{
ans.push_back({
nums[i],nums[j],nums[k],nums[u]});
while(k < u && nums[u] == nums[u - 1])u--;//去掉重复元素
while(k < u && nums[k] == nums[k + 1])k++;//去掉重复元素
k++;
u--;
}
}
}
}
return ans;
}
};
python版
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
ans = []
nums.sort()
n = len(nums)
for i in range(n - 3):
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i + 1, n - 2):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
k = j + 1
u = n - 1
while(k < u):
num = nums[i] + nums[j] + nums[k] + nums[u]
if num > target:
while(k < u and nums[u] == nums[u - 1]):
u -= 1
u -= 1
elif num < target:
while(k < u and nums[k] == nums[k + 1]):
k += 1
k += 1
else:
ans.append([nums[i],nums[j],nums[k],nums[u]])
while(k < u and nums[k] == nums[k + 1]):
k += 1
while(k < u and nums[u] == nums[u - 1]):
u -= 1
k += 1
u -= 1
return ans
19. 删除链表的倒数第N个节点
题目描述
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
题解
第一种做法:首先循环一遍得到长度L,知道长度后再循环一次得到答案
第二种做法:我们可以使用两个指针而不是一个指针。第一个指针从列表的开头向前移动 n+1 步,而第二个指针将从列表的开头出发。
现在,这两个指针被 n 个结点分开。我们通过同时移动两个指针向前来保持这个恒定的间隔,直到第一个指针到达最后一个结点。
此时第二个指针将指向从最后一个结点数起的第 n 个结点。我们重新链接第二个指针所引用的结点的 next 指针指向该结点的下下个结点。
c++版
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* a = new ListNode(-1);
a->next = head;
ListNode* p = a;
ListNode* q = a;
for(int i = 0; i <= n; i++)
p = p->next;
while(p != NULL){
q = q->next;
p = p->next;
}
q->next = q->next->next;
return a->next;
}
};
python版
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
a = ListNode(-1)
a.next = head
p = a
q = a
for i in range(0, n + 1):
p = p.next
while(p):
p = p.next
q = q.next
q.next = q.next.next
return a.next
20. 有效的括号
题目描述
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
题解:
栈的应用,遍历字符串,如果当前字符为括号的左半边’(’, ‘[’, ‘{‘就加入到队列中去;
如果当前字符为括号的右半边’)’, ‘]’, ‘}’,则判断是否与栈中的字符匹配,如果匹配的话就出栈。
最后判断栈是否为空即可
c++版
class Solution {
public:
bool isValid(string s) {
char a[10010];
int t = 0;
for(int i = 0; i < s.size(); i++){
if( t > 0 && s[i] == '}' && a[t] == '{')
t--;
else if( t > 0 && s[i] == ']' && a[t] == '[')
t--;
else if( t > 0 && s[i] == ')' && a[t] == '(')
t--;
else a[++t] = s[i];
}
if(t == 0) return true;
else return false;
}
};
python版
class Solution:
def isValid(self, s: str) -> bool:
stack = [""]
t = 0
for c in s:
if t > 0 and c == ')' and stack[t] == '(':
t -= 1
stack.pop()
elif t > 0 and c == '}' and stack[t] == '{':
t -= 1
stack.pop()
elif t > 0 and c == ']' and stack[t] == '[':
t -= 1
stack.pop()
else:
t += 1
stack.append(c)
return True if t == 0 else False
转载:https://blog.csdn.net/qq_43328040/article/details/107187190