一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
前情提要
咱们先看看需求:先给你一个整数值,然后让你在一个数组中找到这个整数值分解后的两个值。如果找到了那就可以直接返回。
ps:
- 每种输入只会对应一个答案
- 数组中同一个元素在答案里不能重复出现
- 不用在意返回值顺序问题 tips:
2 <= nums.length <= 104-109 <= nums[i] <= 109-109 <= target <= 109- 只会存在一个有效答案
题目分析
- 每种输入只对应一个答案:也就是说在数组中所有的元素都是不一样的,不存在重复的数据。应该是为了避免两个相等的数与另一个数相加,或者与另外两个相等的数相加等于给定的整数值,那么此时就存在多个答案,故而为了保证答案的唯一性,才有了这个条件。
- 数组中同一个元素在答案中不能重复出现:这个条件应该是为了防止我们返回给定数值的一半的那个值,然后返回两次。说明不能返回同一个数值的角标两次。
- 只会存在一个有效答案:因为只存在一个有效答案,所以我们在遍历时,不需要将数组全部遍历一遍,而是找到答案就可以立马通过return结束循环,避免不必要的循环造成时间和空间的消耗。
第一种方案:
思路:遍历数组,用给定的整数减去每个数,然后调用lastIndexOf方法获取差数在数组中的下标
代码:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
function twoSum(nums, target) {
for(let i = 0; i < nums.length; i++){
if(nums.lastIndexOf(target-nums[i])!=-1 && nums.lastIndexOf(target-nums[i]) != i){
return [i, nums.lastIndexOf(target-nums[i])]
}
}
}
提交情况:
第二种方案
思路:先创建一个空对象,再遍历数组,每次遍历都去查验用给定的整数减去每个数是否是创建对象的键,如果是则返回当前的坐标和对像键所对应的值。如果不存在,则将当前的元素作为键,角标作为值赋给创建的对象。
代码:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
function twoSum(nums, target) {
let obj = {}
for(let i = 0; i < nums.length; i++){
if (String(target - nums[i]) in obj) {
return [obj[String(target - nums[i])], i]
} else {
obj[String(nums[i])] = i
}
}
}
提交情况:
第三种方案:
思路:通过双重循环,让元素依次相加得到和为给定的整数的时候就返回。
代码:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
function twoSum(nums, target) {
for (let i = 0; i < nums.length; i++) {
for ( let j = (i + 1); j < nums.length; j++ ) {
if (nums[i] + nums[j] == target) return [i,j];
}
}
}
提交情况:
总结
- 以上第一种和第二种方案其实大体的思路都是一样的,都是基于给定的整数减去每一个元素,然后去查找差值在数组中的位置。
- 第三种大体方向是通过双层遍历看两个元素的和是否与给定的值相等,因为元素只能用一次,所以第二层循环不需要从零开始。而是从外层的元素的后一个元素开始。
- 根据提交结果来看,三次的执行用时有较大的不同。第一种通过lastIndexof的方式耗时最长,之后是双重循环的耗时,最后是第二种方式的耗时是最短的。
关于时间复杂度,各位看官有什么见解?
你还有其他更好的方法吗?在评论区里发出来,让我们一起学习下。