算法练习:两指针之三数之和为0

问题描述

给出一个整型数组,找出所有三个元素的组合,其组合之和等于0。要求在结果集里不含有重复的组合。

举例:

输入{-2, 1, -1, 2, 1}

输出{-2, 1, 1 }

问题分析

最容易想到的是穷举法,挑选第一个元素,然后在其后挑选第二个元素,再从除已经挑选出的两个元素之外挑第三个元素,判断三者之和是否为0;第二种想到的是用回溯递归,这两种方法的时间复杂度均为O(n^3),可参阅代码部分关于这两种方法的实现。

那有没有复杂度低一些的呢,答案是有的,就是使用两指针的方法,从而使复杂度下降到O(n^2)。

首先,将数组按从小到大的排序,然后从头挑选一个元素,接着使用首尾两个指针来挑选后两个元素。如图所示(可结合后面实现代码理解):

代码实现

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

typedef vector<int> IntArray;
typedef vector<IntArray> ResultSet;

ResultSet gResultSet;	//结果集

//穷举法
void GetResultSet( const IntArray& mSrcArray )
{
	for( int i = 0; i < mSrcArray.size();  )
	{
		for( int j = i + 1; j < mSrcArray.size();  )
		{
			for( int k = j + 1; k < mSrcArray.size();  )
			{
				if ( ( mSrcArray[i] + mSrcArray[j] + mSrcArray[k] ) == 0 )
				{
					IntArray mTempArray;
					mTempArray.push_back( mSrcArray[i] );
					mTempArray.push_back( mSrcArray[j] );
					mTempArray.push_back( mSrcArray[k] );
					gResultSet.push_back( mTempArray );
				}

				//避免重复
				do{ ++k; } while( k < mSrcArray.size() && mSrcArray[k-1] == mSrcArray[k] );
			}
			//避免重复
			do{ ++j; } while( j < mSrcArray.size() && mSrcArray[j-1] == mSrcArray[j] );
		}
		//避免重复
		do{ ++i; } while( i < mSrcArray.size() && mSrcArray[i-1] == mSrcArray[i] );
	}
}

//两指针法
void GetResultSet_2Ptr( const IntArray& mSrcArray )
{
	for( int k = 0; k < mSrcArray.size(); ++k )
	{
		//第一个数大于0,因为是从小到大排了序的,所以以下就不可能了。
		if ( mSrcArray[k] > 0 ) break;

		//去除重复结果
		if ( k > 0 && mSrcArray[k] == mSrcArray[k-1] ) continue;

		int i = k + 1;
		int j = mSrcArray.size()-1;

		//两指针向中间靠拢找结果
		while( i < j )
		{
			int sum = mSrcArray[i] + mSrcArray[j] + mSrcArray[k];

			//和过小,左边指针移动
			if ( sum < 0 )
			{
				++i;
			}
			//和过大,右边指针移动
			else if ( sum > 0 )
			{
				--j;
			}
			//找到一个结果
			else
			{
				IntArray mTempArray;
				mTempArray.push_back( mSrcArray[k] );
				mTempArray.push_back( mSrcArray[i] );
				mTempArray.push_back( mSrcArray[j] );
				gResultSet.push_back( mTempArray );

				//避免重复
				do{ ++i; } while( i < j && mSrcArray[i-1] == mSrcArray[i] );

				//避免重复
				do{ --j; } while( i < j  && mSrcArray[j] == mSrcArray[j+1] );
			}
		}

	}
}

//回溯法(递归)
void GetResultSet_Recursive( const IntArray& mSrcArray, IntArray& mDstArrayTemp, int iStart, int nTarget )
{
	if ( nTarget == 0 && mDstArrayTemp.size() == 3 )
	{
		gResultSet.push_back( mDstArrayTemp );
	}
	else
	{
		for( int i = iStart; i < mSrcArray.size(); ++i )
		{
			//数量已经超过3,不能再加入了
			if ( mDstArrayTemp.size() >= 3 ) break;

			//避免重复加入
			if ( i > iStart && mSrcArray[i] == mSrcArray[i-1] ) continue;

			mDstArrayTemp.push_back( mSrcArray[i] );

			GetResultSet_Recursive( mSrcArray, mDstArrayTemp, i+1, nTarget + mSrcArray[i] );

			//回溯
			mDstArrayTemp.pop_back();
		}
	}
}

//打印结果集
void OutSubSets()
{
	for( ResultSet::iterator it = gResultSet.begin();
		it != gResultSet.end(); ++it )
	{
		for( IntArray::iterator itTemp = it->begin();
			itTemp != it->end(); ++itTemp )
		{
			cout << *itTemp << " ";
		}
		cout << endl;
	}
	cout << "--------------------------------------------" << endl;
}  

