leetcode打卡day01----双指针

本文深入解析双指针算法的应用场景与实现技巧,涵盖有序数组的两数之和、两数平方和、反转字符串中的元音字符、回文字符串验证、链表环检测及连续正数序列求和等问题。

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

1.有序数组的两数之和

leetcode 167
在这里插入图片描述

解题思路

这道题和力扣第一题无序的两数之和不同,因为是有序的,所以可以考虑利用双指针,
双指针思想:左指针指向最小的数值,从头向尾遍历,右指针指向最大的数值,从尾向头遍历。

  • 如果target==sum,则返回结果
  • 如果target>sum;则要将sum变大,移动左指针;
  • 如果target<sum,则要将sum变小,移动右指针; 代码如下:
class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
       vector<int>ans;
       int l=0,r=numbers.size()-1;
       while(l<r){
           if(numbers[l]+numbers[r]==target){
               ans.push_back(l+1);
               ans.push_back(r+1);
           }
           if(numbers[l]+numbers[r]<target){
               l++;
           }else{
               r--;
           }
       }
       return ans;
    }
};

2.两数平方和

leetcode633:两数平方和
在这里插入图片描述

解题思路:方法和上面一样,确定左右边界即可,左边界为0,有边界为根号target;注意这题要利用long long 类型,否则会溢出。

class Solution {
public:
    bool judgeSquareSum(int c) {
        if(c==0) return true;
        long long  l=0,r=sqrt(c);
        while(l<=r){
            long long sum=l*l+r*r;
            if(sum==c){
                return true;
            }else if(sum>c){
                r--;
            }else{
                l++;
            }
        }
        return false;
    }
};

3.反转字符串中的元音字符

leetcode345
在这里插入图片描述

解题思路:这题先要遍历一边字符串,把所有的元音字符存在一个容器中,可以是哈希表或是vector中,然后利用左右指针交换元音字符,注意元音字符包括大写的。

class Solution {
public:
    string reverseVowels(string s) {
        vector<int>str;
        string ans=s;
        for(int i=0;i<s.size();i++){
            if(s[i]=='a'||s[i]=='e'||s[i]=='i'||s[i]=='o'||s[i]=='u'||s[i]=='A'||s[i]=='E'||s[i]=='I'||s[i]=='O'||s[i]=='U'){
                str.push_back(i);
            }
        }
        int l=0,r=str.size()-1;
        while(l<=r){
           ans[str[l]]=s[str[r]];
           ans[str[r]]=s[str[l]];
           l++;
           r--;
        }
        return ans;
    }

};

优化方法:上面需要利用额外的空间,可以一边遍历一遍交换,当头尾指针都遇到元音时交换两个字符。可以将全部元音字符存到一个字符串中
注意:npos可以表示string的结束位子,是string::type_size 类型的,也就是find()返回的类型。find函数在找不到指定值得情况下会返回string::npos。

class Solution {
public:
    string reverseVowels(string s) {
        string v = "aeiouAEIOU";
        int i = -1,j = s.size();
        while(i<j){
            while(i<j && v.find(s[++i])==v.npos);
            while(i<j && v.find(s[--j])==v.npos);
            swap(s[i],s[j]);
        }
        return s;
    }
};

作者:SnowD
链接:https://leetcode-cn.com/problems/reverse-vowels-of-a-string/solution/shuang-zhi-zhen-jian-ji-dai-ma-by-snow

4.回文字符串

leetcode 680
在这里插入图片描述

解题思路:本题的关键是处理删除一个字符。在使用双指针遍历字符串时,如果出现两个指针指向的字符不相等的情况,我们就试着删除一个字符,再判断删除完之后的字符串是否是回文字符串。在试着删除字符时,我们既可以删除左指针指向的字符,也可以删除右指针指向的字符。

class Solution {
public:
//构造函数判断删除后的字符是否是回文字符串
    bool isPalindrome(string s,int i,int j){
        while(i<j){
            if(s[i++]!=s[j--]){
                return false;
            }
        }
        return true;
    }
    bool validPalindrome(string s) {
        for(int i=0,j=s.size()-1;i<j;i++,j--){
            if(s[i]!=s[j]){
                //删除左指针或者右指针后在判断
                return isPalindrome(s,i,j-1)||isPalindrome(s,i+1,j);
            }
        }
        return true;
       
    }
};

