Two Sum
题意
给定一个数组,和指定一个目标和。从数组中选择两个数满足和为目标和。保证有且只有一个解。每个元素只可以用一次。
思路
Hash表快速查询值是否在数组中存在。
枚举一个数,查询另一个数是否存在。
注意:虽然一个元素只可以使用一次,但是数组中可以出现重复的元素。
复杂度
T ( N ) = O ( N ) , M ( N ) = O ( N ) T(N)=O(N),M(N)=O(N) 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 ) T(N)=O(N),M(N)=O(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问题。
复杂度
思路1T ( N ) = O ( N 2 ) , M ( N ) = O ( N ) T(N)=O(N^2),M(N)=O(N) T ( N ) = O ( N 2 ) , M ( N ) = O ( N )
思路2T ( N ) = O ( N 2 ) , M ( N ) = O ( 1 ) T(N)=O(N^2),M(N)=O(1) T ( N ) = O ( N 2 ) , 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 ( N 3 ) , M ( N ) = O ( 1 ) T(N)=O(N^3),M(N)=O(1) T ( N ) = O ( N 3 ) , 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 ;
}
}
}