Two Sum
- 题意
给定一个数组,和指定一个目标和。从数组中选择两个数满足和为目标和。保证有且只有一个解。每个元素只可以用一次。 - 思路
Hash表快速查询值是否在数组中存在。
枚举一个数,查询另一个数是否存在。
注意:虽然一个元素只可以使用一次,但是数组中可以出现重复的元素。 - 复杂度
T(N)=O(N),M(N)=O(N) - 结果
Runtime: 5 ms, faster than 70.02% of Java online submissions for Two Sum.
Memory Usage: 37.5 MB, less than 100.00% of Java online submissions for Two Sum.
即(5ms, 70.02%, 37.5MB, 100.0%) - 源码
class Solution {
public int[] twoSum(int[] nums, int target) {
int []ans = new int[2];
Map<Integer,Integer> m = new HashMap<Integer,Integer>();
for (int i = 0;i < nums.length; ++i) {
if (m.containsKey(nums[i])) {
if (nums[i]*2 == target) {
ans[0] = m.get(nums[i]);
ans[1] = i;
return ans;
}
} else {
m.put(nums[i], i);
}
}
for (int i = 0;i < nums.length; ++i)
if (nums[i]*2 != target && m.containsKey(target-nums[i])) {
ans[0] = i;
ans[1] = m.get(target-nums[i]);
break;
}
return ans;
}
}
Two Sum II
- 题意
题意不变。只是输入数组有序。另外返回的下标是基于1。 - 思路
由于有序,维护一左一右两个指针,所指数的和过小,则左指针右移一格;过大,右指针左移一格;等于则返回结果。 - 复杂度
T(N)=O(N),M(N)=O(1) - 结果
(0ms, 100.0%, 36.9 MB, 100.0%) - 源码
class Solution {
public int[] twoSum(int[] nums, int target) {
int l = 0;
int r = nums.length-1;
int t;
while (true) {
t = nums[l]+nums[r];
if (t < target)
++l;
else if (t > target)
--r;
else
return new int[] {l+1,r+1};
}
}
}
3Sum
- 题意
给定一个数组,从中选三个元素a,b,c.使得a+b+c=0.给出所有的可能的三元组(unique triplets)的集合。 - 思路1
原数组分为负数和非负数两个数组。则答案只有这些可能: - 负+非负+非负
- 非负+负+负
- 非负+非负+非负(实际上这个只能是3个0)
3个0的特殊判断。
情况1枚举负数,转换为非负数数组中的two Sum问题。
情况2同理。
- 思路2
先排序,直接枚举一个数。转化为Two Sum II问题。 - 复杂度
思路1 T(N)=O(N2),M(N)=O(N)
思路2 T(N)=O(N2),M(N)=O(1)
且考虑到思路2变成复杂度更低。毫无疑问选思路2. - 结果
(37ms, 99.61%, 48.5MB, 100.00%) - 源码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int target = 0;
List<List<Integer>> ans = new ArrayList<List<Integer>>();
Arrays.sort(nums);
int len = nums.length;
for (int i = 0; i+3 <= len; ++i) {
if (nums[i]*3 > target)
break;
if (i > 0 && nums[i] == nums[i-1])
continue;
int part_target = target-nums[i];
int range[] = {i+1,len-1};
while (range[0] < range[1]) {
twoSum(nums,part_target,range);
if (range[0] >= range[1])
break;
List<Integer> l = new ArrayList<Integer>();
l.add(nums[i]);
l.add(nums[range[0]]);
l.add(nums[range[1]]);
ans.add(l);
do{
++range[0];
}while(range[0] < range[1] && nums[range[0]] == nums[range[0]-1]);
}
}
return ans;
}
public void twoSum(int[] nums, int target, int []range) {
int t;
while (range[0] < range[1]) {
t = nums[range[0]]+nums[range[1]];
if (t < target)
++range[0];
else if (t > target)
--range[1];
else
return;
}
}
}
4Sum
- 题意
类似于3Sum.只是变成4个数了。并且目标和不是固定的0,而是通过输入参数指定。 - 思路
直接循环枚举,注意控制条件以免重复。
其实应该写搜索的,但是枚举更加无脑暴力,就直接敲了。 - 复杂度
T(N)=O(N3),M(N)=O(1) - 结果
(19ms, 89.95%, 40MB, 100.00%) - 源码
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
Arrays.sort(nums);
int len = nums.length;
for (int i = 0; i+4 <= len; ++i) {
if (nums[i]*4 > target)
break;
if (i > 0 && nums[i] == nums[i-1])
continue;
for (int j = i+1; j+3 <= len; ++j) {
if (nums[i]+nums[j]*3 > target)
break;
if (j > i+1 && nums[j] == nums[j-1])
continue;
int part_target = target-nums[i]-nums[j];
int range[] = {j+1,len-1};
while (range[0] < range[1]) {
twoSum(nums,part_target,range);
if (range[0] >= range[1])
break;
List<Integer> l = new ArrayList<Integer>();
l.add(nums[i]);
l.add(nums[j]);
l.add(nums[range[0]]);
l.add(nums[range[1]]);
ans.add(l);
do{
++range[0];
}while(range[0] < range[1] && nums[range[0]] == nums[range[0]-1]);
}
}
}
return ans;
}
public void twoSum(int[] nums, int target, int []range) {
int t;
while (range[0] < range[1]) {
t = nums[range[0]]+nums[range[1]];
if (t < target)
++range[0];
else if (t > target)
--range[1];
else
return;
}
}
}