快速寻找满足条件的两个数

快速寻找满足条件的两个数

提出问题:

能否快速找出一个数组中的两个数字,让这两个数字的和等于一个给定的值,为了简化起见我们假设这个数组中至少存在一组满足要求的解。

例如有如下两个数组,

5,6,1,4,7,9,8 给定sum=10

1,5,6,7,8,9给定sum=10


解法一:

这个题目也不是很难,也很容易理解。但是要得出高效的解法,还是需要一番思考的。

直接的解法是穷举:从数组中任意取出两个数字,计算两者之和是否为给定的数字。

显然其时间复杂度为O(n*n)。这个算法简单,写起来也很容易,但是效率不高。

#include<cstdio>
#include<algorithm>
#define MAX 100005
using namespace std;

int main(int argc,char *argv[])
{
	int n,m;
	int v[MAX];
	int i,j;
	int flag=0;
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)
		scanf("%d",&v[i]);
	sort(v,v+n);
	for(i=0;i<n;i++)
	{
		for(j=i+1;j<n;j++)
		{
			if(v[i]+v[j]==m)
			{
				flag=1;
				break;
			}
		}
		if(flag)
			break;
	}
	if(flag)
		printf("%d %d\n",v[i],v[j]);
	else
		printf("No Solution\n");
	
	return 0;
}

解法二:

求两个数字的和,我们假设给定的和为sum。一个变通的思路,就是对数组中的每个元素array[i]都判断一下sum-array[i]是否在数组中。这样就变成一个查找的算法了。

在一个无序的数组中查找一个数的时间复杂度为O(n),对于每个数字array[i],都需要查找对应的sum-array[i]在不在数组中,很容易得到时间复杂度还是O(n*n)。这和原始的方法相比并没有改进,但是如果能有高效的查找算法,就能提高整个算法的效率,怎样提高查找效率?

大家很容易就会想到二分查找,这样可以将原来O(n)的查找时间缩短到O(logN)。这样对于每个array[i],都要花费O(logN)去查找对应的sum-array[i]在不在数组中,总的时间复杂度降为O(NlogN),另外将长度为N的数组进行排序本身也需要O(NlogN)的时间,这样总的时间复杂度仍然为O(NlogN)。这样,就改进了原始的算法。

到这里,我们可能会想,先排序在进行二分查找固然可以将时间复杂度从O(n*n)缩短到O(NlogN),但是还有更快的查找方法:Hash表。应为给定一个数字,根据Hash映射查找另外一个数字是否数组中只需要O(1),这样的话,总体的算法复杂度可以降为O(N),但这种方法需要额外的空间进行Hash表的存储。

#include<cstdio>
#include<algorithm>
#define MAX 1005

using namespace std;

int v[MAX];

int main(int argc,char *argv[])
{
	int n,m;
	int i;
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)
	{
		int temp;
		scanf("%d",&temp);
		v[temp]++;
	}
	for(i=1;i<=m;i++)
	{
		if(v[i]>0)
		{
			v[i]--;
			if(v[m-i]>0)
			{
				printf("%d %d\n",i,m-i);
				break;
			}
		}
	}
	if(i>m)
		printf("No Solution\n");
	
	return 0;
}

这段代码的前提是知道元素的取值范围是多少,然后用一个数组进行枚举。


解法三:

还可以换个角度思考问题,假设已经给定了这个数组的任意两个元素的和的有序数组,那么利用二分查找,只需要O(NlogN)就可以解决这个问题。当然我们不大可能去计算这个有序数组,应为它需要O(n*n)的时间。但这个方法仍然启发我们可以对两个数字的和进行一个有序遍历。

首先对数组进行排序,时间复杂度为O(NlogN),然后令i=1,j=n-1看array[i]+array[j]是否等于sum,如果是,则结束。如果小于sum,则i=i+1;如果大于sum,则j=j-1。这样只需要在排好的数组上遍历一次,就可以得到最后结果,时间复杂度为O(N)。两部加起来时间复杂度为O(NlogN)。

#include<cstdio>
#include<algorithm>
#define MAX 100005

using namespace std;

int main(int argc,char *argv[])
{
	int i,j;
	int n,m;
	int v[MAX];
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)
		scanf("%d",&v[i]);
	sort(v,v+n);
	int low=0;
	int high=n-1;
	while(low<=high-1)
	{
		int temp=v[low]+v[high];
		if(temp==m)
		{
			printf("%d %d\n",v[low],v[high]);
			break;
		}
		else if(temp>m)
			high--;
		else if(temp<m)
			low++;
	}
	if(low>high-1)
		printf("No Solution\n");

	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值