[编程之美2.12]快速寻找满足条件的两个数及leetcode的3 sum closest 和 4 sum解析

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

例如有如下两个数组:5,6,1,4,7,9,8,给定sum = 10,则这两个数为1和9


思路一:就是枚举,从数组中任意取出两个数字,计算两者之和为给定的数字。其复杂度为O(n^2),这种方法很容易,就是效率不高。

思路二:先排序,对于a[i],则可以查找sum - a[i],看数组中有没有这个数,查找方法可以用二分或者哈希,这种方法可以将复杂度降低到O(nlogn)

思路三:先排序,然后通过两个下标的遍历来得出结果,附代码如下:

扩展问题:

1. 如果把这个问题中的"两个数字"改成"三个数字",解答时什么?

2. 如果完全相等的一对数字找不到,能否找到和最接近的解?

解:其实这两题跟leetcode的题目有点类似,我把leetcode上的2sum,3 sum,3 sum closest,4 sum这几题目可以拿来跟这个题目一起讨论


#include <iostream>
#include <algorithm>
#include <vector>
#include <stdlib.h>

using namespace std;

int main()
{
	vector<int>	   arr;
	int n;
	cin >> n;
	int temp;
	for(int i = 0;i !=n; i++)
	{
		cin >> temp;
		arr.push_back(temp);
	}

	sort(arr.begin(),arr.end());
	
	int sum;
	cin >> sum;

	int closet;

	int i = 0, j = arr.size() - 1;
	while(i < j)
	{
		if(arr[i] + arr[j] > sum)
			j--;
		else if(arr[i] + arr[j] < sum)
			i++;
		else
		{
			cout << arr[i] << " " << arr[j] << endl;
			i++;
			j--;
		}
	}

	system("pause");

}



Two Sum Mar 14 '11 1637 / 5446

Given an array of integers, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.

You may assume that each input would have exactly one solution.

Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2


解答:这里的难点在于要把下标找出来,按照上面的思路,不能直接对数组排序,因为排序过之后原来的下标就会消失,因此就用一个结构来标记,增加一个idx变量,再排序,思路同上题类似。

#include <iostream>
#include <algorithm>
#include <vector>
#include <stdlib.h>

using namespace std;

struct	pai{
	int data;
	int idx;
	bool  operator< (const pai& rhs) const{
		return	data < rhs.data;
	}
};

vector<int> twoSum(vector<int>& numbers, int target)
{
	vector<int>	res;
	vector<pai>	seq(numbers.size())	;

	for(int i = 0; i < numbers.size(); i++)
	{
		seq[i].data = numbers[i];
		seq[i].idx = i;
	}

	sort(seq.begin(), seq.end());

	int i = 0;
	int j = seq.size() - 1;
	while(i < j)
	{
		int plus = seq[i].data + seq[j].data;
		if(plus ==  target)
		{
			int minIndex = min(seq[i].idx, seq[j].idx);
			int maxIndex = max(seq[i].idx, seq[j].idx);
			res.push_back(minIndex);
			res.push_back(maxIndex);
			i++;
		}
		else if(plus < target)
		{
			i++;
		}
		else
		{
			j--;
		}
	}
	return res;
}


int main()
{
	vector<int>	num;
	for(int i = 0;i < 11; i++)
		num.push_back(i);

	vector<int>	result = twoSum(num, 10);

	for(int i = 0; i <result.size(); i++)
	{
		cout << result[i] << "	";
		if((i + 1)%2 == 0)
			cout << endl;
	}
	system("pause");
}

3 sum

3Sum Jan 18 '12 848 / 3229

Given an array S of n integers, are there elements abc in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note:

  • Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ? b ? c)
  • The solution set must not contain duplicate triplets.

    For example, given array S = {-1 0 1 2 -1 -4},

    A solution set is:
    (-1, 0, 1)
    (-1, -1, 2)
解:这里要注意的一点是不能输出相同的答案,因此要在vector中排除掉相同的,我采用的方法就是用set去存储vector中的元素,然后再转存过去。

//利用set来把重复的给消除掉

#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <set>

using namespace std;

vector<vector<int> > threeSum(vector<int> &num) {
	vector<vector<int> > res;
	sort(num.begin(), num.end());
	for(int k = num.size() -1; k >= 0; k--)
	{
		int i = 0;
		int j = k - 1;
		if(i == k)	i++;
		if(j == k)	j--;
		vector<int>	result;
		int subsum = - num[k];
		while(i < j)
		{
			int plus = num[i] + num[j];
			if(plus == subsum)
			{
				result.push_back(num[i]);
				result.push_back(num[j]);
				result.push_back(num[k]);

				res.push_back(result);	

				result.clear();
				i++,j--;
			}
			else if(plus < subsum)
			{
				i++;
			}
			else
			{
				j--;
			}

			if(i == k)	i++;
			if(j == k)	j--;
		}
	}

	set<vector<int> > demo;
	for(int i = 0;i != res.size(); i++)
		demo.insert(res[i]);

	vector<vector<int> > temp;
	for(set<vector<int> >::iterator iter = demo.begin(); iter != demo.end(); iter++)
		temp.push_back(*iter);

	return temp;
}

