所有题目来源:力扣(LeetCode)
链接:
文章目录
- 1. 两数之和
- 2. 两数相加
- 3. 无重复字符的最长子串
- 4. 寻找两个有序数组的中位数
- 5. 最长回文子串
- 10. 正则表达式匹配
- 11. 盛最多水的容器
- 15. 三数之和
- 17. 电话号码的字母组合
- 19. 删除链表的倒数第N个节点
- 20. 有效的括号
- 21. 合并两个有序链表
- 22. 括号生成
- 23. 合并K个排序链表
- 31. 下一个排列
- 32. 最长有效括号
- 33. 搜索旋转排序数组
- 34. 在排序数组中查找元素的第一个和最后一个位置
- 49. 字母异位词分组
- 53. 最大子序和
- 55. 跳跃游戏
- 56. 合并区间
- 64. 最小路径和
- 70. 爬楼梯
- 72. 编辑距离
- 75. 颜色分类
- 121. 买卖股票的最佳时机
- 78. 子集
- 79. 单词搜索
- 94. 二叉树的中序遍历
- 96. 不同的二叉搜索树
- 98. 验证二叉搜索树
- 101. 对称二叉树
- 102. 二叉树的层次遍历
- 104. 二叉树的最大深度
- 136. 只出现一次的数字
1. 两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
-
解法1:暴力,双层循环,时间复杂度O(n2),空间复杂度O(1)
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { vector<int> ans; for(int i = 0;i<nums.size();i++){ for(int j = i+1;j<nums.size();j++){ if(nums.at(i) + nums.at(j) == target) { ans.push_back(i); ans.push_back(j); } } } return ans; } };
-
解法2:利用map是红黑树的特性,将nums中的以[值,下标]的形式存储在一个map中,再逐个检查nums所需的元素(target-nums)是否存在于map中,若存在且并非自身,即找到。
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { map<int,int> mNum; vector<int> vAns; for(int i = 0;i < nums.size();i++) mNum[nums.at(i)] = i; for(int i = 0;i < nums.size();i++){ int temp = target - nums.at(i); if(mNum.count(temp) && mNum[temp] != i){ vAns.push_back(i); vAns.push_back(mNum[temp]); return vAns; } } return vAns; } };
-
解法3:利用unordered_map的地层是哈希表的特性,将nums中的以[值,下标]的形式存储在一个unordered_map中,再逐个检查nums所需的元素(target-nums)是否存在于unordered_map中,若存在且并非自身,即找到。(unordered_map使用的哈希表对于哈希冲突的处理是不允许冲突,从这个角度来说,unordered_map和map在使用和表现上没有任何区别,但是事实上哈希的时间会比红黑树更快,前者是O(1),后者是O(lgn))
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int,int> mNum; vector<int> vAns; for(int i = 0;i < nums.size();i++) mNum[nums.at(i)] = i; for(int i = 0;i < nums.size();i++){ int temp = target - nums.at(i); if(mNum.count(temp) && mNum[temp] != i){ vAns.push_back(i); vAns.push_back(mNum[temp]); return vAns; } } return vAns; } };
2. 两数相加
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
题解:顺序相加,注意进位。还有一种方式是自动为不等长的链表补齐长度计算。
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
if (l1 == NULL)
return l2;
if (l2 == NULL)
return l1;
int jinwei = 0;
ListNode* head=NULL, * cur=NULL;
while (l1 != NULL && l2 != NULL) {
int num = (l1->val + l2->val + jinwei) % 10;
ListNode* temp = new ListNode(num);
jinwei = (l1->val + l2->val + jinwei) / 10;
if (head == NULL) {
head = temp;
cur = temp;
}
else {
cur->next = temp;
cur = temp;
}
l1 = l1->next;
l2 = l2->next;
}
if (l1 != NULL||l2 !=NULL) {
ListNode* unFinished;
if (l1 != NULL)
unFinished = l1;
else
unFinished = l2;
while (unFinished != NULL && jinwei) {
int num = (unFinished->val + jinwei) % 10;
ListNode* temp = new ListNode(num);
jinwei = (unFinished->val + jinwei) / 10;
cur->next = temp;
cur = temp;
unFinished = unFinished->next;
}
if (unFinished == NULL && jinwei) {
ListNode* temp = new ListNode(1);
cur->next = temp;
}
else
cur->next = unFinished;
}
else {
if (jinwei == 1) {
ListNode* temp = new ListNode(1);
cur->next = temp;
}
}
return head;
}
3. 无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
解法:利用滑动窗口进行求解。基本思路为如果[i,j)为无重复子串,则考虑 [i,j]是否为无重复子串,如果j与[i,j)中的J重复了,则将窗口滑动至J+1的位置,每次窗口滑动都记录是否为最大值,判断是否为重复可以采用哈希表或是红黑树。
```cpp
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> mUnRepeated;
int iMax = 0, iLeft = 0, iCurrentMax = 0;
for (auto i = 0; i < s.size(); i++) {
if (mUnRepeated.count(s.at(i)) && mUnRepeated[s.at(i)] >= iLeft) {
iLeft = mUnRepeated[s.at(i)] + 1;
}
iMax = max(iMax, i - iLeft + 1);
mUnRepeated[s.at(i)] = i;
}
return iMax;
}
```
4. 寻找两个有序数组的中位数
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
在不考虑时间复杂度的情况下的解法(见笑):
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int iSize = nums1.size() + nums2.size();
double iAns = 0;
bool isOdd = false;
if (iSize % 2 == 0)
isOdd = false;
else
isOdd = true;
if (isOdd) {
int targetIndex = iSize / 2 + 1, iIndex = 0, iIndex1 = 0, iIndex2 = 0;
while (1){
iIndex++;
if (iIndex == targetIndex) {
if (iIndex1 == nums1.size()) {
iAns = nums2.at(iIndex2);
break;
}
if (iIndex2 == nums2.size()) {
iAns = nums1.at(iIndex1);
break;
}
if (nums1.at(iIndex1) < nums2.at(iIndex2)) {
iAns = nums1.at(iIndex1);
break;
}
else{
iAns = nums2.at(iIndex2);
break;
}
}
else {
if (iIndex1 == nums1.size()) {
iIndex2++;
continue;
}
if (iIndex2 == nums2.size()) {
iIndex1++;
continue;
}
if (nums1.at(iIndex1) < nums2.at(iIndex2))
iIndex1++;
else
iIndex2++;
}
}
}
else {
int targetIndex = iSize / 2, iIndex = 0, iIndex1 = 0, iIndex2 = 0;
while (1){
iIndex++;
if (iIndex == targetIndex) {
if (iIndex1 == nums1.size()) {
iAns = nums2.at(iIndex2)+ nums2.at(iIndex2 + 1);
iAns /= 2;
break;
}
if (iIndex2 == nums2.size()) {
iAns = nums1.at(iIndex1) + nums1.at(iIndex1 + 1);
iAns /= 2;
break;
}
if (nums1.at(iIndex1) < nums2.at(iIndex2)) {
iAns += nums1.at(iIndex1);
iIndex1++;
}
else {
iAns += nums2.at(iIndex2);
iIndex2++;
}
if (iIndex1 == nums1.size()) {
iAns += nums2.at(iIndex2 );
iAns /= 2;
break;
}
if (iIndex2 == nums2.size()) {
iAns += nums1.at(iIndex1 );
iAns /= 2;
break;
}
if (nums1.at(iIndex1) < nums2.at(iIndex2 )) {
iAns += nums1.at(iIndex1);
}
else {
iAns += nums2.at(iIndex2);
}
iAns /= 2;
break;
}
else {
if (iIndex1 == nums1.size()) {
iIndex2++;
continue;
}
if (iIndex2 == nums2.size()) {
iIndex1++;
continue;
}
if (nums1.at(iIndex1) < nums2.at(iIndex2))
iIndex1++;
else
iIndex2++;
}
}
}
return iAns;
}
5. 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
string longestPalindrome(string s) {
int iCurrent = 0, iLeft = 0, iRight = 0, iMax = 0, iPos = 0;
string ans;
for (; iCurrent < s.size(); iCurrent++) {
int iSLeft = 0, iSRight = 0;
iSLeft = iSRight = iLeft = iRight = iCurrent;
if (iRight < s.size() && iLeft >= 0){
while (iRight < s.size() && iLeft >= 0 && s.at(iRight) == s.at(iCurrent)) {
iSRight = iRight;
iRight++;
}
while (iRight < s.size() && iLeft >= 0 && s.at(iLeft) == s.at(iCurrent)) {
iSLeft = iLeft;
iLeft--;
}
while (iRight < s.size() && iLeft >= 0 && s.at(iLeft) == s.at(iRight)) {
iSLeft = iLeft;
iSRight = iRight;
iLeft--;
iRight++;
}
if (iSRight - iSLeft + 1> iMax) {
iMax = iSRight - iSLeft + 1;
iPos = iSLeft;
}
}
}
return s.substr(iPos, iMax);
}
10. 正则表达式匹配
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 1:
输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。
示例 2:
输入:
s = “aa”
p = “a*”
输出: true
解释: 因为 ‘*’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。
示例 3:
输入:
s = “ab”
p = “."
输出: true
解释: ".” 表示可匹配零个或多个(’*’)任意字符(’.’)。
示例 4:
输入:
s = “aab”
p = “cab”
输出: true
解释: 因为 ‘*’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。
示例 5:
输入:
s = “mississippi”
p = “misisp*.”
输出: false
分析:动态规划问题,因为有最优子结构(复杂的串可由前一状态推导)。分析可以知道有以下几种情况:
(1)p[j]==s[i]
此时相当于只要两者的前序正确则本项就正确,即dp[i][j]=dp[i-1][j-1]
(2)p[j]=='.'
,
是相当于任何一个字符,所以和上边的情况是完全一致的。dp[i][j]=dp[i-1][j-1]
(3)p[j]=='*'
''可以表示一个或者多个前一位的字符,这意味着他必须和前一位捆绑分析。有以下几种情况:
1. p[j-1]!=s[i]
相当于是cover了0次s[i]
.dp[i][j] = dp[i][j-2]
2. p[j-1]==s[i] || p[j-1]=='.'
此时的情况有以下几种
(1) *
cover了多次s[i]
此时dp[i][j]=dp[i-1][j]
(不考虑cover了几次,逐渐向前推进)
(2) *
cover了一次 's[i],此时
dp[i][j]=dp[i][j-1](相当于是和没有
是一样的) (3) '*' cover了0次
s[i],此时
dp[i][j]=dp[i][j-2](相当于是两个符号都没用,这种情况可能出现在类似
aa*`这样的组合当中)
bool isMatch(string s, string p) {
vector<vector<bool>> dp(s.size() + 1, vector<bool>(p.size() + 1, false));
dp[0][0] = true;
for (int i = 0; i <= s.size(); i++) {
for (int j = 1; j <= p.size(); j++) {
if (p[j-1] == '*') {
dp[i][j] = dp[i][j - 2] || (i && dp[i - 1][j] && (s[i - 1] == p[j - 2] || p[j - 2] == '.'));
}
else {
dp[i][j] = i && dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.');
}
}
}
return dp[s.size()][p.size()];
}
11. 盛最多水的容器
给定 n 个非负整数 a1,a2,…,an,
每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
解法1:暴力法。通过双重循环逐项取值,计算面积值并进行比对,保留最大面积。时间复杂度O(n2)。事实上会超时。
int maxArea(vector<int>& height) {
int iMaxArea = 0;
for (auto i = 0; i < height.size(); i++) {
for (auto j = i + 1; j < height.size(); j++) {
int iHeight = min(height.at(i), height.at(j));
if (iHeight*(j - i) > iMaxArea)
iMaxArea = iHeight * (j - i);
}
}
return iMaxArea;
}
解法2:双指针法。这种解法利用的是本题的特性,即在左右两边中,决定水桶盛水量的是较短的一边。因此,可以考虑如下情况,左右两边左高右低,此时要将一边向内移动,只能选择短的一边,因为即使长的变得更长,也只能带来面积的减少。时间复杂度O(n)。
int maxArea(vector<int>& height) {
int iMaxArea = 0, iLeft = 0, iRight = height.<