N Sum问题总结

最近在刷leetcode,在Array分类中经常遇到“给定一个TargetNumber和一个Array,在Array中找出n个数,这n个数之和等于TargetNumber”的问题。随着n的值增大,题目的难度也会增大。当n取一个具体值时,该题目就会变成其他题目的变种。比如当n = 1时,就变成了简单的一维数组查找问题。本文通过对比n取不同值时的解题思路,抽象出一种通用解法。

注:本文聚焦算法本身的思想,解法中不考虑重复解问题。

1)n = 1时。此时就是典型的一维数组查找问题。推荐的解法是“排序+二分查找”。此处就不上代码了。

2)n = 2时。思路:

(1)按升序排序;

(2)ptr1和ptr2表示待搜索的2个位置,初始时将ptr1指向数组头,将ptr2指向数组尾;

(3)开始搜索,若指针所指的2数之和大于target number时,ptr2向队首移动一位,若指针所指的2数之和小于targetnumber时,ptr1向队尾移动一位,当2数之和与targetnumber相等时,2个指针所指的数即为结果。

Sample code:

vector<vector<int>> FindTwoSum(vector<int>& nums, int target)
{
    vector<vector<int>> res;
    int n = nums .size();
    if(2 > n) return res;
	
	sort(nums.begin(), nums.end());
	int left = 0, right = nums.size() - 1;
	while(left < right)
	{
		int sum = nums[left] + nums[right];
		if(sum == target)
		{
			vector<int> mem = {nums[left], nums[right]};
			res.push_back(mem);
			++left;
		}
		else if(sum > target) --right;
		else ++left;
	}
	
	return res;
}

3)n = 3 时。

思路:

(1)     首先依然对数组进行排序;

(2)     定义三个指针ptr1,ptr2, ptr3指向3个位置。初始化ptr1指向队首,并固定ptr1的位置,对ptr2和ptr3使用类似TwoSum的策略进行搜索;为了提升搜索运行效率,可以在搜索前通过边界判断,搜索搜索范围;

(3)     向后移动ptr1重复步骤2,所有位置均遍历后结束;

Sample code:

vector<vector<int>> findThreeSum(vector<int> nums, int target)
{
	vector<vector<int>> res;
	int  n = nums.size();
	if(3 > n) return res;	
	sort(nums.begin(), nums.end());
	
	for(int ptr1 = 0; ptr1 < n - 2; ++ptr1)
	{
		// we need cut branches at first.
		if(nums[ptr1] + nums[ptr1 + 1], nums[ptr1 + 2] > target) break;
		if(nums[ptr1] + nums[n - 1] + nums[n - 2] < target) continue;
		
		int ptr2 = ptr1 + 1, ptr3 = n - 1;
		while(ptr2 < ptr3)
		{
			int sum = nums[ptr1] + nums[ptr2] + nums[ptr3];
			if(sum == target)
			{
				vector<int> mem = {nums[ptr1], nums[ptr2], nums[ptr3]};
				res.push_back(mem);
				++ptr2; --ptr3;
			}
			else if(sum > target) ++ptr2;
			else --ptr3;
		}		
	}
	
	return res;
}

4)n = 4时。

思路:

(1)     依然是对数组进行排序;

(2)     定义4个指针ptr1,ptr2,ptr3, ptr4,采用的策略和上文中的策略相似。先确定ptr1的位置,然后采用ThreeSum的策略进行搜素。

Sample code:

vector<vector<int>> findFourSum(vector<int> nums, int target)
{
	vector<vector<int>> res;
	int n = nums.size();
	if(4 > n) return res;
	
	sort(nums.begin(), nums.end());
	for(int ptr1 = 0; ptr1 < n - 3; ++ptr1)
	{
		//cut branches first.
		if(nums[ptr1] + nums[ptr1 + 1] + nums[ptr1 + 2] + nums[ptr1 + 3] > target) break;
		if(nums[ptr1] + nums[n - 1] + nums[n - 2] + nums[n - 3] < target) continue;
		
		for(int ptr2 = ptr1; ptr2 < n - 2; ++ptr2)
		{
			//cut branches second.
			if(nums[ptr1] + nums[ptr2] + nums[ptr2 + 1] + nums[ptr2 + 2] > target) break;
			if(nums[ptr1] + nums[ptr2] + nums[n - 1] + nums[n - 2] < target) continue;
			
			int ptr3 = ptr2 + 1, ptr4 = n - 1;
			while(ptr3 < ptr4)
			{
				int sum = nums[ptr1] + nums[ptr2] + nums[ptr3] + nums[ptr4]; 
				if(sum == target)
				{
					vector<int> mem = {nums[ptr1], nums[ptr2], nums[ptr3], nums[ptr4]};
					res.push_back(mem);
					++ptr3; --ptr4;
				}
				else if(sum < target) ++ptr3;
				else --ptr4;
			}
		}
	}
	
	return res;
}

总结上述解法,不难发现。除了n=1这种场景的解法把不同外,其他情况的策略都大同小异。因此当n很大时,可以考虑使用recursion+ backtracking来解。

Sample code: 

void helper(vector<int>& nums, int target, int start_index, int N, vector<int>& mem, vector<vector<int>>& res)
{	
	if(N == 1)
	{
		// The implementation of binarySearch is omitted.
		binarySearch(nums, target, start_index, nums.size() - 1, mem, res);
		return;
	}
	
	if(N == 2)
	{
	// adopted the TwoSum strategy.
		int ptr1 = start_index, ptr2 = nums.size() - 1;
		while(ptr1 < ptr2)	
		{
			int sum = nums[ptr1] + nums[ptr2];
			if(sum == target)
			{
				mem.push_back(nums[ptr1]);
				mem.push_back(nums[ptr2]);
				res.push_back(mem);
				mem.pop_back(); mem.pop_back();
				++ptr1; --ptr2;
			}
			else if(sum > target) ++ptr1;
			else --ptr2;
		}
		
		return;
	}
	
	
	for(int ptr = start_index; ptr < nums.size() - N + 1; ++ptr)
	{
		//cut branches.
		int sum;
		for(int i = ptr, sum = 0; i < ptr + N; ++i)
			sum += nums[i];
		if(sum > target) break;
		
		for(int i = n -1, sum = nums[ptr]; i > n - N; --i)
			sum += nums[i];
			
		if(sum < target) continue;
		
		mem.push_back(nums[ptr]);
		helper(nums, target - nums[ptr], ptr + 1, N - 1, mem, res);
		mem.pop_back();
	}
}

vector<vector<int>> findNSum(vector<int>&nums, int target, int N)
{
	vector<vector<int>> res;
	if(0 >= N) return res;
	int n = nums.size();
	if(N > n) return res;
	
	sort(nums.begin(), nums.end());
	vector<int> mem;
	helper(nums, target, 0, N, mem, res);
	
	return res;
}

猜你喜欢

转载自blog.csdn.net/endurehero/article/details/80926136
今日推荐