合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n
nums1.length == m + nnums2.length == n
合并再排序
var merge = function(nums1, m, nums2, n) {
// 从m开始,把 nums2上元素添加到nums1中
for(let i=0;i<n;i++) {
nums1[m+i] = nums2[i]
}
// 排序
nums1.sort((a, b) => a - b)
};
- 从
nums1的m开始到末尾, 用nums2的元素代替,达到合并效果 - 给
nums1中的元素排序
双指针法
nums1和nums2中元素都是有序的,可以把两个数组看做一个队列,每次从两个数组头部取出元素,进行比较,把较少的放到结果中
const merge = function (nums1, m, nums2, n) {
// 数组看做队列,每次都从两个数组头部取出较少的放到结果中
let p1 = 0 // nums1的头指针
let p2 = 0 // nums2的头指针
// 缓存结果
let result = []
// 当两个数组都遍历完才会跳出循环
while (p1 < m || p2 < n) {
// 如果nums1/nums2已经遍历完了,那么只能操作另外一个数组了~
if (p1 === m) {
result.push(nums2[p2++])
} else if (p2 === n) {
result.push(nums1[p1++])
} else if (nums1[p1] < nums2[p2]) {
result.push(nums1[p1++])
} else {
result.push(nums2[p2++])
}
}
// 更新nums1
for (let i = 0; i < m + n; i++) {
nums1[i] = result[i]
}
}
从后向前数组遍历
思路参考
准备3个指针:p1, p2, tail
p1指向数组nums1的有数字尾部p2指向数组nums2的尾部tail指向数组nums1的尾部- 每次遍历取出两数组中较大的值,填充到
nums1[tail]中 - 最后,需要要判断nums2是否遍历完,如果没有遍历完,需要把剩余元素遍历到nums1中
const merge = function (nums1, m, nums2, n) {
// p1指向数组nums1的有数尾部
let p1 = m - 1
// p2指向数组nums2的尾部
let p2 = n - 1
// tail指向数组nums1的尾部
let tail = (m + n) - 1
while (p1 >= 0 && p2 >= 0) {
nums1[tail--] = nums1[p1] > nums2[p2] ?
nums1[p1--]
: nums2[p2--]
}
// 如果数组nums2还没有遍历完,继续遍历剩余元素到nums1中
while (p2 >= 0) {
nums1[tail--] = nums2[p2--]
}
}
两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
思路
- 提前准备一个map缓存遍历过的数和索引
- 遍历数组,取出当前数
- 计算另外一个数
otherNum = target-nums[i] - 查询
otherNum是否在map中- 如果存在,返回索引,结束循环
- map中没有查询到,把当前数
nums[i]添加到map中
- 计算另外一个数
const twoSum = function (nums, target) {
// 遍历数组,计算target减去当前项的差,
const map = new Map()
for (let i = 0; i < nums.length; i++) {
// 计算另外一个数,target - nums[i]
const otherNum = target - nums[i]
// 查询该数是否在map中
if (map.has(otherNum)) {
// 返回结果,提前结束函数
return [map.get(otherNum), i]
} else {
// map没有找到,把当前数和索引存储到map中
map.set(nums[i], i)
}
}
return []
}
三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
解法:排序 + 双指针
排序
- 为了方便遍历过程中,去掉重复元素,需要对数组元素进行排序
- 例如第一个数为
nums[i],在获取第二个数和第三个数之前,可以先判断nums[i]===nums[i-1],如果当前数与前一个数相等,那么可以跳过本轮循环 - 第二个数和第三个数也需要在做这种处理
思路
- 判断数组长度是否小于3
- 如果小于,直接返回空数组,提前结束函数
- 对数组元素进行排序,按照从小到大
- 准备一个空数组,存储达成条件的三元数组
- 遍历数组
- 取出当前数
nums[i],如果nums[i]>0,数组已经是排序,无法达成三个数之和等于0,直接返回结果 i > 0 && nums[i]===nums[i-1],元素重复,跳过本次循环,避免出现重复结果- 设置左指针
left=i+1,右指针right=nums[i].length-1 - 当
left < right成立,执行循环- 计算三数之和
- 当和大于0, 说明
nums[right]太大,right往前移 - 当和小于0, 说明
nums[left]太小,left往后移 - 当和等于0,记录下此时的三个数,判断左指针和右指针是否和下一个元素相同,如果相同,继续把left和right移到下一个,直到没有重复元素,重新继续计算
- 取出当前数
图示
- 实现代码:
/**
* @param {number[]} nums
* @return {number[][]}
*/
const threeSum = function (nums) {
// 数组长度小于3,直接结束
if (nums.length < 3) {
return []
}
// 按照从小到大给元素排序
nums.sort((a, b) => a - b)
// 存储三元数组的结果集
const result = []
const numsLen = nums.length
// 遍历数组
for (let i = 0; i < numsLen; i++) {
const firstNum = nums[i]
// 如果第一个数据大于0,后面结果肯定大于0,提前结束整个循环
if (firstNum > 0) {
break
}
// 当前数和前一个数相等,跳过本轮循环,进入下轮循环
if (i > 0 && firstNum === nums[i - 1]) {
continue
}
// 左指针,指向第二个数,向右边移动
let left = i + 1
// 右指针,指向第三个数,从数组末尾开始,往左边移动
let right = numsLen - 1
// 在while循环中,left一直向后移动,right指针一直往前移动,在这个过程中判断三个数之和是否等于0
while (left < right) {
// 计算三数之和
const sum = firstNum + nums[left] + nums[right]
if (sum > 0) {
// nums[right]大了,向前移动
right--
} else if (sum < 0) {
// nums[left]小了,向后移动
left++
} else if (sum === 0) {
// 和为0,做记录
result.push([firstNum, nums[left], nums[right]])
// 相邻数去重
while (left < right && nums[left] === nums[left + 1]) {
left++
}
while (left < right && nums[right] === nums[right - 1]) {
right--
}
left++
right--
}
}
}
return result
}
数组去重
已知如下数组:
const list = [3, 4, 5, 6, 5, 8, 3]
编写一个程序处理数组,最终获得元素不重复的数组
方式一:Set
// Set实现去重
const unique = (arr) => {
return [...new Set(arr)]
}
方式二:Array.from
// Array.from实现去重
const unique2 = (arr) => {
return Array.from(new Set(arr))
}
方式三:indexOf
// indexOf实现去重
const unique3 = (arr) => {
const result = []
arr.forEach(item => {
if (result.indexOf(item) === -1) {
result.push(item)
}
})
return result
}
方式三:filter
// filter实现去重
const unique4 = (arr) => {
return arr.filter((item, index) => {
// 只保留第一次出现的
return arr.indexOf(item) === index
})
}
方式四:reduce
// reduce实现去重
const unique5 = (arr) => {
return arr.reduce((pre, cur) => {
if (pre.indexOf(cur) === -1) {
pre.push(cur)
}
return pre
}, [])
}
数组扁平化
请实现flat函数,实现数组扁平化
let arr1 = [
1,
[2, 3, 4],
[5, [6, [7, [8]]]],
10
]
flat函数特性说明
-
数组.flat可以把嵌套数组拍平,变为一维数组- 该方法返回新数组,不影响原数组
- 没有传递参数,默认拍平一层,可以传入数字,表明要拍平的层数
- 传入参数
<=0的参数,返回原数组,不拍平 - 传入
Infinity关键字,无论嵌套有多深,都为拍平为一维数组 - 如果原数组有空位,
Array.prototype.flat()会跳过空位 包含空位的数组示例:
const arr = [1, [2, 5], , ,] - 传入参数
-
思路:
- 如果数组长度小于等于0,返回原数组,提前结束函数
- 如果数组长度>0,定义遍历result作为结果数组
- 遍历数组
- 判断当前元素是否是数组
- 当前元素是数组,递归调用
flat函数,当前元素作为新数组,depth减一作为新深度,并把flat函数的返回值展开,追加到结果数组中
- 当前元素是数组,递归调用
- 当前元素不是数组,添加到结果数组中
- 最后返回结果数组
方式一:实现flat函数
const flat1 = (arr, depth = 1) => {
// 如果depth小于等于0,返回原数组
if (depth <= 0) {
return arr
}
// 扁平化的数组
const result = []
arr.forEach(item => {
if (Array.isArray(item)) {
// 如果当前元素是数组,继续扁平化,depth减一
// 将扁平化结果展开添加到result中
result.push(...flat1(item, depth - 1))
} else {
result.push(item)
}
})
return result
}
// 指定深度
console.log('flat1', flat1(list, 2))
// 使用 Infinity,可展开任意深度的嵌套数组
console.log('flat1', flat1(list, Infinity))
Array.prototype.push支持添加多个元素
方式二:reduce实现flat
// reduce实现flat函数
const flat2 = (arr, depth = 1) => {
if (depth <= 0) {
return arr
}
return arr.reduce((pre, cur) => {
if (Array.isArray(cur)) {
pre.push(...flat2(cur, depth - 1))
} else {
pre.push(cur)
}
return pre
}, [])
}
// 指定深度
console.log('flat2', flat2(list, 2))
// 使用 Infinity,可展开任意深度的嵌套数组
console.log('flat2', flat2(list, Infinity))
计算两个数组的交集
给定两个数组,编写一个函数来计算它们的交集。
示例1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2]
示例2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [9,4]
-
说明
- 输出结果中的每个元素一定是唯一的。 我们可以不考虑输出结果的顺序
-
思路
- filter过滤,判断当前元素是否存在于另外一个数组中
- 最后用Set对结果做去重,返回去重后的结果
-
代码实现
// 使用filter过滤
// 最后用Set对结果数组做去重
const intersection = (arr1, arr2) => {
const result = arr1.filter(item => {
return arr2.indexOf(item) !== -1
})
return [...new Set(result)]
}
const nums1 = [4, 9, 5, 4]
const nums2 = [9, 4, 9, 8, 4]
// 使用
console.log('intersection', intersection(nums1, nums2))
计算多个数组的交集
要求:输出结果中的每个元素一定是唯一的
- 思路
- 使用
reduce函数,两两比较,比较的结果与后一个数组比较 - 使用
Set对结果去重,返回去重后的结果
- 使用
const intersection = (...args) => {
if (args.length === 0) {
return []
}
if (args.length === 1) {
return args[1]
}
const result = args.reduce((arr1, arr2) => {
// 每次比较两个数组的交集,比较的结果与后一个数组比较
return arr1.filter(item => arr2.includes(item))
})
return [...new Set(result)]
}
const nums1 = [4, 9, 5, 4]
const nums2 = [9, 4, 5, 9, 8, 4]
const nums3 = [3, 4, 5, 6]
// 使用
console.log('intersection', intersection(nums1, nums2, nums3))