5.判断链表是否存在环

leetcode141
在这里插入图片描述

解题思路:设置双指针,一个快指针,一个慢指针,慢指针每次移动一个节点,快指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。 其中利用哈希表unordered_set也可以unordered_set.count()返回值为0或1,存在x,返回1,反之,返回0

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head==NULL) return false;
        //双指针,都从头节点开始,一个快指针一次走两步,一个慢指针走一步,如果存在环,则一定会重合。
        ListNode *p1=head,*p2=head;
        while(p2!=NULL&&p2->next!=NULL){
            p1=p1->next;
            p2=p2->next->next;
             if(p1==p2){
                return true;
            }
        }
        return false;  
    }
};
//利用哈希表存
unordered_set<ListNode*>us;
        while(head){
            if(us.count(head)){
                return true;
            }
            us.insert(head);
            head=head->next;
        }
        return false;

链表中环的入口节点

题目描述:

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

解题思路:

设置双指针,一个慢指针,一个快指针,慢指针一次移动一步,快指针一次移动两步,如果链表中存在环,则一定会相遇,当第一次相遇后,将快指针设置为头节点,然后快指针和慢指针每次移动一步,则下次相遇一定是环的入口节点;

代码实现

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        if(pHead==nullptr||pHead->next==nullptr) return nullptr;
        ListNode *p1=pHead;
        ListNode *p2=pHead;
        //快指针和快指针->next不为空;
        while(p2&&p2->next){
            p1=p1->next;
            p2=p2->next->next;
            if(p1==p2) break;
        }
        //if(!p2||!p2->next) return nullptr;
        p2=pHead;
        while(p2!=p1){
            p2=p2->next;
            p1=p1->next;
        }
        return p1;
    }
};

剑指offer–和为S的连续正序序列

题目描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列?输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

解题思路

解法一:利用三层暴力循环的,第一层左边界,第二层为右边界,第三层从左到右遍历计算和,如果相等则加入结果中。
解法二:利用左右指针,滑动窗口法,i表示左边界,j表示右边界
1.初始化,i=1,j=1, 表示窗口大小为0
2.如果窗口中值的和小于目标值sum, 表示需要扩大窗口,j += 1
3.否则,如果窗口值和大于目标值sum,表示需要缩小窗口,i += 1
4.否则,等于目标值,存结果,缩小窗口,继续进行步骤2,3,4.

代码实现

//三层暴力解法:时间复杂度为O(N^3),空间复杂度为O(1)
class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int>>res;
        for(int i=1;i<=sum/2;i++){
            for(int j=i+1;j<sum;j++){
                int tmp=0;
                for(int k=i;k<=j;k++){
                    tmp+=k;
                }
                if(sum==tmp){
                    vector<int>ans;
                    for(int k=i;k<=j;k++){
                        ans.push_back(k);
                    }
                    res.push_back(ans);
                }else if(tmp>sum){
                    break;
                }
            }
        }
        return res;
    }
};
//解法二:滑动窗口法,时间复杂度为O(n),空间复杂度为O(1)
class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int >>res;
        int tmp=0;
        int l=1,r=1;
        while(l<=sum/2){
            if(tmp<sum){
                tmp+=r;
                r++;
            }else if(tmp>sum){
                tmp-=l;
                l++;
            }else{
                vector<int>ans;
                for(int i=l;i<r;i++){
                    ans.push_back(i);
                }
                tmp-=l;
                l++;
                res.push_back(ans);
            }
            
        }
        return res;
    }
};

剑指offer—和为S的两个数字

题目描述

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

解题思路

利用双指针,一个指向数组头部,一个指向数组尾部,计算两个指针指向的数字之和,如果小于S则左指针向右移,如果大于S则右指针向左移。由于第一个满足体条件一定是乘积最小的,故找到满足条件的break即可。

代码实现

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int >res;
        int r=array.size()-1;
        if(r<0||sum<=array[0]) return res;
        int l=0,tmp=0;
        while(l<=r){
            tmp=array[l]+array[r];
            if(tmp<sum){
                l++;
            }else if(tmp>sum){
                r--;
            }else{
                res.push_back(array[l]);
                res.push_back(array[r]);
                break;
            }
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值