双指针(尺取法)

本文介绍了双指针算法在解决IT问题中的应用,包括碰撞指针、滑动窗口和快慢指针的示例,如LeetCode中的两数之和、平方数之和和回文串问题。通过实例展示了如何在数组和链表操作中高效地使用双指针技巧来优化时间和空间复杂度。
摘要由CSDN通过智能技术生成

双指针(尺取法)

参考1 参考2

双指针顾名思义,同时使用两个指针维护或者统计一些区间信息的。特别是在数组、链表等数据结构中,双指针的算法能大大减少我们的编码速度以及时间复杂度。

双指针大体的应用分为以下三个大类:
碰撞指针滑动窗口快慢指针

碰撞指针

两个指针从头尾向中间移动。

模板:

while(l<r) {
	//具体问题逻辑
	//至少移动一个指针
}

Leetcode 两数之和 II - 输入有序数组
题意:在一个增序的整数数组里找到两个数,使它们的和为给定值。已知有且只有一对解。

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int l=0,r=numbers.size()-1;
        while(l<r) {
            int sum=numbers[l]+numbers[r];
            if(sum==target) return {l+1,r+1};
            else if(sum>target) r--;
            else l++;
        }
        return {-1,-1};
    }
};

Leetcode 633. 平方数之和
题意:给定一个非负整数 c c c ,你要判断是否存在两个整数 a a a b b b,使得 a 2 + b 2 = c a^2 + b^2 = c a2+b2=c

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

Leetcode 680. 验证回文串 II
题意:给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。

class Solution {
public:
    bool check(int l,int r,string s){
        while(l<r) {
            if(s[l]!=s[r]) return false;
            ++l;
            --r;
        }
        return true;
    }
    bool validPalindrome(string s) {
        int l=0,r=s.size()-1,cnt=0;
        while(l<r) {
            if(s[l]==s[r]) {
                ++l;
                --r;
            }
            else return check(l,r-1,s)||check(l+1,r,s);
        }
        return true;
    }
};

滑动窗口

两个指针从头同向移动。

模板:

for(int l=1,r=1;l<=n;++l){ //遍历左指针
	while(r<=n && 某条件) { //右指针向右扩展
		//具体问题逻辑
        //包含进右指针元素
        ++r;
    }
    //循环退出后,统计/维护的区间是[l,r)
    if(某条件) break;
    //具体问题逻辑...
    //剔除左指针元素...
}

维护区间和或积

POJ-3061 Subsequence
题意:给定长度为 N ( 10 < N < 1 0 5 ) N(10 < N <10^5) N(10<N<105) 的正整数数列 a 0 , a 1 , … … , a n − 1 a_0,a_1,……,a_{n-1} a0,a1,……,an1 以及正整数 S ( 0 < S < 1 0 8 ) S(0 < S < 10^8) S(0<S<108)。 求出总合不小于S的连续 子序列长度的最小值。如果解不存在,则输出0。
参考代码:

#include <iostream>
using namespace std;

#define int long long
const int N=1e5+5; 
int n,s,a[N],T; 

signed main(){
	scanf("%lld",&T);
	while(T--){
		scanf("%lld%lld",&n,&s);
		for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
		int sum=0,mi=n+1;
		for(int l=1,r=1;l<=n;++l){ 
			while(r<=n && sum<s) {
				sum+=a[r];
				r++;
			}
			//sum=[l,r)之间的值的和 
			if(sum<s) break; 
			mi=min(mi,r-l);
			sum-=a[l]; //尝试移动左指针一小步 
		}
		if(mi==n+1) printf("0\n");
		else printf("%lld\n",mi);
	}
	return 0;
}

Leetcode 76. 最小覆盖子串
题意:给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""
参考代码:

class Solution {
public:
    unordered_map<char,int> mp;
    string minWindow(string s, string t) {
        
        for(int i=0;i<t.size();++i) mp[t[i]]++;
        int cnt=mp.size();
        int ansi=-1,ansj=-1;
        for(int l=0,r=0;l<s.size();++l) {
            while(r<s.size() && cnt) {
                if(mp.find(s[r])!=mp.end()){
                    mp[s[r]]--;
                    if(mp[s[r]]==0) cnt--;
                }
                r++;
            }
            if(cnt) break;
            if(ansi==-1 || r-l<(ansj-ansi)) {
                ansi=l;
                ansj=r;
            }
            if(mp.find(s[l])!=mp.end()) {
                mp[s[l]]++;
                if(mp[s[l]]==1) cnt++;
            }
        }
        if(ansi==-1) return ""; 
        return s.substr(ansi,ansj-ansi);
    }
};

快慢指针

Leetcode 142. 环形链表 II
题意:给定一个链表,如果有环路,找出环路的开始点。

好题解

题解:给定两个指针,分别为fast和slow,从起始位置开始,fast每次前进两步,slow每次前进一步。如果fast为空或者next为空,则说明没有环路。否则fast会一直走下去,说明有环路,两者一定会相遇。当fast和slow相遇时,再将fast重新移动到head,并让fast和slow每次都前进一步,当两者再次相遇时,相遇节点即为环路开始点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast=head;
        ListNode *slow=head;
        do{
            if(fast==NULL) return fast;
            fast=fast->next;
            if(fast==NULL) return fast;
            fast=fast->next;
            if(fast==NULL) return fast;
            slow=slow->next;
        }while(fast!=slow);
        fast=head;
        while(fast!=slow){
            fast=fast->next;
            slow=slow->next;
        }
        return fast;
    }
};
  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值