LeetCode 1. 两数之和
使用哈希表记录每个遍历过的数和对应的位置,使用目标值减去当前遍历到的数值得到想要的值r,如果哈希表中已经存在这个值r,那么就返回r对应的下表和当前遍历到的值得下标。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hash;
for(int i = 0; i < nums.size(); i ++ ) {
int r = target - nums[i];
if(hash.count(r)) return {hash[r], i};
hash[nums[i]] = i;
}
return {};
}
};
LeetCode 2. 两数相加
链表模拟大数相加的过程,可以类比高精度加法的做法。
做法如下:
1.同时从头开始遍历两个链表的值,将两个值相加存进中间变量t,然后分别指向next节点,新链表的下个节点赋值为(t % 10),t /= 10进位。
2.如果遍历完两个链表后,t != 0 ,那么就再创建一个新节点并赋值为 t
/**
* 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
auto dummy = new ListNode(-1), cur = dummy;
int t = 0;
while(l1 || l2 || t){
if(l1) t += l1->val, l1 = l1->next;
if(l2) t += l2->val, l2 = l2->next;
cur = cur -> next = new ListNode(t % 10);
t /= 10;
}
return dummy -> next;
}
};
LeetCode 3. 无重复字符的最长子串
双指针算法。
使用一个哈希表维护当前找到的字符串区间 [i, j] 中字母的个数。
如果出现重复字符,那么指针 i 向后移动一位,并且哈希表中这个字符的出现次数减一,直到没有重复字符为止,使用了一个while循环实现
如果没有重复字符,那么指针 j 向后移动一位,同时哈希表中对应的字符出现次数加一
在这个过程中使用res记录字串长度的最大值。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> hash;
int res = 0;
for (int i = 0, j = 0; j < s.size(); j ++ )
{
hash[s[j]] ++ ;
while (hash[s[j]] > 1) hash[s[i ++ ]] -- ;
res = max(res, j - i + 1);
}
return res;
}
};
LeetCode 4. 寻找两个正序数组的中位数
寻找两个正序数组的中位数,可以将它转换成寻找两个正序数组中的第k小数,只要能将寻找第k小数的问题能解决,我们需要寻找的中位数就是第(n+m)/2小数。
- 当一共有偶数个数时,找到第
left = total / 2
小和第right = total / 2 + 1
小,结果是(left + right / 2.0)
- 当一共有奇数个数时,找到第
total / 2 + 1
小,即为结果
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int total = nums1.size() + nums2.size();
if (total % 2 == 0) {
int left = findKthNumber(nums1, 0, nums2, 0, total / 2);
int right = findKthNumber(nums1, 0, nums2, 0, total / 2 + 1);
return (left + right) / 2.0;
}
return findKthNumber(nums1, 0, nums2, 0, total / 2 + 1);
}
int findKthNumber(vector<int> &nums1, int i, vector<int> &nums2, int j, int k) {
if (nums1.size() - i > nums2.size() - j) return findKthNumber(nums2, j, nums1, i, k);
if (nums1.size() == i) return nums2[j + k - 1];
if (k == 1) return min(nums1[i], nums2[j]);
int si = min(i + k / 2, int(nums1.size())), sj = j + k / 2;
if (nums1[si - 1] > nums2[sj - 1])
return findKthNumber(nums1, i, nums2, j + k / 2, k - k / 2);
else
return findKthNumber(nums1, si, nums2, j, k - (si - i));
}
};
LeetCode 5. 最长回文子串
暴力枚举
如果回文串长度是奇数,则回文串中心为 i,依次判断 s[i−k]==s[i+k],k=1,2,3,…
如果回文串长度是偶数,则回文串中心为 i, i+1,依次判断 s[i−k]==s[i+k−1],k=1,2,3,…
如果遇到不同字符,则我们就找到了以 i 为中心的回文串边界。
class Solution {
public:
string longestPalindrome(string s) {
string res;
for(int i = 0; i < s.size(); i ++ ) {
//奇数情况
int l = i - 1, r = i + 1;
while(l >= 0 && r < s.size() && s[l] == s[r]) l -- , r ++ ;
if(res.size() < r - l - 1) res = s.substr(l + 1, r - l - 1);
//偶数情况
l = i, r = i + 1;
while(l >= 0 && r < s.size() && s[l] == s[r]) l -- , r ++ ;
if(res.size() < r - l - 1) res = s.substr(l + 1, r - l - 1);
}
return res;
}
};
LeetCode 6. Z 字形变换
在行数等于3时,将字符串"PAYPALISHIRING"按”N”字形排列,如下所示:
P A H N
A P L S I I G
Y I R
即:
0 4 8
1 3 5 7 ..
2 6
而当行数是4的情况如下:
0 6 12
1 5 7 11 ..
2 4 8 10
3 9
可以发现这道题其实是个找数学规律的问题
从上面的例子中我们可以发现,对于行数是 n 的情况:
· 第一行和最后一行的是两个公差为 2(n−1)的等差数列,首项分别为0和n-1
·对于第 i (0<i<n−1),是两个公差为 2(n−1) 的等差数列交替排列,首项分别是 i 和 2n−i−2
按照这样的规律写代码即可
class Solution {
public:
string convert(string s, int n) {
string res;
if(n == 1) return res;
for(int i = 0; i < n; i ++ ) {
if(i == 0 || i == n - 1) {
for(int j = i; j < s.size(); j += 2 * n - 2)
res += s[j];
} else {
for(int j = i, k = 2 * n - 2 - i; j < s.size() || k < s.size(); j += 2 * n - 2, k += 2 * n - 2) {
if(j < s.size()) res += s[j];
if(k < s.size()) res += s[k];
}
}
}
return res;
}
};
LeetCode 7. 整数反转
class Solution {
public:
int reverse(int x) {
long long r = 0;
while(x)
{
//后面的条件为判断x是否溢出,如果溢出则返回0
if(r > 0 && r > (INT_MAX - x % 10) / 10) return 0;
if(r < 0 && r < (INT_MIN - x % 10) / 10) return 0;
r = r * 10 + x % 10;
x /= 10;
}
return r;
}
};
LeetCode 8. 字符串转换整数 (atoi)
按照题目要求的做法模拟即可
class Solution {
public:
int myAtoi(string str) {
//计算空格数量
int k = 0;
while(k < str.size() && str[k] == ' ') k ++ ;
if(k == str.size()) return 0;
//记录数值的正负符号
int minus = 1;
if(str[k] == '-') minus = -1, k ++ ;
else if(str[k] == '+') k ++ ;
int res = 0;
while(k < str.size() && str[k] >= '0' && str[k] <= '9') {
int x = str[k] - '0';
//判断数值是否溢出
if(minus > 0 && res > (INT_MAX - x) / 10) return INT_MAX;
if(minus < 0 && -res < (INT_MIN + x) / 10) return INT_MIN;
if(-res * 10 - x == INT_MIN) return INT_MIN;
res = res * 10 + x;
k ++ ;
if(res > INT_MAX) break;
}
//乘上符号位
res *= minus;
if(res > INT_MAX) res = INT_MAX;
if(res < INT_MIN) res = INT_MIN;
return res;
}
};
LeetCode 9. 回文数
使用字符串操作的函数进行判断即可
class Solution {
public:
bool isPalindrome(int x) {
if(x < 0) return 0;
string s = to_string(x);
return s == string(s.rbegin(), s.rend());
}
};
LeetCode 10. 正则表达式匹配
设状态 f(i,j) 表示字符串 s 的前 i 个字符和字符串 p 的前 j 个字符能否匹配。这里假设 s 和 p 的下标均从 1 开始。初始时,f(0,0)=true。
转移 f(i,j)=f(i,j) or f(i−1,j−1)
,当 i>0
且 s(i) == p(j) || p(j) == '.'
。
当 p(j) == ’ * ’ 时,若 j>=2,f(i,j)可以从 f(i,j−2)) 转移,表示丢弃这一次的 ’ * ’ 和它之前的那个字符;若 i>0 且 s(i) == p(j - 1),表示这个字符可以利用这个 ’ * ',则可以从 f(i−1,j)转移,表示利用 ’ * '。
初始状态 f(0,0)=true;循环枚举 i 从 0 到 n;j 从 1 到 m。因为 f(0,j) 有可能是有意义的,需要被转移更新。
最终答案为 f(n,m)。
引用
作者:wzc1995
链接:https://www.acwing.com/solution/content/557/
来源:AcWing
class Solution {
public:
bool isMatch(string s, string p) {
int n = s.size(), m = p.size();
vector<vector<bool>> f(n + 1, vector<bool>(m + 1, false));
s = ' ' + s, p = ' ' + p;
f[0][0] = true;
for(int i = 0; i <= n; i ++ )
for(int j = 1; j <= m; j ++ ) {
if(i > 0 && (s[i] == p[j] || p[j] == '.'))
f[i][j] = f[i][j] | f[i - 1][j - 1];
if(p[j] == '*') {
if(j >= 2)
f[i][j] = f[i][j] | f[i][j - 2];
if(i > 0 && (s[i] == p[j - 1] || p[j - 1] == '.'))
f[i][j] = f[i][j] | f[i - 1][j];
}
}
return f[n][m];
}
};