int main()
{
	int a[] = {0,0,0,-1,-2,2,1,-3,-3,3,-1};
	vector<int>	num;
	for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
		num.push_back(a[i]);
	vector<vector<int> >results = threeSum(num);

	for(vector<vector<int> >::iterator iter = results.begin(); iter != results.end(); ++iter)
	{
		for(int j = 0; j < 3; ++j)
		{
			cout << (*iter)[j] << "	";
		}
		cout << endl;
	}

	system("pause");
}

3 sum closest

3Sum Closest Jan 18 '12 437 / 1083

Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.

    For example, given array S = {-1 2 1 -4}, and target = 1.

    The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
解:结题思路还是跟上题差不多,一样是两个下标的移位,代码如下:

#include <cmath>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <set>

using namespace std;

int threeSumClosest(vector<int> &num, int target) {
	sort(num.begin(), num.end());         
	int ret;
	bool first = true;
	for(int k = num.size() - 1; k >= 0; k--)
	{
		int i = 0;
		int j = k - 1;
		while(i < j)
		{
			int sum  = num[i] + num[j] + num[k];
			if(first)
			{
				ret = sum;
				first = false;
			}
			else
			{
				if(abs(sum - target) < abs(ret - target))
					ret = sum;
			}

			if(ret == target)
				return ret;
			else if(sum < target)
				i++;
			else
				j--;
		}
	}

	return ret;
}

int main()
{
	int a[] = {1,1,1,0};
	vector<int>	num;
	for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
		num.push_back(a[i]);

	cout << threeSumClosest(num, 100) << endl;


	system("pause");
}

4 sum

4Sum Jan 27 '12 398 / 1131

Given an array S of n integers, are there elements abc, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.

Note:

  • Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ? b ? c ? d)
  • The solution set must not contain duplicate quadruplets.

    For example, given array S = {1 0 -1 0 -2 2}, and target = 0.

    A solution set is:
    (-1,  0, 0, 1)
    (-2, -1, 1, 2)
    (-2,  0, 0, 2)

解:

#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <set>

using namespace std;

vector<vector<int> > fourSum(vector<int> &num, int target) {
	vector<vector<int> > res;
	sort(num.begin(), num.end());

	for(int t = num.size() - 1; t > 0; t--)
	{
		for(int k = t -1; k > 0; k--)
		{
			if(k==t) k--;

			int i = 0;
			int j = k - 1;
			if(i == k)	i++;
			if(j == k)	j--;
			vector<int>	result;
			int subsum =target - num[k] - num[t];
			while(i < j)
			{
				int plus = num[i] + num[j];
				if(plus == subsum)
				{
					result.push_back(num[i]);
					result.push_back(num[j]);
					result.push_back(num[k]);
					result.push_back(num[t]);

					res.push_back(result);	

					result.clear();
					i++,j--;
				}
				else if(plus < subsum)
				{
					i++;
				}
				else
				{
					j--;
				}

				if(i == k)	i++;
				if(j == k)	j--;
			}
		}
	}

	set<vector<int> > demo;
	for(int i = 0;i != res.size(); i++)
		demo.insert(res[i]);

	vector<vector<int> > temp;
	for(set<vector<int> >::iterator iter = demo.begin(); iter != demo.end(); iter++)
		temp.push_back(*iter);

	return temp;
}

int main()
{
	int a[] = {1, 0, -1, 0, -2, 2};
	vector<int>	num;
	for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
		num.push_back(a[i]);
	vector<vector<int> >results = fourSum(num,1);

	for(vector<vector<int> >::iterator iter = results.begin(); iter != results.end(); ++iter)
	{
		for(int j = 0; j < 4; ++j)
		{
			cout << (*iter)[j] << "	";
		}
		cout << endl;
	}
	system("pause");
}

思考问题:如果把3 sum closest 的三个数改成人一个数字呢?那会是什么情况?

思考问题:如果不用set,怎么去除掉vector< vector<int> >中重复的元素?

再思考:用递归去解决3 sum的问题?有什么特别的方法?参见:http://blog.csdn.net/kindlucy/article/details/8011299




















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值