《LeetCode-0001》 两数之和-TwoSum

《LeetCode-0001》 两数之和-TwoSum

https://leetcode-cn.com/problems/two-sum/

题目

给定一个整数数组 nums 和一个目标值 target ,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例

1
2
3
4
给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

思路

暴力

最简单的一种思路,两层循环,两两相加后与target比较。但是循环的起点不同,第一个元素和后面的每个元素进行比较,第二个元素也是和它之后的元素进行比较,所以外层的循环起点为0,内层的循环起点则是由外层决定,若外层为i,则内层循环起点为i+1,这样也可以保证,不重复利用数组中同样的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] result = new int[2];
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
result[0] = i;
result[1] = j;
}
}
}
return result;
}
}

哈希表

暴力方法之所以要循环两次,是因为我们需要找两个数的下标,采用哈希表的方法,在循环过程中存储下标,可以省去嵌套一次循环,但是找结果的方式与暴力方式不太一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
}

上面的暴力方法,是从前往后找另外一半,找到立刻返回,比如

1
int nums[] = new int[]{2, 7, 11, 15};

若target目标值为17,执行过程:

i,j 计算结果 是否匹配
0,1 9 NO
0,2 13 NO
0,3 17 YES

若是用哈希表的方式,可以理解为从当前循环点往前找另外一半,利用的是2+3=5,3+2=5,加法的交换律,不管从前往后还是从后往前,都是OK的。
执行过程:

i 是否匹配 单次循环结束后map
0 NO (2, 0)
1 NO (2, 0) , (7,1)
2 NO (2,0) , (7,1) , (11,3)
3 YES

总结

  1. 某些场景下,暴力方式可能速度更快,但是第二种方式整体时间复杂度低。
  2. 第二种采用HashMap 的方式,当然也可以采用其他存储数值和下标的式。
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×