zoukankan      html  css  js  c++  java
  • 算法练习:两指针之三数之和为0

    问题描写叙述

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

    举例:

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

    输出{-2, 1, 1 }

    问题分析

    最easy想到的是穷举法,挑选第一个元素,然后在其后挑选第二个元素,再从除已经挑选出的两个元素之外挑第三个元素,推断三者之和是否为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


  • 相关阅读:
    P2523 [HAOI2011]Problem c
    P2518 [HAOI2010]计数
    P2513 [HAOI2009]逆序对数列
    P2519 [HAOI2011]problem a
    P5020 货币系统
    P2580 于是他错误的点名开始了(Trie)
    P3805 【模板】manacher算法
    基础
    白兔的字符串(hash入门)
    ACM的分类训练题集(转载)
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/6936237.html
Copyright © 2011-2022 走看看