Waiting to add...
一、数组篇
leetcode-88--合并两个有序数组
Question:给你两个有序整数数组 nums1(m个元素) 和 nums2(n个元素),请你将 nums2 合并到 nums1 中,使 num1 成为一个有序数组。(假设
nums1
的空间大小等于m + n
)示例:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
Method 1: 直接合并后排序
比较直观的做法是先将 nums2 插到 num1 的末尾, 然后排序。这里我们注意 nums1 = [1,2,3,0,0,0],数组有多余空间,只是元素没有占满
const merge = function (nums1, m, nums2, n) {
if (n === 0) return; // 被合并数组没有元素直接返回
nums1.splice(m, nums1.length - m, ...nums2); // 先将 nums2 放在 nums1 后面
// 也可如下:
// for (let i = 0; i <= n; i++) {
// nums1[m + i] = nums2[i];
// }
nums1.sort((a, b) => a - b); // 递增顺序排序
};
note:针对上面解法回顾一下两个知识点
- Array.prototype.splice():通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容(会改变原数组)。这里是用 nums2 的元素来替换 nums1 多余的数组空间(上面用0表示)。
- Array.prototype.sort():用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的。
解法类似 merge sort (归并排序),来看看相关的思路:
merge 的过程可以
是先比较两个数组的头元素,然后将较小的推到最终的数组中,并将其从原数组中出队。不断循环直到两个数组都为空。
// 这里代码赘余为了方便理解
const myMerge = function (nums1, nums2) {
let arr = [];
while (nums1.length || nums2.length) {
// 考虑一下边缘情况
if (nums1.length === 0) {
arr.push(nums2.shift());
continue;
}
if (nums2.length === 0) {
arr.push(nums1.shift());
continue;
}
// 取出两个数组的头部元素
const a = nums1[0];
const b = nums2[0];
// 比较大小
if (a > b) {
arr.push(nums2.shift());
} else {
arr.push(nums1.shift());
}
}
return arr;
};
但是题目要求的是“原地修改”,即返回的是数组 nums1
Method 2: 双指针法
Think1:基于上面的 merge sort ,如果都从数组头部开始比较,需要将 nums1 的前 m 个元素放到一个辅助数组中,避免写指针写入的干扰(空间复杂度为O(m))
Think2:从后往前比较,将比较出来的较大的那个数放在 nums1 最后的那个空余空间(时间复杂度为O(m+n),空间复杂度为O(1))
// Think2
const merge = function (nums1, m, nums2, n) {
let cur = m + n - 1; // 定义写指针,指向当前用于填入元素的位置,初始化指向 nums1 的末尾
while (cur >= 0) {
if (n === 0) return;
// 边缘情况处理
if (m < 1) {
nums1[cur--] = nums2[--n];
continue;
}
if (n < 1) {
nums1[current--] = nums1[--m];
continue;
}
// 具体处理
if (nums1[m - 1] > nums2[n - 1]) {
nums1[current--] = nums1[--m];
} else {
nums1[current--] = nums2[--n];
}
}
}
// 更好的处理
const merge = function (nums1, m, nums2, n) {
// 定义三个指针
let len1 = m - 1,
len2 = n - 1,
len = m + n - 1
while (len2 >= 0) { // nums2 有元素时
if (len1 < 0) { // nums1 没有元素时
nums1[len--] = nums2[len2--]
continue
}
nums1[len--] = nums1[len1] >= nums2[len2] ? nums1[len1--] : nums2[len2--]
}
};
leetcode-1--两数之和
Question:给定一个整数数组
nums
和一个整数目标值target
,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标示例:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]
Method 1:暴力枚举
使用两层循环,外层循环计算当前元素与 target 之间的差值,内层循环寻找该差值,若找到该差值,则返回两个元素的下标(时间复杂度为 O(n^2),空间复杂度为 O(1))
var twoSum = function (nums, target) {
const len = nums.length;
for (let i = 0; i < len; i++) {
let preItem = nums[i];
// j = i + 1 的目的是减少重复计算和避免两个元素下标相同
for (let j = i + 1; j < len; j++) {
let reaItem = nums[j];
if (preItem + reaItem === target)
return [i, j];
}
}
return []; // 没有符合条件的两个数,返回 []
};
Method 2:一层 for 循环寻找差值
使用一层循环,每次循环执行一次 indexOf 判断数组中是否有差值(时间复杂度O(n))
const twoSum = (nums, target) => {
for (let i = 0; i < nums.length; i++) {
let diff= nums.indexOf(target - nums[i]);
if (diff!= -1 && diff!= i) { // 不存在差值会返回 -1,且题目要求不能出现同一元素
return [i, diff]
}
}
return []; // 没有符合条件的两个数,返回 []
}
note:
- Array.prototype.indexOf():返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回 -1
Method 3:HashMap法
增加一个 Map 记录已经遍历过的数字及其对应的索引值:这样当遍历一个新数字的时候就去 Map 里查询 target 与该数的差值是否已经在前面的数字中出现过。如果出现过,则直接拿到这个出现过的数的索引和当前遍历的数的数组下标并返回(时间复杂度O(n),空间复杂度为 O(1))
const twoSum = (nums, target) => {
// 1. 构造 Map 数据结构
const map = new Map(); // 存储方式 {key, index}
// 2. 遍历数组
for (let i = 0; i < nums.length; i++) {
// 2.1 如果找到 target - nums[i] 的值
let diff = target - nums[i];
if (map.has(diff)) { // 判断 map 中是否有这个 diff 值(key)
// 拿到 key 对应的下标(index)及数组元素下标(i),并返回
return [map.get(diff), i];
} else {