注:本文的实现思路主要是基于JS(JavaScript),涉及到的一些函数都是JS中的自带函数
题目描述
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案
输入示例
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
输入:nums = [3,2,4], target = 6
输出:[1,2]
输入:nums = [3,3], target = 6
输出:[0,1]
解题思路
针对这种问题,本文提出了三种解决方案
- 暴力破解法,实践证明这个方法也是能通过所有测试用例的。思路是对于数组中的任一元素
x
,查找能否找到target - x
,若找到,则返回下标。两层for循环即可简单解决问题。显然,时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
var twoSum = function(nums, target) {
for (var i = 0; i < nums.length; i++) {
for (var j = i + 1; j < nums.length; j++) {
if(nums[i] + nums[j] === target)
return [i, j]
}
}
};
- 先对数组进行排序,然后使用两个指针同时从升序排序后数组的两端向中间移动,直到找到满足两个指针所指元素之和为
target
。由于使用了系统默认的排序算法,其时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)。需要说明的是,由于排序后会导致元素的下标发生变化,所以需要使用一个变量记录原始数组,然后再使用一个循环遍历出该数值对应原始数组的下标。下面的代码看似很乱,其实思路很简单。
var twoSum = function(nums, target) {
// 此处不能 temp = nums,否则nums变化会导致temp的变化
const temp = [...nums]
// 升序排序
nums.sort((a,b)=> a-b)
// 定义左右两个指针
let left = 0, right = nums.length-1
while(left !== right){
//两个元素相加小于 target,说明 left太小,需要增加
if (nums[left] + nums[right] < target) left ++
//两个元素相加大于 target,说明 right太大,需要减小
else if (nums[left] + nums[right] > target) right --
else break
}
// 两个变量记录左右指针是否在原数组中找到了下标
let l_flag = false, r_flag = false
for (var i = 0; i < temp.length; i++) {
if (l_flag && r_flag) break
if (nums[left] === temp[i] && !l_flag) {left = i; l_flag = true}
else if(nums[right] === temp[i] && !r_flag) {right = i; r_flag = true}
}
return [left, right]
};
- 使用哈希存储的方式,与其说是哈希存储,不如说是使用对象存储,其key是数组的元素值,value是数组的元素下标。这种方法的时间复杂度和空间复杂度都是 O ( N ) O(N) O(N)。
值得注意的是,一定要先判断
target - nums[i]
是否已经在对象的属性中,若不存在,再向对象中添加对应的key和value。其原因也很简单,对象中的属性具有唯一性,要是直接把nums中的所有元素都添加对象中,再进行判断,重复的元素就回被覆盖。举个反例,nums = [1,1], target=2
var twoSum = function(nums, target) {
let obj = {}
for (var i = 0; i < nums.length; i++) {
if (target - nums[i] in obj)
return[i, obj[target - nums[i]]]
obj[nums[i]] = i
}
};