217. 存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。
示例:输入: [1,2,3,1],输出: true
解题思路:「由于不需要记录重复元素的个数,所以直接利用 JavaScript 中的 Set 数据结构记录遍历的元素即可」。
时间复杂度:O(n),空间复杂度:O(n)。
const containsDuplicate = function(nums) {
const record = new Set();
for (let i = 0; i < nums.length; i++) {
if (!record.has(nums[i])) {
record.add(nums[i]);
} else {
return true;
}
}
return false;
};
53. 最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例:输入:nums = [-2,1,-3,4,-1,2,1,-5,4],输出:6
解题思路:「由于是连续子数组的和,所以在遍历过程中就需要比较当前元素是否加入到连续子数组还是重新作为新的连续子数组的起点,而这取决于两个值的大小关系」。
时间复杂度:O(n),空间复杂度:O(1)。
const maxSubArray = (nums) => {
let ans = nums[0]
let pre = nums[0];
for (let i = 1; i < nums.length; i++) {
pre = Math.max(pre + nums[i], nums[i]);
ans = Math.max(ans, pre);
}
return ans;
}
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例:输入:nums = [2,7,11,15], target = 9, 输出:[0,1]
第一种解法:「两重循环暴力枚举」。
时间复杂度:O(n^2),空间复杂度:O(1)。
const twoSum = (nums, target) => {
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) {
return [i, j];
}
}
}
}
第二种解法:「将num1、num2和target之间的三元关系,通过减法转化为两元关系,这样就把问题转化成了 target - num1 是否存在,再结合哈希表即可」。
时间复杂度:O(n),空间复杂度:O(n)。
const twoSum = (nums, target) => {
const record = new Map();
for (let i = 0; i < nums.length; i++) {
const item = target - nums[i]
const j = record.get(item);
record.set(nums[i], i);
if (j !== undefined && j !== i) {
return [j, i];
}
}
}
88. 合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例:输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3, 输出:[1,2,2,3,5,6]
第一种解法:「合并两个数组,重新排序」。
时间复杂度:O((m + n)log(m+n)),空间复杂度:O(m + n)。
const merge = function(nums1, m, nums2, n) {
nums1.splice(m, nums1.length - m, ...nums2);
return nums1.sort((a, b) => a - b);
};
第二种解法:「由于两个数组本身就是有序的,所以可以直接采用双指针来归并两个数组」。
时间复杂度:O(m + n),空间复杂度:O(m + n)。
const merge = function(nums1, m, nums2, n) {
let p1 = 0;
let p2 = 0;
const ans = [];
while (p1 < m || p2 < n) {
const item1 = nums1[p1];
const item2 = nums2[p2];
if (p1 === m) {
ans.push(item2);
p2++;
} else if (p2 === n) {
ans.push(item1);
p1++
} else if (item1 <= item2) {
ans.push(item1);
p1++;
} else {
ans.push(item2);
p2++;
}
}
ans.forEach((item, index) => nums1[index] = ans[index]);
return nums1;
}
第三种解法:「由于最终的结果需要保存在nums1中,所以采用从前往后的双指针归并的时候,需要临时数组来保存结果,最后还需要O(m+n)的时间复杂度来重新赋值,这里可以变换一下思路,从后往前即可在nums1中原地赋值」。
时间复杂度:O(m + n),空间复杂度:O(1)。
const merge = function(nums1, m, nums2, n) {
let p1 = m - 1;
let p2 = n - 1;
let tail = m + n - 1;
let current;
while (p1 >= 0 || p2 >= 0) {
if (p1 === -1) {
current = nums2[p2--];
} else if (p2 === -1) {
current = nums1[p1--];
} else if (nums1[p1] > nums2[p2]) {
current = nums1[p1--];
} else {
current = nums2[p2--];
}
nums1[tail--] = current;
}
}
五、写在最后
「如果本文对您有帮助,欢迎点赞、分享、或者关注微信公众号《漫谈大前端》。」