1/167/15/16/18/454 K-sum algorithoms

1. Two Sum

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution.

Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

UPDATE (2016/2/13):
The return format had been changed to zero-based indices. Please read the above updated description carefully.

思路:结果要求返回数组index,所以排序再比较,不可以。

暴力搜索,时间复杂度O(N^2),不可以。

考虑到配对是对称性的,所以可以每个元素只在它位置之前的元素中查找是否存在(target-nums[i])的值,若存在,返回;查找的时间代价是O(log n)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> indices;
        unordered_map<int, int> m;
        for(int i=0; i<nums.size(); ++i){
            if( m.find(target-nums[i]) == m.end() ){
                m[nums[i]] = i;
            }else{
                indices.push_back( m[target-nums[i]] );
                indices.push_back( i );
                return indices;
            }
        }
        return indices;
    }
};


167. Two Sum II - Input array is sorted

Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        vector<int> res;
        if(numbers.size()<2) return res;
        vector<int>::iterator iter1 = numbers.begin();
        vector<int>::iterator iter2 = numbers.end()-1;
        while( iter1 != iter2){
            int sum = *iter1 + *iter2;
            if(sum>target){
                --iter2;
            }else{
                if(sum<target){
                    ++iter1;
                }else{
                    res.push_back(iter1-numbers.begin()+1);
                    res.push_back(iter2-numbers.begin()+1);
                    break;
                }
            }
        }
        return res;
    }
};




15. 3Sum

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: The solution set must not contain duplicate triplets.
For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

思路:本题只需要返回3个和为0的元素组合,不需要位置索引,可先排序。

方法, 先排序,对所有负数,遍历,在比它大的部分里,寻找two sum,注意搜索过程中遇到重复数字跳过;尺取法

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
      vector< vector <int>> res; //result for return, pay attention to the return type of function defination
      sort(nums.begin(), nums.end()); //system function in STL
      int length = nums.size();
 /*      //method 1, hash map
      for( int i=0; i<length; ++i){
          if( nums[i] > 0)
          break;
          if ( i>0 &&(nums[i] == nums[i-1]) )	//去重
          continue;  //ignore duplicate triplets
          //unordered_map<int,int> mapnums;
          int target = -nums[i];
          int j = i+1;
          int k = length -1;
          while( j<k ){ //already been sorted, so can search from both sides at the same time
              if( nums[j]+nums[k]==target ){
                  res.push_back( {nums[i], nums[j], nums[k]} );
                  while( (j<k) && (nums[j]==nums[j+1]) ) ++j;
                  while( (j<k) && (nums[k]==nums[k-1]) ) --k;
                  ++j; --k;
              }else{
                  if( nums[j]+nums[k]<target ){
                      ++j;
                  }else{
                      --k;
                  }
              }
          }
    }
*/
     //method 2, front/rear pointer
      for( int i=0; i<length; ++i){
          if( nums[i] > 0)
          break;
          if ( i>0 &&(nums[i] == nums[i-1]) )
          continue;  //ignore duplicate triplets
          int target = -nums[i];
          int front = i + 1, rear = length - 1;
          while (front < rear) {
              if (nums[front]+nums[rear] < target){
                  ++front;
              }else{
                  if (nums[front]+nums[rear] > target){
                      --rear;
                  }else{
                      res.push_back(vector<int> {nums[i], nums[front], nums[rear]});
                      ++front;
                      --rear;
                      while (front < rear && nums[front] == nums[front-1])
                      ++front;
                      while (front < rear && nums[rear] == nums[rear+1])
                      --rear;
                  }
              }
          }
      }
      return res;
    }
};

结果:42ms, your runtime beats 85.74% of cpp submissions.


16. 3Sum Closest

Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.

For example, given array S = {-1 2 1 -4}, and target = 1.
 The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

思路:先排序,然后固定第一个数字,在后面部分里,另外两个数字利用两侧同时向中夹逼的方法,来获取本次的最接近情况;

