1. 两数之和
题目描述
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
示例 1:
1 2 3
| 输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
|
示例 2:
1 2
| 输入:nums = [3,2,4], target = 6 输出:[1,2]
|
示例 3:
1 2
| 输入:nums = [3,3], target = 6 输出:[0,1]
|
题解
1. 暴力
两次枚举,需要两次遍历,时间复杂度O(n²)
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Solution { public int[] twoSum(int[] nums, int target) { for (int i = 0; i < n; i++) { for (int j = i + 1; j < nums.length; j++) { if (nums[i] + nums[j] == target) { return new int[]{i, j}; } } } } }
|
ACM模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); int target = scanner.nextInt();
int[] nums = new int[n]; for (int i = 0; i < n; i++) { nums[i] = scanner.nextInt(); }
for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (nums[i] + nums[j] == target) { System.out.println(i + " " + j); return; } } } } }
|
2. 双指针
由于题目所给数组已经有序排列,则可以从两端开始取数,如果两端取数的和大于目标,则说明:和为目标值的两个元素一定在两端范围内,时间复杂度O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution { public int[] twoSum(int[] nums, int target) { int left = 0; int right = nums.length - 1; while (true) { if ((nums[right] + nums[left]) > target){ right--; } else if((nums[right] + nums[left]) < target){ left++; } else{ return new int[]{left, right}; } } } }
|
3. HashMap
使用HashMap的key作为元素值,value作为数组下标
把元素存到HashMap中,去查找HashMap中与元素相加等于target的key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import java.util.HashMap;
class Solution { public int[] twoSum(int[] nums, int target) { HashMap<Integer, Integer> map = new HashMap<>(); for(int i = 0; i < nums.length; i++){ int need = target - nums[i]; if(map.containsKey(need)){ return new int[]{map.get(need), i}; } map.put(nums[i], i); } return new int[]{-1,-1}; } }
|
心得
很多涉及到「两个变量」的题目,都可以枚举其中一个变量,把它当成常量看待,从而转化成「一个变量」的问题。
代码实现时,通常来说「枚举右,寻找左」是更加好写的。
2. 三数之和
题目描述
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
**注意:**答案中不可以包含重复的三元组。
示例 1:
1 2 3 4 5 6 7 8
| 输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 解释: nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。 注意,输出的顺序和三元组的顺序并不重要。
|
示例 2:
1 2 3
| 输入:nums = [0,1,1] 输出:[] 解释:唯一可能的三元组和不为 0 。
|
示例 3:
1 2 3
| 输入:nums = [0,0,0] 输出:[[0,0,0]] 解释:唯一可能的三元组和为 0 。
|
题解
- 输出的顺序和三元组的顺序并不重要。则可以规定
i < j < k
- 答案中不可以包含重复的三元组。只需要跳过重复的枚举数(确保三个数字中必有一个不同)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| class Solution { public List<List<Integer>> threeSum(int[] nums) { Arrays.sort(nums); int n = nums.length; List<List<Integer>> list = new ArrayList(); for(int i = 0; i < nums.length-2; i++){ if (i > 0 && nums[i] == nums[i-1]) { continue; } if ((nums[i] + nums[i+1] + nums[i+2]) > 0) { break; } if ((nums[i] + nums[n-1] + nums[n-2]) < 0) { continue; }
int j = i + 1; int k = n - 1; while(j < k) { int s = nums[i] + nums[j] + nums[k]; if (s > 0) { k--; } else if (s < 0) { j++; } else { list.add(Arrays.asList(nums[i], nums[j], nums[k])); j++; while(j < k && nums[j] == nums[j-1]) { j++; } k--; while(k > j && nums[k] == nums[k+1]) { k--; } } } } return list; } }
|
思路
- 首先对数组进行排序,排序后固定一个数 nums[i],再使用左右指针指向 nums[i]后面的两端,数字分别为 nums[L] 和 nums[R],计算三个数的和 sum 判断是否满足为 0,满足则添加进结果集
- 如果 nums[i]大于 0,则三数之和必然无法等于 0,结束循环
- 如果 nums[i] == nums[i−1],则说明该数字重复,会导致结果重复,所以应该跳过
- 当 sum == 0 时,nums[L] == nums[L+1] 则会导致结果重复,应该跳过,L++
- 当 sum == 0 时,nums[R] == nums[R−1] 则会导致结果重复,应该跳过,R−−
时间复杂度:O(n ²),n 为数组长度