int main()
{
	IntArray mSrcArray;
	int nTemp;
	while( true )
	{
		mSrcArray.clear();
		while( cin >> nTemp )
		{
			if ( nTemp == 0 ) break;
			mSrcArray.push_back( nTemp );
		}  

		//排序
		sort( mSrcArray.begin(), mSrcArray.end() );  

		gResultSet.clear();
		//GetResultSet( mSrcArray );
		//GetResultSet_2Ptr( mSrcArray );

		IntArray mDstArrayTemp;
		GetResultSet_Recursive( mSrcArray, mDstArrayTemp, 0, 0 );

		//打印结果集
		OutSubSets();
	}  

	return 0;
}

系列文章说明:

1.本系列文章[算法练习],仅仅是本人学习过程的一个记录以及自我激励,没有什么说教的意思。如果能给读者带来些许知识及感悟,那是我的荣幸。

2.本系列文章是本人学习陈东锋老师《进军硅谷,程序员面试揭秘》一书而写的一些心得体会,文章大多数观点均来自此书,特此说明!

3.文章之中,难免有诸多的错误与不足,欢迎读者批评指正,谢谢.

作者:山丘儿

转载请标明出处,谢谢。原文地址:http://blog.csdn.net/s634772208/article/details/46729197

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-06 10:17:40

算法练习:两指针之三数之和为0的相关文章

求一个int型整数的两种递减数之和(java)--2015华为机试题

题目描述: 给出一个整数(负数使用其绝对值),输出这个整数中的两种递减数(1.最大递减数:2.递减数中各位数之和最大的数)之和. 递减数:一个数字的递减数是指相邻的数位从大到小排列的数字,不包含相邻的数位大小相同的情况. 最大递减数:所输入整数的所有递减数中值最大的一个. 如: 75345323,递减数有:75,753,53,53,532,32.那么最大的递减数为753. 各位数字之和最大的递减数: 如75345323中的各递减数:75各位数之和=12(7+5=12),753各位数之和=15(7

算法练习:两指针之三色排序

问题描述 输入一个整型数组,每个元素在0~2之间,其中0,1,2分别代表红.白.蓝.现要求对数组进行排序,相同颜色的在一起,而且按红白蓝顺序先后排列.要求时间复杂度为O(n). 问题分析 最容易想到的是排序,比如快排,归并,堆排等,但它们的时间复杂度为O(nlogn),与题意不符. 第二种想到的是计数排序,扫描一遍过去,分别记录0,1,2的个数,然后再对数组进行赋值.时间复杂度为O(2n),即O(n),满足条件. 还有一种方法,只需扫描一遍数组即可,其充分利用了元素只有3种的特性:在扫描数组的时

LeetCode-3SUM(数组中三数之和为0)

Given an array S of n integers, are there elements a, b, c 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 solut

3sum 三数之和为0的简单实现

思路简单: (1) 三重 for ,符合a+b+c=0的 a,b,c保存在tuple里 (2)tuple保存在set 中,一可去重,二可保持字典序 (3)简单代价就是复杂度很高,O(n^3*logn) typedef tuple<int,int,int> triplet; triplet sort3(int a,int b,int c){ if(a<=b){ if(b>c){ swap(b,c); if(a>b) swap(a,b); } } else{ swap(a,b);

两指针扫描算法举例

问题一:求sum值 描述:给定一有序序列ary和sum值,求序列中是否存在两元素e1和e2,其和刚好为sum. 算法思想:这是典型的两指针的用法.i指针从头部开始,j指针从尾部开始,相向移动,本质向讲,在移动过程中比较ary[i]+ary[j]与sum的大小,达到逐步排除元素的过程,缩短查找范围.最开始,i和j处于序列首部和尾部如果ary[i]+ary[j]=sum,直接返回结果.如果ary[i]+ary[j]<sum,需要增大ary[i]或者ary[j],但是j已经不能在增大,只能i++,因此

LeeCode数组第15题三数之和

题目:三数之和 内容: 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组. 注意:答案中不可以包含重复的三元组. 例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ] 思路:题目实现可分为两个步骤,分别是(1)寻找三个满足条件的元素(2)去重复对于第一个小问题,首先考虑三个for循

LeetCode 18. 4Sum (四数之和)

Given an array S of n integers, are there elements a, b, c, 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: The solution set must not contain duplicate quadruplets. For exampl

LeetCode 18. 四数之和(4Sum)

题目描述 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组. 注意: 答案中不可以包含重复的四元组. 示例: 给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0. 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]

leetcode 三数之和

题目: 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组. 注意:答案中不可以包含重复的三元组. 题目分析: 利用循环,使得三数之和为0.分别令i,l,r为三个数,i作为外循环,遍历数组.主要难点在于不重复 参考:https://leetcode.com/problems/3sum/discuss/147561/Python-tm 代码(python): 原文地址:https://ww