LeetCode总结:2Sum/3Sum/4Sum问题

K Sum问题:

  给定一个数组nums和一个实数target,在数组nums中给出所有的K个数,使K个数之和为target。

1. LeetCode 1:2 Sum

解法1:
  暴力求解:先固定一个数,然后遍历数组,找到另一个数,使两数之和为target。

vector<int> twoSum(vector<int>& nums, int target) {
	vector<int> idx;

	size_t n = nums.size();
	for (size_t i = 0; i < n; i++) {
		for (size_t j = i + 1; j < n; j++) {
			if (nums[i] + nums[j] == target) {
				idx.push_back(i);
				idx.push_back(j);
				return idx;
			}
		}
	}
	return idx;
}

解法二:
  双指针:先对原数组中的数进行从小到大排序(使用algorithm头文件中的sort函数,时间为O(nlgn))。然后定义两个指针分别指向数组的头部和尾部。接着两个指针分别移动,遍历整个数组:如果两个指针指向的元素之和sum小于target,则移动头指针;若大于target,则移动尾指针;若等于target,则找到了满足要求的解。需要注意的是,leetcode的原题中要求的是返回元素的下标,故找到相应的元素后,还需要在元数组中找到对应的下标。

vector<int> twoSum(vector<int>& nums, int target) {
	vector<int> num = nums;
	sort(num.begin(), num.end());	
	
	vector<int> idx;
	int i = 0;	
	int j = num.size() - 1;
	while (i < j) {
		int sum = num[i] + num[j];
		if (sum == target) {
			int n = nums.size();
			for (int k = 0; k < n; k++) {	// 在原数组中寻找解的下标
				if (nums[k] == num[i]) {
					idx.push_back(k);
				}
				else if (nums[k] == num[j]) {
					idx.push_back(k);
				}

				if (idx.size() == 2) {
					break;
				}
			}
			break;		// 只有一对组合满足要求,故找到一组解后就可结束遍历
		}
		else if(sum < target) {
			i++;
		}
		else {
			j--;
		}
	}
	return idx;
}

解法三:
  哈希表:使用map实现哈希表。遍历数组,在map中查找每一个元素对应的解:若解不在map中,说明该元素无解或者还没有找到解,则将该元素加入到map中。故一次遍历就一定能找到相应的解。

vector<int> twoSum(vector<int>& nums, int target) {
	vector<int> res;
	unordered_map<int, int> hashs;

	int n = nums.size();
	for (int i = 0; i < n; i++) {
		if (hashs.end() != hashs.find(target - nums[i])) {
			res.push_back(hashs[target - nums[i]]);
			res.push_back(i);
			return res;
		}
		hashs[nums[i]] = i;
	}
	return res;
}

2. LeetCode15:3 Sum

  本题要求返回所有的元素,而不是元素的下标。依然可以使用暴力求解,但显然效率太低。我们可以先固定一个数,然后将3 Sum问题转化为2 Sum问题。需要注意的是,本题需要排除重复解。

vector<vector<int>> threeSum(vector<int>& nums) {
	vector<vector<int>> ans;
	size_t n = nums.size();	

	if (n < 3) return ans;

	sort(nums.begin(), nums.end());		// 将数组从小到大排序
	for (size_t i = 0; i < n - 2; i++) {
		/* 在nums[i+1, n-1]查找和为-nums[i]的两个数 */
		int begin = i + 1;
		int end = n - 1;
		while (begin < end) {
			int sum = nums[begin] + nums[end];
			if (sum == -nums[i]) {
				vector<int> res = { nums[i], nums[begin], nums[end] };	// 一组解
				ans.push_back(res);
				begin++;
				end--;

				/* 排除重复解 */
				while (begin < end && nums[begin] == nums[begin - 1]) {
					begin++;
				}
				while (begin < end && nums[end] == nums[end + 1]) {
					end--;
				}
			}
			else if (sum < -nums[i]) {
				begin++;
			}
			else {
				end--;
			}
		}

		/* 排除重复解 */
		while (i < n - 2 && nums[i] == nums[i + 1]) {
			i++;
		}
	}
	return ans;
}

