[LeetCode]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, and you may not use the same element twice.
测试案例
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
思路一
- 先将数组排序。
- 从左至右依次遍历每个元素,同时在其右边的子序列中采用二分法查找 target 与当前元素的差值。
代码如下
class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length, pos;
int[] res = new int[2];
Arrays.sort(nums);
for(int i = 0; i < n - 1; i++){
if((pos = Arrays.binarySearch(nums, i + 1, n, target - nums[i])) > -1){
res[0] = nums[i];
res[1] = nums[pos];
break;
}
}
return res;
}
}
思路二
- 先将数组排序
- 从左至右依次遍历每个元素,同时在其右边的子序列中采用二分法查找 target 与当前元素的差值。由于元素值依次递增,从而,下次二分查找的范围必定在上次二分查找的范围内。所以,可以保存上次二分查找返回的下标。作为下次二分查找的右边界。
代码如下
class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length, pos = n;
int[] res = new int[2];
Arrays.sort(nums);
for(int i = 0; i < n - 1; i++){
if((pos = Arrays.binarySearch(nums, i + 1, pos, target - nums[i])) > -1){
res[0] = nums[i];
res[1] = nums[pos];
break;
}
//当pos = i + 1 时,说明后面的元素均不满足
else if((pos = -(pos + 1)) == i + 1){
break;
}
}
return res;
}
}
思路三
-
先将数组排序。
-
定义两个下标,start 和 end,初始时,(start = 0, end = n - 1)。
-
比较 start 和 end 位置处的元素之和与 target 的大小。
当 nums[start] + nums[end] == target 时,循环结束,找到结果。
当 nums[start] + nums[end] < target 时,start++。
当 nums[start] + nums[end] > target 时,end--。
代码如下
class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length, temp, start = 0, end = n - 1;
int[] res = new int[2];
Arrays.sort(nums);
while(start < end){
if((temp = nums[start] + nums[end]) > target){
end--;
}
else if(temp == target){
res[0] = nums[start];
res[1] = nums[end];
break;
}
else{
start++;
}
}
return res;
}
}
[LeetCode 15] 3Sum
题目
//题目被简化掉了返回所有组合的结果,仅返回一组。原题在后面
Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0?
测试案例
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is: [-1, 0, 1]
思路
利用2Sum,先将数组进行排序,然后从左至右依次遍历每个元素,对于每个元素,在其右边的子数组中调用2Sum。
代码如下
class Solution {
public List<Integer> threeSum(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
List<Integer> res = null;
for(int i = 0; i < n -2; i++){
if((res = twoSum(nums, i + 1, n - 1, -nums[i])) != null){
res.add(nums[i]);
break;
}
}
return res;
}
List<Integer> twoSum(int[] nums, int start, int end, int target){
int temp;
List<Integer> res = null;
while(start < end){
if((temp = nums[start] + nums[end]) < target){
start++;
}
else if(temp > target){
end--;
}
else{
res = new ArrayList<>(3);
res.add(nums[start]);
res.add(nums[end]);
break;
}
}
return res;
}
}
原题
Given an array nums of n integers, are there elements a, b, c in nums 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.
测试案例
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路
- 在上面3sum的基础上输出所有结果,同时要去重。
- 去重的关键在于从左至右遍历每个元素时,如果与前面的元素相同,则不考虑当前元素。
- 同时为了输出所有结果,在2sum中,也需要输出所有满足条件的结果及去重。
- 在2sum中输出所有结果的思路是:当找到一对结果时,左右下标同时移动。
- 在2sum中去重的思路是:每次下标移动时,如果和前继元素相同,则继续移动。
这是常用的去重思路。
代码如下
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
List<List<Integer>> res = new LinkedList<>(), temp;
Arrays.sort(nums);
for(int i = 0; i < n - 2; i++){
//3sum去重
if(i == 0 || nums[i] != nums[i - 1]){
res.addAll(twoSum(nums, i + 1, n - 1, -nums[i]));
}
}
return res;
}
List<List<Integer>> twoSum(int[] nums, int start, int end, int target){
int temp;
List<Integer> list;
List<List<Integer>> res = new LinkedList<>();
while(start < end){
if((temp = nums[start] + nums[end]) == target){
list = new ArrayList<>(3);
list.add(nums[start]);
list.add(nums[end]);
list.add(-target);
res.add(list);
}
//2sum去重
if(temp <= target){
while(++start < end && nums[start] == nums[start - 1]);
}
//2sum去重
if(temp >= target){
while(--end > start && nums[end] == nums[end + 1]);
}
}
return res;
}
}
[LeetCode 18] 4Sum
题目
Given an array nums of n integers and an integer target, are there elements a, b, c, and d in nums 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.
测试案例
Given array nums = [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的思路,在3sum外面加一个循环。时间复杂度为 (O(n^3))。
代码
class Solution {
List<List<Integer>> result = new LinkedList<>();
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
int length = nums.length, pre;
for(int i = 0;i < length - 3; i++){
if(i > 0 && nums[i] == nums[i - 1]){
continue;
}
threeSum(nums,target - nums[i], i + 1, nums[i]);
}
return result;
}
public void threeSum(int[] nums, int target, int start, int num1){
int n = nums.length;
for(int i = start; i < n - 2; i++){
if(i > start && nums[i] == nums[i - 1]){
continue;
}
twosum(nums, i + 1, target - nums[i], num1, nums[i]);
}
}
void twosum(int[] nums, int start, int target, int num1, int num2){
int temp, end = nums.length - 1;
List<Integer> list;
while(start < end){
if((temp = nums[start] + nums[end]) == target){
list = new ArrayList<>(4);
list.add(num1);
list.add(num2);
list.add(nums[start]);
list.add(nums[end]);
result.add(list);
}
if(temp >= target){
while(--end > start && nums[end] == nums[end + 1]);
}
if(temp <= target){
while(++start < end && nums[start] == nums[start - 1]);
}
}
}
}
思路2
- 将任意两点的和作为key,value为一个 list ,里面存放和值相同的两点的下标,放入 HashMap 中。时间复杂度为 (O(n^2))。
- 然后两层循环遍历每一对元素,从 target - 和值 对应的 value 中找出满足要求的结点。在 list 中查找的时间复杂度未知。
kSum
题目
Given an unsorted array, determine if there are K elements that sum up to SUM.
思路1
延续上面思路,在 2sum 外面加 (k - 2) 层循环,时间复杂度为 (O(n^{k - 1}))。
思路2
将 ksum 问题看作一个二维背包问题。一个约束为 sum,另一个约束为 k。且要求出同时满足的所有所有情况。
代码如下
class Solution{
int min;
int n;
boolean[][][] record;
LinkedList<Integer> stack = new LinkedList<>();
List<List<Integer>> res = new LinkedList<>();
public List<List<Integer>> kSum(int[] nums, int k, int sum){
n = nums.length;
Arrays.sort(nums);
min = nums[0];
sum -= min * k;
//转变为非负数
int index = 0;
while(index < n && (nums[index] - min <= sum)){
nums[index++] -= min;
}
n = index;
record = new boolean[n + 1][k + 1][sum + 1];
record[0][0][0] = true;
for(int i = 1; i <= n; i++){
for(int j = 0; j <= k; j++){
for(int p = 0; p <= sum; p++){
record[i][j][p] = record[i - 1][j][p];
if(!(record[i][j][p]) && j > 0 && p >= nums[i - 1]){
record[i][j][p] = record[i - 1][j - 1][p - nums[i - 1]];
}
}
}
}
rebuild(nums, n, k, sum);
return res;
}
void rebuild(int[] nums, int index, int k, int sum){
if(k <= 0){
res.add(new LinkedList<>(stack));
return;
}
if(record[index - 1][k][sum]){
rebuild(nums, index - 1, k, sum);
}
//选择当前结点,需要进行过滤
if(sum >= nums[index - 1] && record[index - 1][k - 1][sum - nums[index - 1]]){
stack.push(nums[index - 1] + min);
rebuild(nums, index - 1, k - 1, sum - nums[index - 1]);
stack.pop();
}
}
}