(注:一开始,我认为另外两个数字夹逼过程中,diff绝对值应该是越来越小的,若出现了反转,则说明本次夹逼可以停止;不过,这种想法是错误的,没考虑diff正负号变化)

正确代码如下;

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        sort( nums.begin(), nums.end() );
        int closetSum = nums[0]+nums[1]+nums[2];
        int diff = abs(closetSum-target);
        int len = nums.size();
        for( int i=0; i<len-2; ++i ){
            int left=i+1, right = len-1;
            while( left<right ){
                int sum = nums[left]+nums[right]+nums[i];
                int newDiff = abs(sum-target);
                if( newDiff<diff ){
                    closetSum = sum;
                    diff = newDiff;
                 }
                if(sum<target){
                    ++left;
                }else{
                    if(sum>target){
                        --right;
                    }else{
                        return target;
                    }
                }
           }
        }
        
      return closetSum;
    }
};

Runtime: 13 ms
Your runtime beats 58.48% of cpp submissions


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 example, given array S = [1, 0, -1, 0, -2, 2], and target = 0.
A solution set is:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

方法1:我们可以仿照3sum的解决方法。这里枚举第一个和第二个数,然后对余下数的求2sum,算法复杂度为O(n^3)

(注意:solution里面每条记录中可以有重复元素;不同条记录间防止重复,可以①先用set保存,再转为vector返回;②因为已排序,所以只可能和上一条记录重复,所以只需每次插入时判断尾部是否重复即可)

Runtime: 96 ms.Your runtime beats 46.16% of cpp submissions.

方法2:http://www.cnblogs.com/TenosDoIt/p/3649607.html  先对两个数字求和,建立hash_map;剩下的两个数字配对,在hash_map中寻找所有 和=target-data1-data2的组合;注意去重。具体思路如下;

O(n^2)的算法,先对数组排序。我们先枚举出所有二个数的和存放在哈希map中,其中map的key对应的是二个数的和,因为多对元素求和可能是相同的值,故哈希map的value是一个链表(下面的代码中用数组代替),链表每个节点存的是这两个数在数组的下标;这个预处理的时间复杂度是O(n^2)。接着和算法1类似,枚举第一个和第二个元素,假设分别为v1,v2, 然后在哈希map中查找和为target-v1-v2的所有二元对(在对应的链表中),查找的时间为O(1),为了保证不重复计算,我们只保留两个数下标都大于V2的二元对(其实我们在前面3sum问题中所求得的三个数在排序后的数组中下标都是递增的),即时是这样也有可能重复:比如排好序后数组为-9 -4 -2 0 2 4 4,target = 0,当第一个和第二个元素分别是-4,-2时,我们要得到和为0-(-2)-(-4) = 6的二元对,这样的二元对有两个,都是(2,4),且他们在数组中的下标都大于-4和-2,如果都加入结果,则(-4,-2,2,4)会出现两次,因此在加入二元对时,要判断是否和已经加入的二元对重复(由于过早二元对之前数组已经排过序,所以两个元素都相同的二元对可以保证在链表中是相邻的,链表不会出现(2,4)->(1,5)->(2,4)的情况,因此只要判断新加入的二元对和上一个加入的二元对是否重复即可),因为同一个链表中的二元对两个元素的和都是相同的,因此只要二元对的一个元素不同,则这个二元对就不同。我们可以认为哈希map中key对应的链表长度为常数,那么算法总的复杂度为O(n^2)

Runtime: 178 ms. Your runtime beats 12.31% of cpp submissions.

Runtime: 205 ms. Your runtime beats 7.03% of cpp submissions.

