双指针的奇妙用法

导语:

有些问题问题的求解需要n2的复杂度,但是用双指针的话可以降低很多复杂度,举两个例子阐述一下双指针的用法。

First:

给定一个无序的数组a(长度为len)和一个数字k,让你求满足a[i] + a[j] <= k(其中i != j)的(i, j)的数目。

暴力跑的话两重for就可以解决了,并不复杂。但是这是n2的复杂度。
再说说用双指针如何做(解释写代码里了),给出代码如下:

int fun(int a[], int len) {
	int ans = 0;
	sort(a, a + len);    //先排序,让整个数组变得有序
	int i = 0, j = len - 1;
	while(i < j) {
		while(a[i] + a[j] > k && i < j)   //从最大的开始,往前移动指针
			j --;
		ans += j - i;      //这个时候所有a[i]和a[j]之间的数(不包括a[i])和a[i]的和都不会超过k
		i ++;      //统计完了包含a[i]的情况前指针后移
	}
	return ans;
}

Second:

还是给定一个数组a,其长度为len,和一个k。求满足a[i] + a[j] == k的(i, j)的对数。

暴力的方法还是两重for,有更优一点的解法,可以在一重for的复杂度之内解决:

int num[100000];    //开一个桶
memset(num, 0, sizeof(num));
for(int i = 0; i < len; ++ i) {   //记录每个数字的个数
	num[a[i]]++;
}
int ans = 0;
for(int i = 0; i < len; ++ i) {    //枚举a[i],检查k - a[i]的个数
	ans += num[k - a[i]];
}
ans = ans / 2;          //会加重复的,所以要除以2

这似乎是一个不错的方法,可是题目中给的k高达1e9的话怎么办???数组是开不到这么大的。
这时就有人会说用STL中的map映射一波,好吧,这样确实可以,也就是常数稍微高了那么一点点,插入和查看操作都是近似log(n),总的来说也就是n * log(n) + log(n)级别。代码我就不写了,还是很简单的。
下面要用双指针法解决:

int fun(int a[], int len) {
	int ans = 0;
	sort(a, a + len);    //还是先排序
	int i = 0, j = len - 1;
	while(i < j) {
		if(a[i] + a[j] > k)   //后指针前移
			j --;
		else if(a[i] + a[j] < k)  //前指针后移
			i ++;
		else {
			if(a[i] == a[j]) {        //如果两个指针所指向的数字都相同了,那么两者之间所有的数都相同,从(j - i + 1)中挑两个,求一下C(j - i + 1, 2)就行了
				ans += (j - i + 1) * (j - i) / 2;
				break;
			}
			int l = i, r = j;
			while(a[l] == a[i]) ++ l;    //寻找和a[i]相等的数
			while(a[r] == a[j]) -- r;	//同理,寻找和a[j]相等的数
			ans += (l - i) * (j - r);   //统计上
			i = l;
			j = r;
		}
	}
	return ans;
}

暂时就这么多了,以后发现新东西在补充。。。

你可能感兴趣的:(算法杂谈)