本文已参与「新人创作礼」活动,一起开启掘金创作之路。
题目链接: leetcode.com/problems/tw…
1. 题目介绍(两数和)
Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.
【Translate】: 给定一个整数数组nums
和一个整数target
,返回整形数组nums中两数相加为target的元素下标。
You may assume that each input would have exactly one solution, and you may not use the same element twice.
【Translate】: 你可以假设每个输入都恰好只有一个解决方案,并且你不会使用两次相同的元素。
You can return the answer in any order.
【Translate】: 你可以按任意顺序返回答案。
测试用例:
约束:
期望:
2. 题解
2.1 暴力穷举 — O(n2)
最简单最好用的方法无非就是暴力穷举,别管行不行,我先试一试。但其效率肯定是非常慢的,遇到大数据量的题目,就很容易歇菜了,你就等吧。我们可以看到暴力穷举的耗时很长,性能不佳,但是肯定能对,所以我们有时候可以用暴力方法作为一个对数器来检测我们新的方法是否正确,以及用来探索一些数学规律。
class Solution {
public int[] twoSum(int[] nums, int target) {
for(int i = 0; i<nums.length; i++){
for (int j = 0; j<nums.length; j++){
if(target == nums[i]+nums[j] && i != j){
return new int[] {i, j};
}
}
}
return null;
}
}
2.2 Two-pass Hash Table — O(n)
Solution中的答案,其实也没什么特别的思想,只不过换了一种存储方式来实现,HashMap在插入和删除的时候要比数组快的多,这里用HashMap先把nums中的值作为key先存了一遍,然后用target - nums[i],再判断HashMap中是否包含complement,以及complement的位置。
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement) && map.get(complement) != i) {
return new int[] { i, map.get(complement) };
}
}
// In case there is no solution, we'll just return null
return null;
}
2.3 One-pass Hash Table — O(n)
Solution中的第三种解法,是对 2.2 Two-pass Hash Table
的改进,当然说是改进,其实也没有什么特殊的,只不过是把2.2中的两个循环合在了一起,当然这样做到好处就是可以让时间缩短一半。
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);
}
// In case there is no solution, we'll just return null
return null;
}
2.4 数组左右边界 — O(n)
fredyzhang提供的解法。这个解法的核心思想就是先将原数组拷贝下来,对其进行sort()排序,这样就确保了copyNums中的数都是升序排列的了,而原数组的数据不变,不用担心到时候下标出错。之后,定义两个变量,一左一右和数组的长度对应,进入循环开始判断,然后左右逐步移动,直到找到正确的结果后结束,这样在一定程度上就能节省时间,除非运气很差,才会在最后返回结果。最后,我们把定位到的数在nums中遍历,找到它们的下标即可,不过这种方法虽然节省的时间,但是语句过长,显得有些麻烦和啰嗦,还是不是很推荐这种方式的。
public int[] twoSum(int[] nums, int target) {
int[] copyNums = Arrays.copyOf(nums, nums.length);
Arrays.sort(copyNums);
int left = 0, right = copyNums.length - 1;
int[] indices = new int[2];
while (left < right) {
int sum = copyNums[left] + copyNums[right];
if (sum == target) {
indices[0] = left;
indices[1] = right;
break;
} else if (sum < target) {
left++;
} else {
right--;
}
}
for (int i = 0; i < nums.length; i++) {
if (copyNums[indices[0]] == nums[i]) {
indices[0] = i;
break;
}
}
for (int i = nums.length - 1; i >= 0; i--) {
if (copyNums[indices[1]] == nums[i]) {
indices[1] = i;
break;
}
}
return indices;
}
3. 可参考
[1] java中sort函数的使用