力扣刷题---双指针之 对撞指针

本文介绍了双指针算法的概念,包括对撞指针和快慢指针的使用场景,以及在解决区间问题、字符串操作(如回文串检查)、几何问题(如盛最多水的容器)和数组搜索(如搜索二维矩阵和三数之和)中的应用。通过具体例题展示了双指针优化算法效率的方法。
摘要由CSDN通过智能技术生成

基础知识:

双指针简介:

双指针(Two Pointers):指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。如果两个指针方向相反,则称为「对撞指针」。如果两个指针方向相同,则称为「快慢指针」。如果两个指针分别属于不同的数组 / 链表,则称为「分离双指针」。

在数组的区间问题上,暴力算法的时间复杂度往往是0\left (n^{2} \right )。而双指针利用了区间「单调性」的性质,可以将时间复杂度降到 0\left (n \right )

 对撞指针适用范围+部分例题 

对撞指针一般用来解决有序数组或者字符串问题:

  • 查找有序数组中满足某些约束条件的一组元素问题:比如二分查找、数字之和等问题。
  • 字符串反转问题:反转字符串、回文数、颠倒二进制等问题。

 例题:

125. 验证回文串

 这道题理解了 对撞指针就很好想出一个思路--两头直接开始比较(另一个也可以用栈)。但本体还有一个比较讨厌的地方---字符串格式转换

class Solution {
public:
    bool isPalindrome(string s) {
        if(s.size()<=1) return true;
        
        string sive;
        for(char &c:s){
            if(isalnum(c)) sive+=tolower(c);
        }
        int  left=0;int right=sive.size()-1;
        while(left<right){
            if(sive[left]==sive[right]){
                left++;right--;
            }
            else 
                return false;
        }
        return true;
    }
};

字符串部分代码解释:

isalnum(c)//c是否为数字或者字母。
isdigit()//数字?
isalpha()//字母?
tolower(c);//c无论大小写同意变换为小写
s.size();//string 求长度用

11. 盛最多水的容器

思路:本题要求找到底乘高的面积最大值。其中,高是两个杆子之中最短的那个,底是两个杆子位置差。假设选中i,j两个杆子:高h=min(hight[i],hight[j]); 底d=j-i。

那么我们常规方法得到i,j 需要两层for循环遍历,但可根据双指针方法,减少遍历次数。

核心思想:
在每个状态下,无论长板或短板向中间收窄一格,都会导致水槽 底边宽度 −1​ 变短:
若向内 移动短板 ,水槽的短板 min(h[i],h[j]) 可能变大,因此下个水槽的面积 可能增大 。
若向内 移动长板 ,水槽的短板 min(h[i],h[j])不变或变小,因此下个水槽的面积 一定变小 。
因此,初始化双指针分列水槽左右两端,循环每轮将短板向内移动一格,
并更新面积最大值,直到两指针相遇时跳出;即可获得最大面积。

可以参考题解:11. 盛最多水的容器 - 力扣(LeetCode) 

代码实现: 

class Solution {
public:
    int maxArea(vector<int>& height) {
        int i=0;int j=height.size()-1;
        int ans=0;
        while(i<j){
            int h=min(height[i],height[j]);int d=j-i;
            ans=max(ans,h*d);
            if(h==height[i]) i++;
            else j--; 
        }
        return ans; 
    }
};

240. 搜索二维矩阵 II

矩阵查找值,优先考虑双指针+缩减区域。

大体思路:本题需要搜索全部区域(为什么从右上角?左下角出发-----可以参考链接),所以跳出循环的条件是到头(行 :0—m-1,列:n-1—0)

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m=matrix.size();int n=matrix[0].size();
        int row=0;int col=n-1;int num;
        while(row<=m-1&&col>=0){
            num=matrix[row][col];
            if(num>target) col--;
            else if(num<target) row++;
            else return true;

        }
        return false;
    }
};

881. 救生艇 :(和上一篇文章    力扣基础刷题---二分查找-CSDN博客里面的167题思路一样 )

class Solution {//每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit
public:
    int numRescueBoats(vector<int>& people, int limit) {
        sort(people.begin(),people.end());
        int ans=0;//存组合数
        int i=0;int j=people.size()-1;
        
        while(i<j){
            int start=people[i];int end=people[j];
            if(start+end<=limit){
                ans++;i++;j--;//数据只能用一次
            }
            else if(start+end>limit){
                ans++;j--;
            }
        }
        if(i==j) ans++;
        return ans;
    }
};

 611. 有效三角形的个数

从 nums 中选三个数,满足 1≤a≤b≤c 且 a+b>c   的方案数。

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());//为了能够使用相向双指针,先对数组从小到大排序。
        int ans=0;//存组合数
        for(int c=2;c<nums.size();c++){//外层循环枚举 =nums[c]
            int a=0;int b=c-1;
            while(a<b){
                if(nums[a]+nums[b]>nums[c]){
                    ans+=b-a;//若果a+b>c 那么a+n+b>c必然成立.这其中一共有b-a个数字
                    b--;//和大了就减小
                } 
                else  a++;//和小了就增大
            }
        }
        return ans;
    }
};

15. 三数之和

读题目发现和上面的题目十分相似,于是写出主要部分。

class Solution { //和三角形题目很像
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        if (nums.size() < 3)
            return ans;
        sort(nums.begin(), nums.end());

        for (int k = 2; k < nums.size() ; k++) {
            int i = 0, j =k-1;
            if (nums[i] > 0)
                break;
            // 如果当前数字大于0,则三数之和一定大于0,所以结束循环--->直接到return
           //或者可以将break换位return ans;
            while (i < j) {
                cout<<nums[i]<<nums[j]<<nums[k]<<endl;
                if (nums[i] + nums[j] + nums[k] == 0) {
                    ans.push_back({ nums[i], nums[j],nums[k]});
                    i++;
                    j--;

                } //为保持平衡需要一变大,另一变小
                else if (nums[i] + nums[j] + nums[k] > 0) {
                    j--;
                } //和大了,减少

                else {
                    i++;
                } //和小了,增大
            }
        }
        return ans;
    }
};

但是本题需要去重,太难了

去重 核心代码://if前面的条件判断也很重要--->防止越界。

i++;
if(i>0&&nums[i]==nums[i-1]) i++;
//i++之后,和上一个相比。不能和下一个相比,否则会漏掉这个(一下子i+2)

j--;
if(j<n&&nums[j]==nums[j+1])j--;
class Solution {//和三角形题目很像
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        if(nums.size()<3) return ans;
        sort(nums.begin(),nums.end());
        
        for(int k=0;k<nums.size()-2;k++){
            int i=k+1,j=nums.size()-1;
            if(nums[k] > 0) break;
            // 如果当前数字大于0,则三数之和一定大于0,所以结束循环--->直接到return
            if(k>0&&nums[k]==nums[k-1]) continue;//---》到for循环;
            while(i<j){
                
                if(nums[i] + nums[j] + nums[k] == 0){
                    ans.push_back({nums[k],nums[i],nums[j]});
                    i++;j--;
                    while (i<j && nums[i] == nums[i-1]) i++; // 去重
                    while (i<j && nums[j] == nums[j+1]) j--; // 去重
                    
                }
                else if(nums[i] + nums[j] + nums[k] > 0){
                    j--;
                    while (i<j && nums[j] == nums[j+1]) j--;
                }//和大了,减少
                    
                else{i++;
                    while (i<j && nums[i] == nums[i-1]) i++;
                } //和小了,增大
                     
            }
        }
        return ans;
    }
};

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值