微软面试题:快速寻找满足条件的两个数

这同样是博主在面试 Microsoft 时遇到的面试题,只是当时博主没有答好,惭愧...

问题描述很简单,给出一个 int 类型的数组,能否快速找出数组中的两个数字,使它们的和等于一个给定的值

即,给出数组

假定给定两数只和 SUM 为15

那么我们需要找到的分别是 (8, 7),(4, 11),(9, 6)

首先当我被问到这个问题的时候,我首先想到的就是穷举法,即遍历整个数组的所有可能的组合,穷举法实现简单,只需要两个 for 循环便可得到我们想要的答案

面试官又问我该算法的时间复杂度,显然其时间复杂度为N*(N - 1) / 2,即O(N2)

接着被问到能不能想个更加高效的算法来实现该功能,也就是说要我优化其时间复杂度。想到“时间复杂度”,首先想到的自然是数据结构的内容,由于之前那个先入为主的观念,

我思考的方向始终是逐个匹配的算法

以下是我当时的想法:

当时我首先想到的是先把数组做排序,记录下第一个数值小于 SUM 的元素a[n],接着用a[0]开始与a[n]求和,如果其结果大于 SUM,则计算a[0]与a[n-1]的值并与 SUM 进行比较,

如果其结果小于 SUM,则计算a[1]与a[n]的值并与 SUM 进行比较。

当时的面试管朝我笑笑便闲聊了几句离开了,前后才20分钟,感觉不妙。后来博主想想,觉得这算法的时间复杂度的确已经做到了优化,但似乎任然不到位!

**后来经过多方查阅资料,终于找到一种最为有效的方法,在此与大家分享:

首先我们得转变思路,既然我们要验证数组中是否存在一个数与arr[i]之和为 SUM,我们何不判别 SUM - arr[i] 是否在数组中呢。这样,该问题就变成了一个查找的算法。但是,直接在一个无序数组中查找一个数的时间复杂度是O(N),那么对于每个arr[i]都需要查找对应的SUM - arr[i],最终的时间复杂度仍然是O(N2),所以,新排序是正确的。在已排好序的数组中进行二分查找的时间复杂度为O(log2N) ,对长度为N的数组进行排序本身也需要O(Nlog2N)的时间,所以总的时间复杂度仍然是O(Nlog2N)。

--------------------附录--------------------

该方法所用到的算法

快速排序:

//快速排序
int *qsort(int *a, int al, int ar)
{
	int i = al, j = ar;	//al, ar分别为子数组的首尾索引值
	int k = a[al];	//保存key值
	while (i < j && a[j] >= k)
	{
		j--;
	}
	if (i < j)
	{
		a[i] = a[j];
	}
	while (i < j && a[i] < k)
	{
		i++;
	}
	if(i < j)
	{
		a[j] = a[i];
	}
	a[i] = k;
	if((i - al) > 0)
	{
		qsort(a, al, i-1);
	}
	if ((ar-i) > 0)
	{
		qsort(a, i+1, ar);
	}
	return a;
}

 

二分查找:

//二分查找
void search(int *a, int L, int s)
{
	cout<<"查找满足和为"<<s<<"的两个数..."<<endl;
	int cnt = 0;
	for (int c = 0; c <= L - 1; c++)
	{
		int i = c, j = L -1, d = s - a[i];
		while(i <= j)
		{
			if (a[mid] == d && j != c)
			{
				cnt++;
				cout<<"第"<<cnt<<"组:"<<a[c]<<" "<<a[mid]<<endl;
				break;
			}
			else if (a[mid] > d)
			{
				j = mid - 1;
			}
			else
			{
				i = mid +1;
			}
		}
	}
}

 

----------运行结果----------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值