啃一本算法书啃了快一年了,用嘴想一想都该只剩渣了,脑子是怎么想的???
- 真希望有一天“爷啃完了,爷不要你了,爷换一本啃”,,欸欸欸??罪过罪过!!!
- 哈哈哈
文章目录
一、 两数之和
1-1
- 两数之和【返回数组下标】使用HashMap解决就很巴适,真简单!
- 排序+双指针给n数之和铺路【返回数组元素】
- 排序之后数组下标对应的元素发生改变!!!
1-1-1 排序+双指针【返回!!数组元素!!不是数组下标】
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
// 当需要返回结果数组时
// 排序+双指针
nums.sort((a,b) => {return a-b;})
let left = 0,right = nums.length-1;
const res = [];
// 左右指针
while(left < right) {
let leftValue = nums[left],rightValue = nums[right];
let sum = leftValue + rightValue
if(sum < target){
while(left < right && nums[left] == leftValue) left++;
}
else if(sum > target) {
while (left < right && nums[right] == rightValue) right--;
}
else {
res.push(nums[left],nums[right])
while(left < right && nums[left] == leftValue ) left++;
while(left < right && nums[right] == rightValue ) right--;
}
}
return res;
};
1-1-2 HashMap 两数之和
- 用 hashMap 存储遍历过的元素和对应的索引。
- 每遍历一个元素,看看 hashMap 中是否存在满足要求的目标数字。
- 所有事情在一次遍历中完成(空间换取时间)
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
const map = new Map();
for(let i in nums) {
if(map.get(target - nums[i])) {
return [i,map.get(target - nums[i])]
}
map.set(nums[i],i)
}
};
二、 三数之和【返回数组元素】
2-1 三数之和
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
2-1-1 排序+双指针
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
return threeSumTarget(nums, 0)
};
const threeSumTarget = (nums, target) => {
nums.sort((a,b)=>a-b)
// const res = new Set();
const res= [];
for(let i = 0; i<nums.length; i++) {
let temp = towSumTarget(nums, i+1, target - nums[i])
if(temp.length) {
for(let item of temp) {
item.push(nums[i])
res.push(item);
}
}
// 跳过第一个数字重复的情况,否则会出现重复结果
while(i < nums.length-1 && nums[i]==nums[i+1]) i++;
}
return res;
}
var towSumTarget = function(nums, start, target) {
// 当需要返回结果数组时
// 排序+双指针
nums.sort((a,b) => {return a-b;})
let left = start,right = nums.length-1;
const res = [];
// 左右指针
while(left < right) {
let leftValue = nums[left];
let rightValue = nums[right];
if(nums[left] + nums[right] < target) left++;
else if(nums[left] + nums[right] > target) right--;
else {
res.push([nums[left],nums[right]])
while(left < right && nums[left] == leftValue ) left++;
while(left < right && nums[right] == rightValue ) right--;
}
}
return res;
};
2-1-2 思路分析
-
确定 第一个数字nums[i]之后,剩下的两个数字就是和为
target-nums[i]
的两个数字- 三数之和------》 两数之和
-
2-1-3 去重逻辑的思考
说道去重,其实主要考虑三个数的去重。 a, b ,c, 对应的就是 nums[i],nums[left],nums[right]
2-1-3-1 a的去重
-
分析
-
a 如果重复了怎么办,a是nums里遍历的元素,那么应该直接跳过去。
-
但这里有一个问题,是判断 nums[i] 与 nums[i + 1]是否相同,还是判断 nums[i] 与 nums[i-1] 是否相同。
-
都是和 nums[i]进行比较,是比较它的前一个,还是比较他的后一个。
if (nums[i] == nums[i + 1]) { // 去重操作 continue; }
-
-
那就我们就把 三元组中出现重复元素的情况直接pass掉了。 例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了。
- 我们要做的是 不能有重复的三元组,但三元组内的元素是可以重复的!
-
-
所以这里是有两个重复的维度。那么应该这么写:
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
- 这么写就是当前使用 nums[i],我们判断前一位是不是一样的元素,在看 {-1, -1 ,2} 这组数据,当遍历到 第一个 -1 的时候,只要前一位没有-1,那么 {-1, -1 ,2} 这组数据一样可以收录到 结果集里。
2-1-3-2 b与c的去重
- 很多同学写本题的时候,去重的逻辑多加了 对right 和left 的去重:(代码中注释部分)
while (right > left) {
if (nums[i] + nums[left] + nums[right] > 0) {
right--;
// 去重 right
while (left < right && nums[right] == nums[right + 1]) right--;
} else if (nums[i] + nums[left] + nums[right] < 0) {
left++;
// 去重 left
while (left < right && nums[left] == nums[left - 1]) left++;
} else {
}
}
-
这种去重其实对提升程序运行效率是没有帮助的。
- 拿right去重为例,即使不加这个去重逻辑,依然根据
while (right > left) 和 if (nums[i] + nums[left] + nums[right] > 0)
去完成right-- 的操作。 - 多加了 while (left < right && nums[right] == nums[right + 1]) right–; 这一行代码,其实就是把 需要执行的逻辑提前执行了,但并没有减少 判断的逻辑。
- 拿right去重为例,即使不加这个去重逻辑,依然根据
-
-
最直白的思考过程,就是right还是一个数一个数的减下去的,所以在哪里减的都是一样的。
-
所以这种去重 是可以不加的。 仅仅是 把去重的逻辑提前了而已。
-