LeetCode 三数之和(三指针)

题目描述:给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

算法思路:最简单又粗暴的,当然是暴搜法,但是O(n^3)的时间复杂度太大了。比较可行的一种思路是将三层降为两层。
首先我们分析一下三数和为零的情况,

**第一种**两个负数、一个正数;
**第二种**一个负数、一个零、一个正数;
**第三种**一个负数、两个正数;
**第四种**三个零()。

这样就将三层降为两层,问题的关键转化为两个负数(正数)的和并且判断其相反数是否在给定数组中,为此引进map容器进行标记。
此题比较大的另一个难点就是去重,我的算法没有什么创新点,但是在两层for循环中还是利用的比较巧妙的,如果不理解的话好好想一想。LeetCode 三数之和(三指针)_第1张图片

vector<vector<int>> threeSum(vector<int>& nums) {
	vector<vector<int> > result;//结果容器
	int length = nums.size();
	if (length == 0) {
		return result;
	}
	sort(nums.begin(), nums.end());//首先排序
	map<int, int> positiveNumMap;//正数map容器
	map<int, int> negativeNumMap;//负数map容器
	bool zeroFlag = false;//0是否存在flag
	int zeroNum = 0;//零的数目
	int negativeNumIndex = 0;//非正数起始下标
	int positiveNumIndex = 0;//正数起始下标
	//寻找负数下标
	while (negativeNumIndex < length && nums[negativeNumIndex] < 0) {
		//将负数放入负数map容器
		++negativeNumMap[nums[negativeNumIndex]];
		++negativeNumIndex;
	}
	//判断0是否存在
	if (nums[negativeNumIndex] == 0) {
		zeroFlag = true;
	}
	//寻找正数开始下标
	positiveNumIndex = negativeNumIndex;
	while (positiveNumIndex < length && nums[positiveNumIndex] == 0) {
		//零的数目自增
		++zeroNum;
		++positiveNumIndex;
	}
	//如果无负数或正数,且零的数目小于3(即只有非正数或只有非负数的情况且零的数目小于3)
	if ((negativeNumIndex == 0 || positiveNumIndex == length) && zeroNum < 3) {
		return result;
	}
	//将所有的正数标记到map容器
	for (int i = positiveNumIndex; i < length; ++i) {
		++positiveNumMap[nums[i]];
	}
	//对任意两个负数和进行寻找,这里要注意需要排除重复的问题
	for (int i = 0; i < negativeNumIndex - 1; ) {
		for (int j = i + 1; j < negativeNumIndex; ) {
			//寻找负数和的相反数是否存在
			if (positiveNumMap[-(nums[i] + nums[j])] > 0) {
				//如果存在,则找到了一组
				vector<int> tempResult;
				tempResult.push_back(nums[i]);
				tempResult.push_back(nums[j]);
				tempResult.push_back(-(nums[i] + nums[j]));
				result.push_back(tempResult);
			}
			//跳过重复
			int tempJ = nums[j];
			while (j < length && tempJ == nums[j]) {
				++j;
			}
		}
		//跳过重复
		int tempI = nums[i];
		while (i < length - 1 && tempI == nums[i]) {
			++i;
		}
	}
	//如果零存在
	if (zeroFlag) {
		for (int i = 0; i < negativeNumIndex;) {
			//寻找负数的相反数是否存在
			if (positiveNumMap[-nums[i]] > 0) {
				//如果存在,则找到了一组
				vector<int> tempResult;
				tempResult.push_back(nums[i]);
				tempResult.push_back(0);
				tempResult.push_back(-nums[i]);
				result.push_back(tempResult);
			}
			//跳过相同的负数
			int temp = nums[i];
			while (i < negativeNumIndex && nums[i] == temp) {
				++i;
			}
		}
		//如果零的数目大于等于3
		if (zeroNum >= 3) {
			vector<int> tempResult;
			tempResult.push_back(0);
			tempResult.push_back(0);
			tempResult.push_back(0);
			result.push_back(tempResult);
		}
	}
	//对任意两个正数的和进行寻找
	for (int i = positiveNumIndex; i < length - 1; ) {
		for (int j = i + 1; j < length; ) {
			//寻找正数和的相反数是否存在
			if (negativeNumMap[-(nums[i] + nums[j])] > 0) {
				//如果存在,则找到了一组
				vector<int> tempResult;
				tempResult.push_back(-(nums[i] + nums[j]));
				tempResult.push_back(nums[i]);
				tempResult.push_back(nums[j]);
				result.push_back(tempResult);
			}
			//跳过重复
			int tempJ = nums[j];
			while (j < length && tempJ == nums[j]) {
				++j;
			}
		}
		//跳过重复
		int tempI = nums[i];
		while (i < length - 1 && tempI == nums[i]) {
			++i;
		}
	}
	return result;
}

虽然写出来了,但是。。。
LeetCode 三数之和(三指针)_第2张图片
下面是一位大佬的三指针算法,我只是添加了一些注释。

vector<vector<int>> threeSum(vector<int>& nums) {
	sort(nums.begin(), nums.end());//首先进行升序排序
	set<vector<int>> S;//利用集合的互异性去重
	int length = nums.size();
	//第一层循环用于移动中间的指针
	for (int i = 0; i < length; ++i) {
		int l = 0, r = length - 1;//左指针、右指针
		while (l < r) {
			if (l == i || r == i) {
			//left必须在i的左边,r必须在i的右边
				break;
			}
			if (nums[l] + nums[r] + nums[i] == 0) {
				vector<int> tmp = { nums[l],nums[i],nums[r] };
				S.insert(tmp);//插入集合,防止重复记录
				++l; 
				--r;
			}
			else if (nums[l] + nums[r] + nums[i] < 0) {
			//小于零,说明nums[l] + nums[r]小了,只能右移left,达到增大nums[l]的效果
				++l;
			}
			else {
			//大于零,说明nums[l] + nums[r]大了,只能左移right,达到减小nums[r]的效果
				--r;
			}
		}
	}
	vector<vector<int>> res(S.begin(), S.end());
	return res;
}

你可能感兴趣的:(LeetCode)