练习题 四数之和

题目

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abc 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

提示:

  • 1 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
提交代码
//四数之和

//三次双指针
//先确定数1可能的位置,再确定数2可能的位置,最后确定数3和数4 
//数组无序
//答案不唯一,且不能重复
//四个数之和是目标值 

#include
#include
#include
using namespace std;

vector nums;//整数数组
int target;//目标值
vector> result;//存储结果 
int n;//数组长度

//三次双指针算法
void fs(){
	int i = 0;//数1指针
	int last1 = (int)1e9 + 1;//存储上一个数1的值,防止重复 
	
	while(i < n - 3){
		if(last1 == nums[i]){
			//防止重复
			i++; 
		}else if(nums[i] + nums[n - 3] + nums[n - 2] + nums[n - 1] == target){
			//找到一个答案
			vector res;
			res.push_back(nums[i]);
			res.push_back(nums[n - 3]);
			res.push_back(nums[n - 2]);
			res.push_back(nums[n - 1]);
			result.push_back(res);
			//数1在位置i不可能还有答案,找下一个可能的数1
			last1 = nums[i]; 
			i++;
		}else if(nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] == target){
			//找到一个答案
			vector res;
			res.push_back(nums[i]);
			res.push_back(nums[i + 1]);
			res.push_back(nums[i + 2]);
			res.push_back(nums[i + 3]);
			result.push_back(res);
			//不可能再有下一个答案了
			break; 
		}else if(nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target){
			//不可能再有答案了
			break; 
		}else if(nums[i] + nums[n - 3] + nums[n - 2] + nums[n - 1] < target){
			//数1在位置i不可能有答案
			//找下一个可能的数1
			last1 = nums[i];
			i++; 
		}else if(nums[i] + nums[n - 3] + nums[n - 2] + nums[n - 1] > target){
			//找可能的数2的位置
			int j = i + 1;//数2指针
			int last2 = (int)1e9 + 1;//上一个数2的值,防重复 
			
			while(j < n - 2){
				if(last2 == nums[j]){
					//防止重复
					j++; 
				}else if(nums[i] + nums[j] + nums[n - 2] + nums[n - 1] == target){
					//找到一个答案
					vector res;
					res.push_back(nums[i]);
					res.push_back(nums[j]);
					res.push_back(nums[n - 2]);
					res.push_back(nums[n - 1]);
					result.push_back(res);
					//数2在j位置不可能还有答案,找下一个可能的数2
					last2 = nums[j];
					j++; 
				}else if(nums[i] + nums[j] + nums[j + 1] + nums[j + 2] == target){
					//找到一个答案
					vector res;
					res.push_back(nums[i]);
					res.push_back(nums[j]);
					res.push_back(nums[j + 1]);
					res.push_back(nums[j + 2]);
					result.push_back(res);
					//该数1的情况下不可能再有答案了
					break; 
				}else if(nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target){
					//该数1的情况下不可能再有答案了
					break; 
				}else if(nums[i] + nums[j] + nums[n - 2] + nums[n - 1] < target){
					//该数2不可能再有答案了 
					last2 = nums[j];
					j++;
				}else if(nums[i] + nums[j] + nums[n - 2] + nums[n - 1] > target){
					//找可能的数3和数4
					int k = j + 1,m = n - 1;//数3指针和数4指针
					int last3 = (int)1e9 + 1;//上一个数3的值,防重复 
					
					while(k < m){
						if(last3 == nums[k]){
							//防止重复
							k++; 
						}else if(nums[i] + nums[j] + nums[k] + nums[m] == target){
							//找到一个答案
							vector res;
							res.push_back(nums[i]);
							res.push_back(nums[j]);
							res.push_back(nums[k]);
							res.push_back(nums[m]);
							result.push_back(res);
							//找下一个答案
							last3 = nums[k];
							k++;
							m--; 
						}else if(nums[i] + nums[j] + nums[k] + nums[m] < target){
							last3 = nums[k];
							k++;
						}else if(nums[i] + nums[j] + nums[k] + nums[m] > target){
							m--;
						}
					}
					
					//找下一个可能的数2
					last2 = nums[j];
					j++; 
				}
			} 
			
			//找下一个可能的数1 
			last1 = nums[i];
			i++;
		}
	} 
} 

int main(){
	//输入数组 
	int t;//存储整数 
	
	while(cin.peek() != '\n'){
		scanf("%d",&t);
		nums.push_back(t);
	}
	
	//输入目标值
	scanf("%d",&target); 
	
	//-------------------------------
	
	n = nums.size();//数组长度
	
	//数组排序
	sort(nums.begin(),nums.end());
	
	//三次双指针算法
	fs();
	
	//输出结果
	for(int i = 0;i < n;i++){
		for(int j = 0;j < 4;j++){
			printf("%d ",result[i][j]);
		} 
		printf("\n");
	}
	
	return 0;
} 
 总结

解题思路:该题目不难看出暴力解法,可以使用双指针优化,四个元素用三次双指针,第一次双指针判断数1可能的位置,第二次双指针判断数2可能的位置,第三次双指针判断数3和数4可能的位置,时间复杂度为O(n^3)。使用last1、last2、last3记录数1、数2、数3上次的数值,防止重复。

注意:防止整数溢出的情况,虽然每个整数都未超过int的范围,但是相加后可能会超过int的范围,注意类型转换。在C++中,整数相加后的结果类型由操作数的类型来确定。如果你将两个整数相加,它们的类型决定了结果的类型。如果两个整数的类型相同,那么它们相加的结果将是相同类型的整数。如果两个整数的类型不同,C++ 将执行整数提升(Integer Promotion),将较小类型转换为较大类型,然后进行相加。如果有符号整数和无符号整数相加,有符号整数会被转换为无符号整数。

你可能感兴趣的:(练习题,算法,数据结构,笔记,leetcode)