尺取法(指针移动)

什么是尺取法?对一个序列进行扫描其区间。

模板:这里是写成伪代码的形式

int a[n];
sort(a , a + n);//看情况判断是否需要排序
//while循环
int i = 0;
int j = n - 1;
wile(i < j)
{
    ...;//用途
    i++;//头
    j--;//尾
}
//for循环
for(int i = 0,j = n  - 1;i < j;i++ , j--)
{
    ...;//用途
}

尺取法有两种扫描途径:

1.反向扫描:

找一个n数列中两个元素的和 = c

输入:n  c

         a[n] = {...}

输出:两个数分别是多少

(1)暴力 O(n^2) ,time limit exceed

(2)二分查找:先排序,随后遍历整个数组,锁定一个a[i],然后二分查找m - a[i]

(3)尺取法:跟二分法的时间复杂度是一样的,但是代码比较简洁且容易思考

假设n = 9

a[n] = {21,4,5,6,13,65,32,9,23}

为了便于扫描加和,这里咱们不妨将其排序,利用C++的sort函数

随后按照模板设置两个指针,分别指向头和尾不断地向中间移动,如果sum > c就使j--,来保证加和的数变小,这也是为什么咱们要排序,如果sum < c就使i++.当 a[i] + a[j] = c就 cout 输出并让 i++ 来继续查找,要是没有写 i++ 就会重复输出以至于死循环一直输入当前查找的数。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000;
int a[N];
//查找数组内两个数的和=c的数
int main()
{
	int n , c;
	cin >> n >> c;
	for(int i = 0;i < n;i++)  cin >> a[i];
	sort(a , a + n);
	int i = 0,j = n - 1;
	while(i < j)
	{
		int sum = a[i] + a[j];
		if(sum > c)  j--;
		if(sum < c)  i++;
		if(sum == c)
		{
			cout << i << ' ' << j << endl;
			i++;
		}
	}
}

判断回文串:
输入:2

         level

         abcda

输出:YES

           NO

这道题就很经典了,这道题咱们在前面讲述动态规划讲到很多子序列问题 ,这里咱们利用尺取法解题,定义i,j两个指针分别指向头和尾不断地缩小区间,如果指针指向的两个地址的元素不相等,那么就直接返回false。

这里咱们是利用了C++的字符串类型,便于指针的遍历。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000;
int main()
{
	int n;
	cin >> n;
	while(n--)
	{
		string a;
		cin >> a;
        bool ans = true;
		int i = 0,j = a.size() - 1;
		while(i < j)
		{
			if(a[i] != a[j])
			{
				ans = false;
				break;
			}
			i++;
			j--;
		}
        if(ans)
		    cout << "YES" << endl;
        else
            cout << "NO" << endl;
	}
    return 0;
}

2.同向扫描:

同向扫描实际就是类似滑动窗口一样来扫描整个数组每个区间段

寻找区间和:

给定一个数组,请找出这个数组一段区间和=c的区间

输入:15(n = 15)

           6 1 2 3 4 6 4 2 8 9 10 11 12 13 14

           6(c = 6)

输出:0 0

           1 3

           5 5

           6 7

(1)因为要区间和并且这里要输出其区间,所以显而易见这里并不需要排序。

(2)另外这里要取其区间和,咱们可以从a[i]+...+a[j],但是其运算量可想而知,所以对于区间和,咱们应该联想到的是前缀和的知识点(如果不了解前缀和可以看我的算法专栏,里面有解释前缀和与差分的使用,这里不过多的解释了),所以我们可以在指针移动的同时加减其前位置,以此达到计算区间和。

(3)然后对于指针的运动,这里取同向指针是因为便于做前缀和的计算,咱们也可以去反向扫描,但是咱们就需要求出前缀和数组来进行计算。如果sum = c就输出其区间,如果sum > c那么就使 i++ 并让 sum -= a[i]. 如果 sum < c 我们可以让 j++ 并让 sum += a[j] ,注意这里 i 与 j 在让 sum加和的值,这里咱们看写出代码的先后顺序加以分析

(4)同向扫描要注意不可以让 i 的位置超过 j ,保证i <= j 。

#include iostream>
using namespace std;
const int N = 1000;
int a[N];
int main()//伸缩区间
{
	int n,c;
	cin >> n >> c;
	for(int i = 0;i < n;i++)
	    cin >> a[i];
	int i = 0,j = 0;
	int sum = a[0];
	while(i < j)
	{
		if(sum >= c)
		{
		    if(sum == c)
		        cout << i << ' ' << j << endl;
	        sum -= a[i];//前缀和思想
	        i++;	//注意顺序
	        if(i > j)//同向扫描要注意i不可以超过j
	        {
				j++;//注意顺序
				sum += a[j];
			}
		}
	    else{
			j++;
			sum += a[j];
		}
	}
}

这里讲述的是两个指针

特殊情况:

如果在数组中存在两个相同的数

作差:

A - B = C

输入:  6  3

          8 4 5 7 7 4

首先这道题同样是遍历数组,首先将其排序,然后这里要注意排序后结果为4 4 5 7 7 8

如果按照两个指针同向扫描,那么4 4 7 7 这里会出现四种情况,且随着指针移动会错过两种情况,那么这个时候就需要定义一个指针k记录[j , k)区间内为相同元素。

#include <bits/stdc++.h>
using namespace std;
int a[N];
int main()
{
	int n,c;
	cin >> n >> c;
	int res = 0;
	for(int i = 1;i <= n;i++)  cin >> a[i];
	sort(a + 1, a + n + 1);
	for(int i = 1,j = 1,k = 1;i <= n;i++)
	{
		while(j <= n && a[j] - a[i] < c)  
		    j++;
		while(k <= n && a[k] - a[i] <= c)//这里的循环是记录k - j个元素相同
		    k++;
		if(a[j] - a[i] == c && a[k - 1] - a[i] == c && k > 1)
		    res += k - j;//加上k - j个情况
	}
	cout << res;
	return 0;
}

好了,今天的分享就到这里了,感谢收看,记得三连支持,后序继续更新算法知识点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

记得开心一点嘛

您的打赏是对我最大的鼓励与期待

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值