3. LeetCode16:3 Sum Closest

  本题是3 Sum问题的变形,即若nums中没有满足三数之和为target的解,就返回三数之和最接近target的解。本题思路与3 Sum问题相同,只不过需要计算当前的三数之和sum与target之间的距离dis,并更新最小距离mdis。

int threeSumClosest(vector<int>& nums, int target) {
	if (nums.size() < 3) return NULL;

	int closest;
	int mdis = INT_MAX;		// 最小距离
	int n = nums.size();
	sort(nums.begin(), nums.end());

	for (int i = 0; i < n - 2; i++) {
		int begin = i + 1;
		int end = n - 1;
		while (begin < end) {
			int sum = nums[i] + nums[begin] + nums[end];
			if (sum == target) {
				return target;
			}
			else if(sum < target) {
				int dis = target - sum;		// 当前和与目标值的距离
				if (dis < mdis) {			// 更新最小距离
					mdis = dis;
					closest = sum;			// 记录最接近目标值的和
				}
				begin++;
			}
			else {
				int dis = sum - target;
				if (dis < mdis) {
					mdis = dis;
					closest = sum;
				}
				end--;
			}

		}
	}
	return closest;
}

4. LeetCode18:4 Sum

  4 Sum问题的思路与3 Sum问题的思路相同,只不过3 Sum问题先固定一个数,4 Sum问题需要先固定两个数,体现在程序中就是多套一层for循环。同样需要注意的是重复解的问题:对于K个数相加,k - 1个加数确定,和也确定,则最后一个加数就一定是确定的,因此需要对k - 1个加数进行判重。

vector<vector<int>> fourSum(vector<int>& nums, int target) {
	vector<vector<int>> res;

	sort(nums.begin(), nums.end());

	int len = nums.size();
	for (int i = 0; i < len - 3; i++) {
		for (int j = i + 1; j < len - 2; j++) {
			int begin = j + 1, end = len - 1;

			while (begin < end) {
				int sum = nums[i] + nums[j] + nums[begin] + nums[end];
				if (sum == target) {
					vector<int> tmp = { nums[i], nums[j], nums[begin], nums[end] };
					res.push_back(tmp);
					begin++;
					end--;

					/* 排除重复解 */
					while (begin < end && nums[begin] == nums[begin -1]) {
						begin++;
					}
					while (begin < end && nums[end] == nums[end + 1]) {
						end--;
					}
				}
				else if(sum > target) {
					end--;
				}
				else {
					begin++;
				}
			}

			/* 排除重复解 */
			while (j < len -2 && nums[j] == nums[j + 1]) {
				j++;
			}
		}

		/* 排除重复解 */
		while (i < len - 3 && nums[i] == nums[i + 1]) {
			i++;
		}
	}

	return res;
}

5. LeetCode454:4 Sum II

  本题严格来说已经不是K Sum问题,因为本题要求在四个等长的额数组中找到target为0的解的个数。本题现在遍历两个数组,将每一个可能的和记录在map中,如果出现相同的数,则在map中将对应的value再加1。再遍历剩余两个数组,计算任意两个数的和,并判断能否与map中的数组成target为0的解。用变量count记录解的个数,若存在满足要求的解,则count加上map中的变量对应的值。

    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
        int len = A.size();
        int count = 0;
        unordered_map<int, int> hash;

        for (int i = 0; i < len; i++) {
            for (int j = 0; j < len; j++) {
                hash[A[i] + B[j]]++;
            }
        }

        for (int i = 0; i < len; i++) {
            for (int j = 0; j < len; j++) {
                if (hash.find(-C[i] - D[j]) != hash.end()) {
                    count += hash[-C[i] - D[j]];
                }
            }
        }

        return count;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值