n数之和
两数之和
给定一个整数数组 nums
和一个目标值 target
,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。不能重复利用这个数组中同样的元素。
题目链接:https://leetcode-cn.com/problems/two-sum/
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
思路:设定一个哈希表map
,记录每个值对应的索引i
,通过判断target-nums[i]
的值是否存在来确定另一个值的位置。
/* 两数之和 */
var twoSum = function (nums, target) {
let map = {};
for (let i = 0; i < nums.length; i++) {
if (map[target - nums[i]] !== undefined) {
return [map[target - nums[i]], i];
}
map[nums[i]] = i;
}
};
let res = twoSum([2, 7, 11, 15], 9);//[0,1]
console.log(res);
三数之和
给你一个包含 n 个整数的数组 nums
,判断 nums
中是否存在三个元素 a,b,c
,使得 a + b + c = 0
?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
题目链接:https://leetcode-cn.com/problems/3sum
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
思路1:每次循环确定nums[i]
,另外两数之和为target-nums[i]
,利用上面的两数之和方法即可求到对应值,但是提交超时。
/* ---------------哈希表实现(超时)--------------- */
var threeSum = function (nums) {
let set = new Set();
for (let i = 0; i < nums.length - 2; i++) {
/* 去重 */
if (i > 0 && nums[i] === nums[i - 1]) continue;
/* 设置map标记 */
let map = {};
for (let j = i + 1; j < nums.length; j++) {
if (!map[nums[j]]) {
/* 标记第三个值 */
map[0 - nums[i] - nums[j]] = 1;
} else {/* 访问到前面标记的值 */
/* 排序后转换为字符串才能在set去重 */
set.add([nums[i], 0 - nums[i] - nums[j], nums[j]].sort().toString());
}
}
}
let res = [];
/* 将set内的字符串转数组给res */
set.forEach(v => res.push(v.split(',')));
/* 子数组排序输出 */
return res.sort();
};
console.log(threeSum([-1, 0, 1, 2, -1, -4]));//[ [ '-1', '-1', '2' ], [ '-1', '0', '1' ] ]
思路2:先将nums
数组排序,然后i
遍历,转换为求另外两数之和,求两数之和的方式改成设置两个指针st
,ed
,从数组的两端走,根据两处值的和nums[st]+nums[ed]
大小确定st走还是ed走,符合条件则将nums[i]
,nums[st]
,nums[ed]
构成的数组放到res
中。
/* ----------------双指针实现---------------- */
var threeSum = function (nums) {
/* 排序 */
nums.sort((x, y) => x - y);
/* 全为正或全为负则退出 */
if (nums[0] >= 0 || nums[nums.length - 1] <= 0) return [];
let res = [];
/* i从左往右 */
for (let i = 0; i < nums.length - 2; i++) {
/* 去重 */
if (i >= 1 && nums[i] === nums[i - 1]) continue;
/* 另外两个数之和 */
let target = 0 - nums[i];
/* 指针头和尾 */
let st = i + 1, ed = nums.length - 1;
if (nums[i] > 0) break;
while (st < ed) {
let sum = nums[st] + nums[ed];
if (sum < target) {
st++;
} else if (sum > target) {
ed--;
} else {
/* 排序后插入 */
res.push([nums[i], nums[st], nums[ed]]);
while (st < ed && nums[st + 1] === nums[st]) st++;
while (st < ed && nums[ed - 1] === nums[ed]) ed--;
st++;
ed--;
}
}
}
return res;
};
console.log(threeSum([-1, 0, 1, 2, -1, -4]));//[ [ '-1', '-1', '2' ], [ '-1', '0', '1' ] ]
四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
题目链接:https://leetcode-cn.com/problems/4sum
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
思路:与三数之和双指针一样的步骤,多加一层循环,并且给新的循环加去重判断即可。
/* 四数之和 */
var fourSum = function (nums, target) {
let res = [];
nums.sort((x, y) => x - y); //排序 [ -3, -2, -1, 0, 0, 1, 2, 3 ]
/* 遍历第1个数 */
for (let i = 0; i < nums.length - 3; i++) {
/* 与i相等的数结果相等,为了去重直接跳过 */
if (i > 0 && nums[i] === nums[i - 1]) continue;
/* 第2个数 */
for (let j = i + 1; j < nums.length - 2; j++) {
/* 去重 */
if (j > i + 1 && nums[j] === nums[j - 1]) continue;
/* 设置双指针选取后两个数 */
let x = j + 1, y = nums.length - 1;
/* 剩余值 */
let tgt = target - nums[i] - nums[j];
while (x < y) {
if (nums[x] + nums[y] < tgt) {
x++;
} else if (nums[x] + nums[y] > tgt) {
y--;
} else {
/* 符合条件 */
res.push([nums[i], nums[j], nums[x], nums[y]]);
/* 去重 */
while (x < y && nums[x] === nums[x + 1]) x++;
while (x < y && nums[y] === nums[y - 1]) y--;
/* 移动 */
x++; y--;
}
}
}
}
return res;
};
let arr = [-3, -2, -1, 0, 0, 1, 2, 3];
let target = 0;
console.log(fourSum(arr, target));
/*
[
[-3, -2, 2, 3],
[-3, -1, 1, 3],
[-3, 0, 0, 3],
[-3, 0, 1, 2],
[-2, -1, 0, 3],
[-2, -1, 1, 2],
[-2, 0, 0, 2],
[-1, 0, 0, 1]
]
*/