您的位置:首页 > 其它

LeetCode1/16/15/18 Two Sum/3Sum/3Sum Closest/4Sum**

2015-04-03 19:37 363 查看
一:Two Sum

题目:

Given an array of integers, 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

链接:https://leetcode.com/problems/two-sum/

分析:此题有三种解法,时间复杂度也是逐渐降低的,第一种方法是暴力,所需时间为O(N^2),第二种方法是排序,后两边往中间扫描,时间复杂度为排序的时间O(NlgN);第三种方法是用hash_map, 所需的时间复杂度为O(N),it is amazing!!

1:暴力法

暴力法即通过两个循环判断两个数的和是否为target, 时间复杂度为O(N),在leetcode上过不了,会TLE

class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
        vector<int> vec;
        for(int i =0; i < numbers.size()-1; i++){
            for(int j = i+1; j < numbers.size(); j++){
                if((numbers[i] + numbers[j]) == target){
                   vec.push_back(i);
                   vec.push_back(j);  
                   return vec;
                }
            }
            
        }
    }
};
2:排序+两边扫描

由于我们要求数组中元素的小标,排序必然破坏这点,所以需要一个结构体来保存数组中的值和小标,然后依据结构体中的value进行排序;排序后可以通过两个指针,分别指向首i和尾j,如果和等于target,则找到;如果大于target,则将j--以降低和大小;否则小于则i++增大和,时间复杂度为O(NlgN+N)= O(lgN).

相对于第一种方法,时间复杂度降低了,但是空间复杂度增加到了O(N),可以说是以空间换时间吧。从两端往中间扫描的思想很重要,或者二分,上一道题也用到了这个思想,

// 结构体 为了保存下标
struct sNode{
    int value;
    int index;
    sNode(int v, int i){
        this->value = v;
        this->index = i;
    }
};

bool cmp(sNode s1, sNode s2){  // 按值排序
        return s1.value < s2.value;
    }

class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
        vector<sNode> vec;
        vector<int> result;
        for(int i = 0; i < numbers.size(); i++){
            vec.push_back(sNode(numbers[i], i));
        }
        sort(vec.begin(), vec.end(), cmp);
        int i = 0, j = vec.size()-1;
        while(i != j){
			int sum = vec[i].value + vec[j].value;
            if(sum == target){   // 等于则找到
                if(vec[i].index < vec[j].index){
                    result.push_back(vec[i].index+1);
                    result.push_back(vec[j].index+1);
                }
                else{
                    result.push_back(vec[j].index+1);
                    result.push_back(vec[i].index+1);
                }
				break;
            }
			else if(sum < target){   // 小于 i++ 增大和
				i++;
			}
			else j--;   // 大于 j-- 降低和
        }
        return result;
        
    }
};

此外,如果这里有多个满足a+b= target 怎么办,比如[-4,-1,0,0,0,2,2,3] target = 2, 如何求出多个且互相独立的<-1,3>与[0,2]呢,

此时我们在判断出a[i]+a[j] == target的时候,还需要判断a[i] == a[i-1] && a[j]==a[j+1] 是否成立,成立的话,则不不进行加入,直接i++,j--

代码如下:

void twoSumAll(vector<int> &num, int target, vector<vector<int> > &result){ //找到a+b == target并且unique 假设num已经排序好O(N)

		int i = 0,  j = num.size()-1;
		while(i < j){
			int a = num[i];
			int b = num[j];
			if(a + b == target){
				// 如果重复则 i++ ,j--  如 [-1 0 0 2 2 4] target = 2 
				if((i-1 >= 0 && num[i-1]==a) && (j+1 < num.size()-1 && num[j] == b)){
					i++;
					j--;
				}else{  // 不重复 独一无二的
					vector<int> v;
					v.push_back(a);
					v.push_back(b);
					result.push_back(v);
					i++;
					j--;
				}
			}else if(a+b < target) i++;      // 小于 i++ 增大和  
			else j--;   //大于 j-- 降低和 
		}
	}


3:hash_map

只需扫描一次,每次扫描的时候判断是否在hash_map中,如果不在加入hash_map表中,接着判断target-该元素是否在hash表中,如果在,则找到,注意这里有两点:(1)防止如[3,2,6] 6,,3+3=6 两次hash要判断下标是否相同 (2)如果有重复元素也是可以的,因为即使重复元素没有加入hash_map中,它还是会找target-该元素 是否存在hash_map中的, 如[3,4,3] 6 也是可以满足的。

相对于第一种方法,以空间换时间,但是时间复杂度只需要O(N)了。

//  hash_map    时间复杂度降维O(n)
class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
        vector<int> vec;
		map<int, int> hmap;
        for(int i =0; i < numbers.size(); i++){
			if(!hmap.count(numbers[i])){
				hmap.insert(pair<int, int>(numbers[i], i));
			}
			if(hmap.count(target-numbers[i])){   // 此时如果数组中元素重复了 没有加入到hash表中,但是结果仍然是正确的 
				int j = hmap[target-numbers[i]];      // 如3,4,3,6
				if(j != i){                 // 注意这里判断下 以防止如[3,2,4] 6  3+3=6 没有判断则输出1 1
					vec.push_back(j+1);
					vec.push_back(i+1);
					return vec;
				}
			}
        }
		return vec;
    }
};
由于LeetCode上没有包含hash_map这个STL容器,故这里用了map,map是基于RBTree的,hash_map基于hash_table,