(从测试结果来看,时间反而更久;)

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
/*      //method 1
        set<vector<int>> res;
        int len = nums.size();
        if(len<4) return vector<vector<int> > (res.begin(), res.end());
        sort(nums.begin(), nums.end() );
        for( int i=0; i<len-3; ++i ){
            //while( i<len-3 && nums[i]==nums[i+1] ) ++i; //允许重复元素
            for( int j=i+1; j<len-2; ++j ){
                //while( j<len-2 && nums[j]==nums[j+1] ) ++j; //结果允许重复元素
                int sum = target-nums[i] - nums[j];
                int left=j+1, right = len-1;
                while( left<right ){
                    if( nums[left]+nums[right]==sum ){
                        vector<int> tmpRes;
                        tmpRes.push_back(nums[i]);
                        tmpRes.push_back(nums[j]);
                        tmpRes.push_back(nums[left]);
                        tmpRes.push_back(nums[right]);
                        res.insert(tmpRes);
                        //res.push_back(vector<int>{num[i], num[j], num[left], num[right]});
                        ++left; --right;
                    }else{
                        if( nums[left]+nums[right]<sum ){
                            ++left;
                        }else{
                            --right;
                        }
                    }
                    //while( left<right && nums[left]==nums[left+1] ) ++left;
                    //while( left<right && nums[right]==nums[right-1] ) --right;
                }
            }
        }
        return vector<vector<int> > (res.begin(), res.end());
*/        

        //method 2
        int n = nums.size();
        vector<vector<int> > res;
        unordered_map<int, vector<pair<int, int> > >pairs;
        pairs.reserve(n*n);
        sort(nums.begin(), nums.end());
        
        for(int i = 0; i < n; i++)
            for(int j = i+1 ; j < n; j++)
                pairs[nums[i]+nums[j]].push_back(make_pair(i,j));
        
        for(int i = 0; i < n - 3; i++)
        {
            if(i != 0 && nums[i] == nums[i-1]) continue;//防止第一个元素重复
            for(int j = i+1; j < n - 2; j++)
            {
                if(j != i+1 && nums[j] == nums[j-1]) continue;//防止第二个元素重复
                if(pairs.find(target - nums[i] - nums[j]) != pairs.end())
                {
                    vector<pair<int, int>> &sum2 = pairs[target - nums[i] - nums[j]];
                    bool isFirstPush = true;
                    for(int k = 0; k < sum2.size(); k++)
                    {
                        if(sum2[k].first <= j)continue;//保证所求的四元组的数组下标是递增的
                        if(isFirstPush || (res.back())[2] != nums[sum2[k].first])
                        {
                            res.push_back(vector<int>{nums[i], nums[j], nums[sum2[k].first], nums[sum2[k].second]});
                            isFirstPush = false;
                        }
                    }
                }
            }
        }
        return vector<vector<int> > (res.begin(), res.end());
        
    }
};


454. 4Sum II

Given four lists A, B, C, D of integer values, compute how many tuples (i, j, k, l) there are such that A[i] + B[j] + C[k] + D[l] is zero.
To make problem a bit easier, all A, B, C, D have same length of N where 0 ≤ N ≤ 500. All integers are in the range of -228 to 228 - 1 and the result is guaranteed to be at most 231 - 1.

Example:
Input:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
Output:
2
Explanation:
The two tuples are:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

方法:暴力遍历法,时间复杂度O(N^4)

先遍历A,B两个数组,求和存入hash_map,key为和值,value为此和值对应的对数;再遍历C/D两个数组,在hash_map中搜索0-c-d的值是否存在;如果存在,计数加1。时间复杂度O(N^2)

class Solution {
public:
    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
        int len = A.size();
        unordered_map<int, int> intHash;
        for( int i=0; i<len; ++i ){
            for( int j=0; j<len; ++j ){
                int sum = A[i]+B[j];
                if( intHash.find(sum)!=intHash.end() ){
                    intHash[sum]++;
                }else{
                    intHash[sum]=1;
                }
            }
        }
        int count = 0;
        for( int i=0; i<len; ++i ){
            for( int j=0; j<len; ++j ){
                int sum = 0 - C[i] - D[j];
                if( intHash.find(sum)!=intHash.end() ){
                    count += intHash[sum];
                }
            }
        }
        return count;
    }
};

Runtime: 872 ms.  Your runtime beats 55.32% of cpp submissions.


(纸上得来终觉浅 绝知此事要躬行)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值