持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情
每日刷题 2022.06.06
- leetcode原题链接:leetcode.cn/problems/tw…
- 难度:简单
- 方法:哈希映射、二分
题目
- 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
- 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
- 你可以按任意顺序返回答案。
示例
- 示例1
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
- 示例2
输入: nums = [3,2,4], target = 6
输出: [1,2]
- 示例3
输入: nums = [3,3], target = 6
输出: [0,1]
提示
- 2 <= nums.length <= 10^4
- -10^9 <= nums[i] <= 10^9
- -10^9 <= target <= 10^9
- 只会存在一个有效答案
- 进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?
解题思路
- !!每种输出只会对应一个答案!!
第一种做法:二分
- 分析:在
nums数组两个数,加和起来等于target,返回这两个数对应的下标。注意⚠️:同一个数只能使用一次- 同一个数,表示的是下标相同值也相同的数不能一起使用,下标不同但是值相同是可以一起使用的。
- 常规的做法:对于数组
nums中的每一个位置i,都向后遍历j,查找nums[i] + nums[j] = target的元素。- 分析时间复杂度:
o(n ^ 2)
- 分析时间复杂度:
- 进阶的做法:固定一个元素查找另一个元素,且数组
nums可以排序成为有序数组,那么就可以使用二分查找,来查找到另一个元素。- 因为二分查找的时间复杂度:
o(logn),再加上我们最坏要对数组nums中的每个数都遍历一遍,总的时间复杂度o(n logn)
- 因为二分查找的时间复杂度:
技巧
- 因为对
nums数组进行排序后,原本的下标就会被打乱,而我们需要返回的就是原本的下标,想着能不能使用map来存储下标和值之间的映射关系,发现不太行,因为数组nums中会存在多个相同的值,并不是一一对应的,因此排除掉map; - 后来就想到了使用自定义排序,如果我们创建一个新的数组
idx,将其按照nums数组中的值的大小进行排序,那么就可以得到nums排序后每个位置对应的下标,形成一一对应。 - 举例
nums = [3, 2, 4]
对应的下标:0, 1, 2
创建新的数组idx = [0, 1, 2]
对idx数组按照nums数组中的数值大小进行自定义升序排序后:
idx = [1, 0, 2]
再对nums数组进行升序排序后:
nums = [2, 3, 4]
观察此时的idx和nums数组,nums数组中的值对应的唯一的下标就一一对应在idx数组中
第二种做法:哈希映射
- 并不是一开始就将所有的数组
nums中的值和下标对应的关系存储到map集合中,而是将数组nums中位置i之前的存入到map集合中。 - 这种做法也是比较巧妙的,对于数组
nums的每一个元素nums[i],往前查找有没有符合target - nums[i]的,使用map数据结构存储:当前位置i前面的值和下标的对应关系,直接从map中查找即可。- 两个相同的数但不同下标,
3 + 3 = 6这样的,当前位于i,前面或者后面寻找一个即可,不会被覆盖
- 两个相同的数但不同下标,
- 这种做法只适用于两数之和,如果是三数之和就不行了,因为
map中的元素并不是一一对应的,可能会存在重复覆盖的情况 - 举例
nums = [3,5,3,3], target = 9
// 此时使用map集合记录每个数值对应的下标的时候,就会被后面来的覆盖掉
AC代码
- 第一种做法:二分查找
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
// nlogn
// console.log(nums)
let n = nums.length, ans = new Array(2);
let index = new Array(n);
for(let i = 0; i < n; i++) {
index[i] = i;
}
// console.log(index)
index.sort((a, b) => {
return nums[a] - nums[b];
});
// console.log(index)
nums.sort((a, b) => a - b);
function binary (t) {
// 返回值是找的匹配的下标
let l = -1, r = n;
while(l + 1 != r) {
let mid = l + parseInt((r - l) / 2);
// console.log(l,r,mid)
if(nums[mid] < t){
l = mid;
}else {
r = mid;
}
}
return r;
}
for(let i = 0; i < n; i++) {
let tempt = target - nums[i];
// 小于的时候执行
let idx = binary(tempt);
// 如果没有找到
// console.log(i,nums[i], idx)
if(idx != n && nums[idx] == tempt && idx != i) {
ans[0] = index[i], ans[1] = index[idx];
return ans;
}
}
return ans;
};
- 第二种做法:哈希映射
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let map = new Map(), n = nums.length;
for(let i = 0; i < n; i++) {
let cha = target - nums[i];
if(map.has(cha)) {
return [map.get(cha), i];
}
map.set(nums[i], i);
}
};
总结
- 简单的题目,要更注重思考的过程