leetcode之双指针类-----OJ 228/15/16/18/26/80/121/75

本文深入探讨双指针算法的应用技巧,通过经典题目如区间查找、三数之和、股票买卖等,详解如何利用双指针高效解决问题,并提供详细的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表中使用快慢指针的题也都可以算作双指针,本文着重更容易被作为双指针典型考察的典型题;

 

1、OJ228 summary ranges

给定有序数组找出其中的区间,如给定[0,1,2,4,5,7],结果为:["0->2", "4->5", "7"]

本题看起来是数组类区间相关,但其实是双指针操作能力考察

思路:事实上是3个指针,一个是当前区间头指针st,然后是一个cur和next形成前后双指针,双指针的题一定注意边界处理:
1、next不能越界

2、前后不再自增时,记录st到cur的区间,然后更新st和cur都到next,next再自增

3、前后自增时,cur和next依次前移

4、next过界后,有两种漏加最后一组区间情况:4.1、最后一个区间是单个数,特征为st==cur且next-cur==1;4.2、最后一个区间是一个区间,特征为st!=cur;以上两种情况都要补充区间,这是本题双指针操作重点;

 

OJ228代码:

 

class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums) {
        std::vector<std::string> res;
        if (nums.empty()) {
            return res;
        }
        
        int st = 0;
        while (st < nums.size()) {
            std::string tmp = std::to_string(nums[st]);
            
            int cur = st, ed = st + 1;
            while (ed < nums.size() && nums[ed] - nums[cur] == 1) {
                cur = ed;
                ed += 1;
            }
            
            if (ed > st + 1) {
                tmp += "->";
                tmp += std::to_string(nums[ed - 1]);
            }
            
            res.push_back(tmp);
            st = ed;
        }
        
        return res;
    }
};


2、OJ15 3sum

 

给定一个数组,找到其中的3个数的组合和为0的全部3数组合;

典型双指针,而且是相遇型双指针,思路是:

1、排序

2、以每个数为基数,然后在它后边的范围内,双指针分别指向首和尾,寻找三数和为0的情况;注意如果基数已经大于0,说明后边的更大,这时要及时停止无谓计算

 

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        if (nums.empty() || nums.size() < 3) {
            return res;
        } else if (nums.size() == 3) {
            if (nums[0] + nums[1] + nums[2] == 0) {
                vector<int> r;
                r.push_back(nums[0]);
                r.push_back(nums[1]);
                r.push_back(nums[2]);
                res.push_back(r);
                return res;
            } else {
                return res;
            }
        }
        
        //用set是为了确保3个数是按题意要求的升序
        set<vector<int>> rset;
        sort(nums.begin(), nums.end());
        for (size_t i = 0; i < nums.size() - 2; i++) {
            int cur = nums[i];
            //当前数已经大于0, 都应该break了
            if (cur > 0) {
                break;
            }
            //前边已经算过了不要重复计算
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            size_t st = i + 1, ed = nums.size() - 1;
            
            while (st < ed) {
                int cursum = nums[st] + nums[ed] + cur;
                if (cursum == 0) {
                    //找到后记录下来, 然后接着找
                    vector<int> r;
                    r.push_back(cur);
                    r.push_back(nums[st]);
                    r.push_back(nums[ed]);
                    rset.insert(r);
                    ++st;
                    --ed;
                } else if (cursum > 0) {
                    //超过target(0)就减减小的
                    --ed;
                } else {
                    //小于target(0)就加加大的
                    ++st;
                }
            }
        }
        
        return vector<vector<int>>(rset.begin(), rset.end());
    }
};

 

 

3、OJ16 3sum closet

和3sum几乎一样。

注意最接近target的判断和更新,另外注意如果直接等于target是应该直接返回的,因为这个是最近的

OJ16代码:

 

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        int res, mindiff = INT_MAX;
        
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++) {
            int cur = nums[i];
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            
            int st = i + 1, ed = nums.size() - 1, curval = INT_MAX;
            while (st < ed) {
                //不断更新最接近的sum
                int sum = cur + nums[st] + nums[ed];
                if (abs(sum - target) < mindiff) {
                    mindiff = abs(sum - target);
                    res = sum;
                }
                
                //依然是少进多回, 需要注意当发现等于target时是最近的要直接返回
                if (sum > target) {
                    --ed;
                } else if (sum < target) {
                    ++st;
                } else {
                    return sum;
                }
            }
        }
        
        return res;
    }
};

 

 

 

 

 

 

4、OJ18 4sum

4sum以后统称Ksum,都可以简单的基于3sum的思路去做,只是往往不是最优解法(如4sum宜用二分思路优化),3sum的时间复杂度是O(N^2),基于3sum做法的4sum是O(N^3);

