15. 三数之和 - 力扣(LeetCode)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
vector<vector<int>>ans;
for(int i = 0; i < n; i ++ )
{
if(i > 0 && nums[i] == nums[i - 1]) continue;
int j = i + 1, k = n - 1;
while(j < k)
{
while(j > i + 1 && j < n && nums[j] == nums[j - 1]) j ++;
if(j >= k) break;
int sum = nums[i] + nums[j] + nums[k];
if(sum == 0)
{
ans.push_back({nums[i], nums[j], nums[k]});
j ++;
}
else if(sum > 0)
k --;
else if(sum < 0)
j ++;
}
}
return ans;
}
};
16. 最接近的三数之和 - 力扣(LeetCode)
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int cha = 1e9;
int ans;
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, k = n - 1;
while(j < k)
{
while(j > i + 1 && j <n && nums[j] == nums[j - 1]) j ++;
if(j >= k) break;
int sum = nums[i] + nums[j] + nums[k];
if(abs(sum - target) < cha)
{
ans = sum;
cha = abs(sum - target);
}
if(sum == target)
{
ans = target;
break;
}
else if(sum > target) k --;
else j ++;
}
}
return ans;
}
};
18. 四数之和 - 力扣(LeetCode)
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
vector<vector<int> > ans;
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;
int k = j + 1, p = n - 1;
while(k < p)
{
while(k > j + 1 && k < n && nums[k] == nums[k - 1]) k ++;
if(k >= p) break;
long long sum = (long long)nums[i] + nums[j] + nums[k] + nums[p];
if(sum == target)
{
ans.push_back({nums[i], nums[j], nums[k], nums[p]});
k ++;
}
else if(sum < target) k ++;
else p --;
}
}
}
return ans;
}
};
11. 盛最多水的容器 - 力扣(LeetCode)
如果一个指针的高度小于另一个指针,那么以这个指针作为边界就没有意义了,可以移动
class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0, r = height.size() - 1;
int ans = 0;
while(l < r)
{
ans = max(ans, min(height[l], height[r]) * (r - l));
if(height[l] < height[r]) l ++;
else r --;
}
return ans;
}
};
26. 删除有序数组中的重复项 - 力扣(LeetCode)
一个指针插入,一个指针遍历
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int n = nums.size();
if(n == 0) return 0;
int j = 0;
for(int i = 0; i < n; i++){
if(nums[j] != nums[i]){
nums[++j] = nums[i];
}
}
return j + 1;
}
};
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
return process(nums,1);
}
int process(vector<int>& nums,int k){
int idx = 0;
for(auto x : nums){
if(idx < k or nums[idx - k] != x){
nums[idx++] = x;
}
}
return idx;
}
};
45. 跳跃游戏 II - 力扣(LeetCode)
class Solution {
public:
int jump(vector<int>& nums) {
int s = 0, mp = 0, end = 0;
int n = nums.size() ;
for(int i = 0; i < n - 1; i ++ )//i为走到n之前的中间节点
{
mp = max(mp, i + nums[i]);//这是走到end之前能走到的最大值
if(i == end)//还不能走到结尾,这个时候需要跳跃
{
end = mp;//end为能走到的最大值
s ++;
}
}
return s;
}
};
88. 合并两个有序数组 题解 - 力扣(LeetCode)
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = nums1.size() - 1;
m--;
n--;
while (n >= 0) {
while (m >= 0 && nums1[m] > nums2[n]) {
swap(nums1[i--], nums1[m--]);
}
swap(nums1[i--], nums2[n--]);
}
}
345. 反转字符串中的元音字母 - 力扣(LeetCode)
class Solution {
public:
unordered_map<char, bool> mp;
string reverseVowels(string s) {
//双指针
for(auto c: "aeiouAEIOU") mp[c] = true;
int n = s.size();
int l = 0, r = n - 1;
while(l < r)
{
while(l < n && !mp[s[l]]) l ++;
while(r > 0 && !mp[s[r]]) r --;
if(l >= r) break;
swap(s[l], s[r]);
l ++, r --;
}
return s;
}
};
395. 至少有 K 个重复字符的最长子串 题解 - 力扣(LeetCode)
class Solution {
public:
int sum[26][10010];
int ans = 0;
int longestSubstring(string s, int k) {
//统计所有26个字符的前缀和
int n = s.size();
for(int i = 0; i < n; i ++ )
{
for(int j = 0; j < 26; j ++ )
{
sum[j][i + 1] = sum[j][i] + (s[i] == (char)('a' + j));
}
}
dfs(s, k, 1, n);
return ans;
}
void dfs(string s, int k, int l, int r)
{
if(l > r) return;
unordered_map<char, bool> mp;
bool flag = true;
for(int i = 0; i < 26; i ++ )
{
int cnt = sum[i][r] - sum[i][l - 1];
if(cnt > 0 && cnt < k)//这个字符不行
{
mp[i + 'a'] = true;
flag = false;
}
}
if(flag)
{
ans = max(ans, r - l + 1);
return;
}
int ll = l;
for(int i = l; i <= r; i ++ )
{
if(mp[s[i - 1]])
{
dfs(s, k, ll, i - 1);
ll = i + 1;
}
}
//cout << ll << " " << r << endl;
dfs(s, k, ll, r);
}
};
class Solution {
public:
int longestSubstring(string s, int k) {
int ans = 0;
int n = s.size();
string cs = s;
int cnt[26];
for(int p = 1; p <= 26; p ++ )
{
memset(cnt, 0, sizeof(cnt));
for(int i = 0, j = 0, tot = 0, sum = 0; i < n; i ++ )
{
int u = cs[i] - 'a';
cnt[u] ++;
if(cnt[u] == 1) tot ++;
if(cnt[u] == k) sum ++;
//当区间包含的转字符种类数量超过了限定p
while(tot > p)
{
int t = cs[j ++ ] - 'a';
cnt[t] --;
if(cnt[t] == 0) tot --;
if(cnt[t] == k - 1) sum --;
}
if(tot == sum) ans = max(ans, i - j + 1);
}
}
return ans;
}
};
413. 等差数列划分 - 力扣(LeetCode)
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& nums) {
vector<int> dp(nums.size() + 1);
int ans = 0;
for(int i = 2; i < nums.size(); i ++ )
{
if(nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2])
{
dp[i] = dp[i - 1] + 1;//找到有几个连续的三
ans += dp[i];//连续的可以累加
}
}
return ans;
}
};
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& nums) {
if(nums.size() < 3)
return 0;
int ans = 0;
for(int i = 0; i < nums.size() - 2; i ++ )
{
int start = i;
int end = i + 1;
int c = nums[end] - nums[start];
while(end + 1 < nums.size() && nums[end + 1] - nums[end] == c)
end ++;
int len = end - start + 1;
int a1 = 1, an = len - 3 + 1;
ans += (a1 + an) * an / 2;
i = end - 1;
}
return ans;
}
};
424. 替换后的最长重复字符 - 力扣(LeetCode)
给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
一个很关键的性质,当指定l
、r
指针以后,发现r
指针移动会使替换的数量增加,l
指针移动使替换的数量减小,这样就可以在保证最多有k个替换的情况下,移动指针,遍历所有情况。
class Solution {
public:
int characterReplacement(string s, int k) {
//选择A k次替换
int n = s.size();
int ans = 0;
for(int i = 0; i < 26; i ++ )
{
char c = 'A' + i;
int l = 0, r = 0;
int cnt = 0;
while(r < n)
{
while(r < n && s[r] == c) r ++;//找到所有的A
ans = max(ans, r - l);
if(r == n) break;//r不能再替换了
if(cnt < k)
{
cnt ++;//替换掉这一个
r ++;
ans = max(ans, r - l);
}
else //已经不能再替换了,那么就移动l指针直到找到第一个不是c也就是之前替换的
{
while(l < n && s[l] == c) l ++;
l ++;//跳过这一个
r ++;//r被替换
}
}
}
return ans;
}
};
class Solution {
public:
int characterReplacement(string s, int k) {
//记录当前窗口字母出现的个数
vector<int> counts(26, 0);
// maxCount记录字符出现次数最多那个字符的次数,res储存最大的窗口大小
int left = 0, res = 0, maxCount = 0;
for(int i = 0; i < s.size(); i ++) {
counts[s[i] - 'A'] ++;
// 比较之前记录的最大数 和 当前字符的数量
maxCount = max(maxCount, counts[s[i] - 'A']);
// 若当前窗口大小 减去 窗口中最多相同字符的个数 大于 k 时
while(i - left + 1 - maxCount > k){
// 将窗口最左边的字符 在计数数组中减1
counts[s[left] - 'A'] --;
left ++; // 滑动窗口
}
res = max(res, i - left + 1);
}
return res;
}
};
438. 找到字符串中所有字母异位词 - 力扣(LeetCode)
可以发现,固定长度的窗口,对左右进行更新,可以得到结果
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int sl = s.size(), pl = p.size();
if(sl < pl) return {};
vector<int> cnt(26);
vector<int> ans;
int dif = 0;//多少个不相同的字符
for(int i = 0; i < pl; i ++ )
{
cnt[s[i] - 'a'] ++;
cnt[p[i] - 'a'] --;
}
for(int i = 0; i < 26; i ++ )
{
if(cnt[i]) dif ++;
}
if(dif == 0) ans.push_back(0);
for(int i = 0; i < sl - pl; i ++ )
{
if(cnt[s[i] - 'a'] == 1)//删掉左边
dif --;
else if(cnt[s[i] - 'a'] == 0)
dif ++;
cnt[s[i] - 'a'] --;
int ne = s[i + pl] - 'a';
if(cnt[ne] == -1)
dif --;
else if(cnt[ne] == 0)
dif ++;
cnt[ne] ++;
if(!dif) ans.push_back(i + 1);
}
return ans;
}
};
443. 压缩字符串 - 力扣(LeetCode)
class Solution {
public:
int compress(vector<char>& chars) {
int l = 0, r = 1;
int n = chars.size();
int ans = 0;
while(l < n)
{
chars[ans ++] = chars[l];//需要加入字符
while(r < n && chars[r] == chars[r - 1]) r ++;
if(r - l > 1)
{
for(auto c: to_string(r - l))
chars[ans ++] = c;//加入数字
}
if(r >= n) break;
l = r;//接下来要加入r
r ++;
}
return ans;
}
};
581. 最短无序连续子数组 - 力扣(LeetCode)
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
if (is_sorted(nums.begin(), nums.end())) {
return 0;
}
vector<int> numsSorted(nums);
sort(numsSorted.begin(), numsSorted.end());
int left = 0;
while (nums[left] == numsSorted[left]) {
left++;
}
int right = nums.size() - 1;
while (nums[right] == numsSorted[right]) {
right--;
}
return right - left + 1;
}
};
611. 有效三角形的个数 - 力扣(LeetCode)
双指针是如何加速的
很容易想到的思路是固定两个边,去找第三条边,可以发现,固定前两条边,去找第三条边,第三条边的指针是随着第二条边的增长而增长的,这样就就不用每次从第二条边+1开始循环,而是在第一次循环时就到达了一个边界,然后第二条边增加,第三条边也不断增加。
class Solution {
public:
int triangleNumber(vector<int>& nums) {
// a + b > c 即可
sort(nums.begin(), nums.end());
int n = nums.size();
int ans = 0;
int cnt = 0;
while(cnt < n && !nums[cnt]) cnt ++;
for(int i = cnt; i < n; i ++ ) //最左边指针
{
int r = i + 1;
for(int j = i + 1; j < n; j ++ )
{
while(r < n && nums[i] + nums[j] > nums[r]) r ++;
//找到第一个不合适的
ans += r - j - 1;//j + 1 ---- r是合适的
}
}
return ans;
}
};
//还可以固定最后数,然后移动左右指针
class Solution {
public:
int triangleNumber(vector<int>& nums) {
const int n = nums.size();
std::sort(nums.begin(),nums.end());
int ans = 0;
for(int i = n - 1; i >= 2; i--){
int l = 0, r = i - 1;
while(l < r){
if(nums[l] + nums[r] > nums[i]){
ans += (r - l);
r--;
}
else{
l++;
}
}
}
return ans;
}
};
633. 平方数之和 - 力扣(LeetCode)
class Solution {
public:
bool judgeSquareSum(int c) {
//寻找a + b = c 的问题
//固定 c a 和 b 一定是小于等于sqrt(c)的
long l = 0, r = sqrt(c);
while(l <= r)
{
long res = l * l + r * r;
if(res == c) return true;
else if(res > c) r --;
else l ++;
}
return false;
}
};
786. 第 K 个最小的素数分数 - 力扣(LeetCode)
class Solution {
public:
double eps = 1e-8;
int a, b;
vector<int> kthSmallestPrimeFraction(vector<int>& arr, int k) {
//二分答案,如果前边恰好有k个分数比他小,
double l = eps, r = 1 - eps;
while(r - l >= eps)
{
double mid = (l + r) / 2;
int num = check(arr, mid);
cout << mid << " ";
if(num < k)
{
l = mid + eps;
}
else if(num > k)
{
r = mid - eps;
}
else break;
}
return {a, b};
}
int check(vector<int>& arr, double x)
{
int res = 0;
double c = 100.0;
for(int i = 0, j = 1; i < arr.size(); i ++ )
{
while(j < arr.size() && (double)arr[i] / (double)arr[j] > x) j ++;//找到第一个比x小的
res += arr.size() - j;
if(j == arr.size()) break;
double p = x - (double)arr[i] / (double)arr[j];
if(p < c)//差值小
{
a = arr[i], b = arr[j];
c = p;
}
}
return res;
}
};
930. 和相同的二元子数组 - 力扣(LeetCode)
class Solution {
public:
int numSubarraysWithSum(vector<int>& nums, int goal) {
int n = nums.size();
vector<int> sum(n + 1);
for(int i = 0; i < n; i ++ )
sum[i + 1] = sum[i] + nums[i];
int ans = 0;
int l = 0, r = 0;
for(int i = goal; i <= n; i ++ )
{
int res = sum[i] - goal;//目标值
if(res < 0) continue;
while(l < i && sum[l] < res) l ++;//找到第一个值
while(r < i && sum[r] <= res) r ++;//找到最后一个值,后边的
ans += r - l;
}
return ans;
}
};
992. K 个不同整数的子数组 - 力扣(LeetCode)
标准的双指针求方案数
class Solution {
public:
int subarraysWithKDistinct(vector<int>& nums, int k) {
//不同整数的个数
//l1 l2 ------r
int n = nums.size();
int l1 = 0, l2 = 0, r = 0;
int dif1 = 0, dif2 = 0;
unordered_map<int, int> mp1, mp2;
int ans = 0;
while(r < n)
{
mp1[nums[r]] ++;
if(mp1[nums[r]] == 1)
dif1 ++;//种类增多
while(l1 <= r && dif1 > k)//找到第一个是k的位置
{
mp1[nums[l1]] --;
if(!mp1[nums[l1]]) dif1 --;
l1 ++;//左指针右移
}
mp2[nums[r]] ++;
if(mp2[nums[r]] == 1)
dif2 ++;
while(l2 <= r && dif2 >= k) //找到最后一个是k的位置
{
mp2[nums[l2]] --;
if(!mp2[nums[l2]]) dif2 --;
l2 ++;
}
r ++;
ans += l2 - l1;
}
return ans;
}
};
1004. 最大连续1的个数 III - 力扣(LeetCode)
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
//最多包含k个0的区间长度
int dif = 0;
int n = nums.size();
int r = 0, l = 0;
int ans = 0;
while(r < n)
{
dif += (nums[r] == 0);
while(dif > k)
{
dif -= (nums[l] == 0);
l ++;
}
ans = max(ans, r - l + 1);
r ++;
}
return ans;
}
};
1743. 从相邻元素对还原数组 - 力扣(LeetCode)
class Solution {
public:
vector<int> restoreArray(vector<vector<int>>& ad) {
//n个不同元素
unordered_map<int, vector<int>> mp;
for(auto p : ad)
{
mp[p[0]].push_back(p[1]);
mp[p[1]].push_back(p[0]);
}
int n = ad.size() + 1;
vector<int> res(n);
for(auto [e, adj]: mp)
{
if(adj.size() == 1)
{
res[0] = e;//找到开头
break;
}
}
res[1] = mp[res[0]][0];//第一组为e---
for(int i = 2; i < n; i ++ )
{
auto adj = mp[res[i - 1]];
if(res[i - 2] == adj[0])
res[i] = adj[1];
else res[i] = adj[0];
}
return res;
}
};
1764. 通过连接另一个数组的子数组得到一个数组 - 力扣(LeetCode)
class Solution {
public:
bool canChoose(vector<vector<int>>& groups, vector<int>& nums) {
int n = nums.size();
int l = 0, r = 0;//滑动窗口
for(int i = 0; i < groups.size(); i ++ )
{
int m = groups[i].size();
int flag = false;
while(l < n)
{
if(nums[l] == groups[i][0])
{
r = l;
while(r < n && r - l < m && nums[r] == groups[i][r - l]) r ++;
if(r - l == m) flag = true;
}
if(flag) break;
l ++;
}
if(flag == false) return false;
l = r;
}
return true;
}
};