区别:set和map底层数据结构是rbtree,而hash_map和hash_set底层是hashtable,不具有自动排序的功能了,但是当数据量很大的时候,只能用hash_map或者hash_set,因为map或者set都是基于rbtree,此时结点多了速度也慢,以空间换时间,查找速度更快

二:
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:

Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
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)

分析:
这里由题目知道要求排序后的结果,肯定是利用上面的方法二 ——排序与两边扫描,,这里要求a+b+c = 0, 肯定还是先排序,然后只需要将c作为target即可,这是target为数组中的每个元素,遍历一遍,比如target= num[k], 此时只需要在[k+1, n-1]之间寻找num[i]+num[j] = -num[k] 即可。。 。注意这里要求为多个且独一无二 就需要有条件限制,因为是有序,所以条件限制也很简单。。时间复杂度为排序O(NlgN) + O(N*N) = O(N^2)。。。当然暴力也可以,但是暴力的时间复杂度为O(N^3)
肯定超时。

class Solution {
public:
    vector<vector<int> > threeSum(vector<int> &num) {
        sort(num.begin(), num.end());
        vector<vector<int> > result;
        int t = 0;
        if(num.size() < 2) return result;
        for(int k = 0; k < num.size()-2; k++){
            if(k >0 && num[k] == num[k-1])continue;   // 重复则跳过  独一无二
           // int i = k+1, j = num.size()-1;
            int c = num[k];
			twoSumUnique(num, -c, k, result);
        }
        return result;
    }

	void twoSumUnique(vector<int> &num, int target, int k, vector<vector<int> > &result){ //找到a+b == target并且unique 假设num已经排序好O(N)

		int i = k+1,  j = num.size()-1;
		//vector<vector<int> > result;
		while(i < j){
			int a = num[i];
			int b = num[j];
			if(a + b == target){
				// 如果重复则 i++ ,j--  如 [-1 0 0 2 2 4] target = 2 
				if((i-1 > k && num[i-1]==a) && (j+1 < num.size() && num[j+1] == b)){
					i++;
					j--;
				}else{  // 不重复 独一无二的
					vector<int> v;
					v.push_back(-target);
					v.push_back(a);
					v.push_back(b);
					result.push_back(v);
					i++;
					j--;
				}
			}else if(a+b < target) i++;      // 小于 i++ 增大和  
			else j--;   //大于 j-- 降低和 
		}
	}
};


三:
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).

链接:https://leetcode.com/problems/3sum-closest/
分析:这题的思路还是和3Sum一样,先排序,然后遍历固定num[k], 在[k+1, n-1]区间内两边往中间扫描找到另外两个值,为clost1 = num[k]+num[i]+num[j],,当clost1>target时,我们应该减小clost1 即j--,否则clost1 < target时,我们应该增大clost1,即i++ ,这里面就会有一个局部clost1使得abs(clost1-target)达到最小。局部最小值再与外围的k变化比较得到全局最小值。代码如下:

#define Max 0x7fffffff
class Solution {
public:
    int threeSumClosest(vector<int> &num, int target) {
        if (num.size() < 3) return 0;
        sort(num.begin(), num.end());
        int minResult = Max;
        int sum = Max;
        for(int k = 0; k < num.size() - 2; k++){
            int i = k+1, j = num.size()-1;
            int temp = Max;
            int result;
            while(i < j){
                int clost1 = num[i]+num[j]+num[k];
                if(temp > abs(clost1-target)){   // num[k]固定 找到局部最小的 
                    temp = abs(clost1-target);
                    result = clost1;
                }
                if(clost1 > target) j--;           // 当clost1大于target 则j-- 会减小之间的差距
                else if(clost1 < target) i++;      // 当clost1 小于target 则i++  会减小之间的差距
                else return clost1;               // 如果等于0 则返回
            }
            if(minResult > temp){      // k在变动  找到全局最近的
                minResult = temp;
                sum = result;
            }
        }
      return sum;  
    }
};


四: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:

Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d)
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)


链接:https://leetcode.com/problems/4sum/

分析:此题思路是一样的,4Sum就需要先固定两个元素,然后再用双指针+两边夹逼遍历的思想,时间复杂度为O(N^3)

class Solution {
public:
    vector<vector<int> > fourSum(vector<int> &num, int target) {
        vector<vector<int> > result;
        int n = num.size();
        if(n == 0) return result;
        sort(num.begin(), num.end());
        for(int p = 0; p <= n-4; p++){
            int a = num[p];          // 先规定第一个元素
            if(p-1 >= 0 && a == num[p-1])continue;   // 避免重复
            for(int q = n-1; q >= 3; q--){ 
                int b = num[q];        // 从后固定第二个元素
                if(q+1 <= n-1 && num[q+1]==b) continue;      // 避免重复
                int i = p+1, j = q-1;
                while(i < j){                    // 然后使用双指针+两边遍历的思想
                    int c=num[i], d=num[j];
                    if(a+b+c+d == target){
                        vector<int> tmp;
                        tmp.push_back(a);
                        tmp.push_back(c);
                        tmp.push_back(d);
                        tmp.push_back(b);
                        result.push_back(tmp);
                        while(i+1 < j && num[i+1]==c) i++;    // 避免重复
                        while(j-1 > i && num[j-1]==d) j--;
                        i++;
                        j--;
                    }
                    else if(a+b+c+d < target) i++;
                    else j--;
                }
            }
        }
        return result;
    }
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: