双指针——leetcode

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 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
一个很关键的性质,当指定lr指针以后,发现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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值