这里先只介绍下基于3sum的4sum可AC方法,道理和3sum一样;

OJ18代码:

 

class Solution {
public:   
    vector<vector<int>> helper (const vector<int> &relate, int target) {
        vector<vector<int>> res;
        for (int i = 0; i < relate.size(); i++) {
            int cur = relate[i];
            int st = i + 1, ed = relate.size() - 1;
            if (i > 0 && relate[i] == relate[i - 1]) {
                continue;
            }
            
            while (st < ed) {
                if (relate[st] + relate[ed] + cur == target) {
                    vector<int> s;
                    s.push_back(cur);
                    s.push_back(relate[st]);
                    s.push_back(relate[ed]);
                    res.push_back(s);
                    ++st;
                    --ed;
                } else if (relate[st] + relate[ed] + cur > target) {
                    --ed;
                } else {
                    ++st;
                }
            }
        }
        
        return res;
    }
    
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        set<vector<int>> res;
        if (nums.empty()) {
            vector<vector<int>> empty;
            return empty;
        }
        
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            
            vector<int> relate;
            for (int j = i + 1; j < nums.size(); j++) {
                relate.push_back(nums[j]);
            }
            vector<vector<int>> r = helper(relate, target - nums[i]);
            for (auto v: r) {
                v.push_back(nums[i]);
                sort(v.begin(), v.end());
                res.insert(v);
            }
        }
        
        vector<vector<int>> result(res.begin(), res.end());
        return result;
    }
};

 

 

 

5、OJ26/80 remove duplicate elements from sorted array I/II

 

 

典型双指针运用,原地修改数据

OJ80,允许重复的数据出现2次,外加hashmap记录次数的逻辑

OJ26代码:

 

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.empty()) {
            return 0;
        }
        
        int res = 1;
        for (int i = 1, j = 1; i < nums.size(); i++) {
            if (nums[i] != nums[i - 1]) {
                ++res;
                nums[j] = nums[i];
                ++j;
            }
        }
        
        return res;
    }
};

 

 

OJ80代码:

 

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.empty()) {
            return 0;
        }
        
        vector<int>::iterator it = nums.begin();
        unordered_map<int, int> hmap;
        for (int i = 0; i < nums.size(); i++) {
            if (hmap.find(nums[i]) == hmap.end()) {
                *it = nums[i];
                ++it;
                hmap[nums[i]] = 1;
            } else {
                if (hmap[nums[i]] == 1) {
                    hmap[nums[i]] = 2;
                    *it = nums[i];
                    ++it;
                }
            }
        }
        
        nums.erase(it, nums.end());
        return nums.size();
    }
};

 


6、OJ121 best time to buy and sell stock

股票买卖题1,典型双指针,一个用于更新最小值,一个用于更新利润值

OJ121代码:

 

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.empty()) {
            return 0;
        }
        
        int min = INT_MAX, val = INT_MIN;
        for (auto i: prices) {
            min = std::min(i, min);
            val = std::max(val, i - min);
        }
        
        return val;
    }
};


7、OJ75 sort color

 

三色排序也叫荷兰国旗,给定数组里边有乱序的最多0、1、2三种元素,需要把0放在左部分,1放在中间部分,2放在右部分;

典型三指针:

1、从头找到第一个不为0的地方st;

2、从尾找到第一个不问2的地方ed;

3、位移指针mid从st开始,如果为1则mid向右顺移,若为0则与st交换元素,且st自增mid也右移,注意如果为2除与ed交换元素ed左移外,mid不能右移,因为可能交换得到的元素是0,需要做下一轮循环中与st交换;

OJ75代码:

 

class Solution {
public:
    void sortColors(vector<int>& nums) {
        if (nums.empty()) {
            return;
        }
        
        //得到从头开始第一个不为0的地方st, 也是mid
        //得到从尾开始第一个不问2的地方ed
        int st = 0, mid = 0, ed = nums.size() - 1;
        while (st < nums.size() && nums[st] == 0) {
            ++st;
        }
        mid = st;
        while (ed >= 0 && nums[ed] == 2) {
            --ed;
        }
        
        //保证st <= mid <= ed
        while (st <= mid && mid <= ed) {
            if (nums[mid] == 0) {
                //mid与st交换后, 均自增, 因为如果mid处为2下一次循环就会被处理
                int t = nums[st];
                nums[st] = nums[mid];
                nums[mid] = t;
                ++st;
                ++mid;
            } else if (nums[mid] == 1) {
                //mid处为1, 正好, 自增mid
                ++mid;
            } else {
                //mid与ed交换后, mid不能自增, 因为mid处可能为0, 还要和st交换
                int t = nums[ed];
                nums[ed] = nums[mid];
                nums[mid] = t;
                --ed;
            }
        }
    }
};

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值