JS算法
01_两数之和
问题描述
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。 详细代码 let list = [1, 8, 4, 9, 3, 16];
const twoSum = function (nums, target) {
let map = new Map();
for (let i = 0; i < nums.length; i++) {
const result = target - nums[i];
if (map.has(result)) {
return [map.get(result), i];
} else {
map.set(nums[i], i);
}
}
};
const result = twoSum(list, 10);
console.log(result);
代码解析
思路:使用map存储已遍历的元素,空间换时间
🐋 Map 对象是键值对的集合。Map 中的一个键只能出现一次;它在 Map 的集合中是独一无二的。Map 对象按键值对迭代——一个 for...of 循环在每次迭代后会返回一个形式为 [key,value] 的数组。 迭代按插入顺序进行,即键值对按 set() 方法首次插入到集合中的顺序(也就是说,当调用 set() 时,map 中没有具有相同值的键)进行迭代。 使用Map()的set和has函数,把target和nums[i]相差值与对应的i作为键和值存入map中,若差值与已有存有的值相等,则返回该值与对应的i
● has() 方法返回一个布尔值,指示具有指定键的元素是否存在
● set() 方法为 Map 对象添加或更新一个指定了键(key)和值(value)的(新)键值对
● get() 方法从 Map 对象返回指定的元素(传入键返回值)
● delete() 方法用于移除 Map 对象中指定的元素(传入键)
● clear() 方法会移除 Map 对象中的所有元素
02_三数之和
问题描述
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。 示例: 给定数组 nums = [-1, 0, 1, 2, -1, -4] 满足要求的三元组集合为:[[-1, 0, 1],[-1, -1, 2]]
详细代码
const nums = [-1,0,1,2,-1,-4];
const threeSum = function (nums) {
// 特殊情况返回[]
if (!nums || nums.length < 3) return []
// 升序排序
nums.sort((a, b) => a-b)
let result = [], second, last
for(let i = 0; i < nums.length; i++) {
// 第一位大于0时无满足条件直接结束
if(nums[i] > 0) break
// 去重
if(i > 0 && nums[i] === nums[i-1]) continue
second = i + 1
last = nums.length - 1
while(second < last) {
const sum = nums[i] + nums[second] + nums[last]
if(sum === 0) {
result.push([nums[i], nums[second], nums[last]])
// while去重(注意不能使用if,避免只跳过第一个重复元素)
while(second < last && nums[second] === nums[second + 1]) {
second++
}
while(second < last && nums[last] === nums[last - 1]) {
last--
}
last--
second++
}
else if(sum > 0) last--
else if(sum < 0) second++
}
}
return result
};
代码解析
思路:先数组排序,排序完后遍历数组,以 nums[i] 作为第一个数 first ,以 nums[i+1] 作为第二个数 second ,将 nums[nums.length - 1] 作为第三个数 last ,判断三数之和是否为 0 , ● <0 ,则 second 往后移动一位(nums 是增序排列),继续判断
● >0 ,则 last 往前移动一位(nums 是增序排列),继续判断
● ===0 ,则进入结果数组中,并且 second 往后移动一位, last 往前移动一位,继续判断下一个元组 直至 second >= last 结束循环,此时, nums[i] 作为第一个数的所有满足条件的元组都已写入结果数组中了,继续遍历数组,直至 i === nums.length - 2 (后面需要有 second 、 last )
- 首先,检查特殊情况。如果 nums 为 null 或者长度小于 3,则直接返回空数组 []。
- 对数组 nums 进行升序排序,以便后续处理。
- 初始化变量 result 为结果数组,second 和 last 为指针。
- 进行主循环,遍历数组中的每个元素。如果当前元素大于 0,则跳出循环,因为数组已经升序排列,后面的元素都会大于 0,无法满足和为零的条件。
- 在循环中进行去重操作。如果当前元素与前一个元素相等,即存在重复,使用 continue 跳过本次循环,避免重复的三元组。
- 初始化指针 second 为当前元素的下一个位置,初始化指针 last 为数组的最后一个位置。
- 使用 while 循环来遍历 second 和 last 指针之间的元素。计算当前三个元素的和 sum。
- 如果 sum 等于 0,则找到了和为零的三元组。将这个三元组 [nums[i], nums[second], nums[last]] 添加到结果数组 result 中。
- 在找到和为零的三元组后,需要进行去重操作。使用 while 循环来连续跳过重复元素。对于 second 指针,如果当前元素与下一个元素相等,则 second 向后移动一位;对于 last 指针,如果当前元素与前一个元素相等,则 last 向前移动一位。
- 在完成去重操作后,将 second 向后移动一位,将 last 向前移动一位,以继续寻找下一个可能的三元组。
- 如果 sum 大于 0,则说明当前三元组的和偏大,需要将 last 向前移动一位,以减小和的值。
- 如果 sum 小于 0,则说明当前三元组的和偏小,需要将 second 向后移动一位,以增大和的值。
- 循环结束后,返回结果数组 result。
03_N数之和
问题描述
请用算法实现,从给定的无需、不重复的数组A中,取出N个数,使其相加和为M。并给出算法的时间、空间复杂度
var arr = [1, 4, 7, 11, 9, 8, 10, 6];
var N = 3;
var M = 27;
Result:
[7, 11, 9], [11, 10, 6], [9, 8, 10]
详细代码
const findNNumbersWithSum = (arr, N, M) => {
const result = [];
const path = [];
const backtrack = (startIndex, target, count) => {
// 当和为 M 且选取了 N 个数时,将当前路径加入结果数组
if (target === 0 && count === N) {
result.push([...path]);
return;
}
// 当选取的数超过 N 个、和大于 M、或者遍历到了数组末尾时,结束当前路径的探索
if (count > N || target < 0 || startIndex === arr.length) {
return;
}
// 从 startIndex 开始向后遍历数组
for (let i = startIndex; i < arr.length; i++) {
// 选择当前元素,将其加入路径,并继续向后递归
path.push(arr[i]);
backtrack(i + 1, target - arr[i], count + 1);
// 回溯,撤销选择,继续探索其他分支
path.pop();
}
};
// 调用回溯函数,从第一个元素开始探索
backtrack(0, M, 0);
return result;
};
代码解析
采用回溯法解决N数和问题
- 创建一个空数组 result 用于存储满足条件的结果组合。
- 创建一个空数组 path 用于记录当前路径的选取结果。
- 定义回溯函数 backtrack,它接受三个参数:startIndex 表示从数组的哪个位置开始选取元素,target 表示目标和,count 表示已选取的元素个数。
- 在 backtrack 函数内部,首先进行终止条件的判断:
- 如果 target 等于 0 且 count 等于 N,说明当前路径选取的元素和为目标和,将当前路径加入结果数组 result 中。
- 如果 count 大于 N、target 小于 0 或者 startIndex 达到数组末尾,意味着当前路径无法满足条件,直接返回。
- 然后,在 startIndex 后面的元素中进行选择。使用一个 for 循环从 startIndex 开始向后遍历数组:
- 选择当前元素,将其加入路径 path 中,并继续向后递归调用 backtrack 函数。
- 递归调用结束后,进行回溯操作,即撤销当前选择的元素,继续探索其他分支。
- 最后,在主函数 findNNumbersWithSum 中调用 backtrack 函数,从第一个元素开始探索。
- 返回最终的结果数组 result,其中每个数组表示一种满足条件的选取组合。
04_数组扁平去重与排序
问题描述
已知如下数组:var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
详细代码
const arr = [ [1, 2, 52], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10]
function handleFlat(array) {
return array.flat(Infinity)
}
console.log(handleFlat(arr));
const hhh = [...new Set(handleFlat(arr))]
// const hhh = Array.from(new Set(handleFlat(arr)))
const result = hhh.sort((a, b) => {
return a-b
})
console.log(result);
代码解析
☕考察数据的一些基本方法
- 扁平化:采用array.flat()方法实现数组扁平,其中参数指定要提取嵌套数组的结构深度(数字),默认值 1,设置为Infinity(无穷大)时为展开任意层级的数组;
- 去重:使用Set数据结构将数组去重,再用扩展运算符或者Array.from将Set结构转换为数组;
- 排序:使用Array.sort()方法进行升序排序,传入的比较函数 (a, b) => a - b 确保按数字大小排序;
05_数组交并差集
问题描述
给定两个数组,编写一个函数来计算它们的交集并集差集。
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [9,4]
实际代码
const nums1 = [1, 2, 2, 1, 87];
const nums2 = [2, 2, NaN, undefined, null, 5];
// 交集
function intersection(nums1, nums2) {
return [...new Set(nums1.filter((item) => nums2.includes(item)))];
}
console.log(intersection([1, 2, 2, 1], [2, 2]));
// 并集
function union(nums1, nums2) {
return [...new Set(nums1.concat(nums2))];
}
console.log(union(nums1, nums2));
// 差集
function difference(nums1, nums2) {
const result1 = nums1.filter(item => {
return !nums2.includes(item)
})
const result2 = nums2.filter(item => {
return !nums1.includes(item)
})
return [[Array.from(new Set(result1))], [Array.from(new Set(result2))]]
}
console.log(difference(nums1, nums2));
代码解析
🏐 主要使用数组的几个方法:
● filter:创建给定数组一部分的浅拷贝,其包含通过所提供函数实现的测试的所有元素,用于实现过滤
● includes:用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回 false
● concat:合并两个或多个数组,此方法不会更改现有数组,而是返回一个新数组
- 交集函数 intersection(nums1, nums2):
○这个函数接受两个数组 nums1 和 nums2 作为参数。
○使用 filter() 方法过滤 nums1 数组,保留那些也在 nums2 数组中存在的元素。
○通过 Set 和扩展运算符 ... 的组合,去除重复的元素,并将结果转换为数组。
○返回结果,即为两个数组的交集。 - 并集函数 union(nums1, nums2):
○这个函数接受两个数组 nums1 和 nums2 作为参数。
○使用 concat() 方法将 nums1 和 nums2 数组合并成一个新数组。
○通过 Set 和扩展运算符 ... 的组合,去除重复的元素,并将结果转换为数组。
○返回结果,即为两个数组的并集。 - 差集函数 difference(nums1, nums2):
○这个函数接受两个数组 nums1 和 nums2 作为参数。
○使用 filter() 方法过滤 nums1 数组,保留那些不在 nums2 数组中存在的元素。
○使用 filter() 方法过滤 nums2 数组,保留那些不在 nums1 数组中存在的元素。
○通过 Set、Array.from() 和括号的组合,去除重复的元素,并将结果转换为二维数组形式。
○返回结果,即为两个数组的差集,包含两个数组分别独有的元素。