1. 两数之和
题目链接:https://leetcode-cn.com/problems/two-sum/
解题思路
1. 暴力求解
算法时间复杂度 O(n^2)
两层循环,外层循环枚举(或称作选中一个标杆),内层循环从枚举值之后开始遍历,计算两数的和是否等于target
。如果找到了两个数,那么返回这两个数的下标;
代码
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function (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];
}
}
}
return [];
};
function twoSum(nums: number[], target: number): number[] {
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (target === nums[i] + nums[j]) {
return [i, j];
}
}
}
return [];
}
2. 哈希表求解
这道题最优的做法时间复杂度是 O(n)。
顺序遍历数组,对每⼀个元素a
, 用target-nums[i]
得到b
, 若b
存在于map
中,直接返回 2 个数字的下标即可。如果b
不存在, 就把a
作为key
, a
的下标作为value
存⼊map
中,等待扫到“另⼀半”数字的时候,再取出来返回结果。
代码
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function (nums, target) {
let map = new Map();
for (let i = 0; i < nums.length; i++) {
if (map.has(target - nums[i])) {
return [map.get(target - nums[i]), i];
}
map.set(nums[i], i);
}
return [];
};
function twoSum(nums: number[], target: number): number[] {
let map = new Map();
for (let i = 0; i < nums.length; i++) {
if (map.has(target - nums[i])) {
return [i, map.get(target - nums[i])];
}
map.set(nums[i], i);
}
return [];
}
202. 快乐数
题目链接:https://leetcode-cn.com/problems/happy-number/
解题思路
使用一个map
存储计算过的数字,如果目前的数字已经计算过,表示死循环出现,return false
。持续计算到 1 return true
或是死循环出现return false
。
范例:判断 4 是不是 happy number
4 ^ 2 = 16
1 ^ 2 + 6 ^ 2 = 37
3 ^ 2 + 7 ^ 2 = 58
...
... = 20
2 ^ 2 + 0 = 4
4 重复出现,因此进入死循环
1. 哈希表
代码
/**
* @param {number} n
* @return {boolean}
*/
var isHappy = function (n) {
// map存储计算过的数字
let map = new Map();
// 如果nap里面出现过这个数字 或 数字 = 1,停止循环
while (!map.has(n) && n !== 1) {
map.set(n, n);
// 单纯的计算每一个位数的平方和
n.toString()
.split("")
.forEach((item, index) => {
if (index === 0) {
n = 0;
}
n += item * item;
});
n = parseInt(n);
}
return n === 1;
};
242.有效的字母异位词
题目链接:https://leetcode.cn/problems/valid-anagram/
解题思路
1. 转化为顺序字符串
代码
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isAnagram = function (s, t) {
return s.split("").sort().join("") == t.split("").sort().join("");
};
var isAnagram = function (s, t) {
return (
s.length == t.length && [...s].sort().join("") === [...t].sort().join("")
);
};
2.
定义一个数组叫做 record 用来上记录字符串 s 里字符出现的次数。
需要把字符映射到数组也就是哈希表的索引下表上,因为字符 a 到字符 z 的 ASCII 是 26 个连续的数值,所以字符 a 映射为下表 0,相应的字符 z 映射为下表 25。
再遍历 字符串 s 的时候,只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符 a 的 ASCII,只要求出一个相对数值就可以了。 这样就将字符串 s 中字符出现的次数,统计出来了。
那看一下如何检查字符串 t 中是否出现了这些字符,同样在遍历字符串 t 的时候,对 t 中出现的字符映射哈希表索引上的数值再做-1 的操作。
那么最后检查一下,record 数组如果有的元素不为零 0,说明字符串 s 和 t 一定是谁多了字符或者谁少了字符,return false。
最后如果 record 数组所有元素都为零 0,说明字符串 s 和 t 是字母异位词,return true。
代码
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isAnagram = function (s, t) {
if (s.length !== t.length) return false;
const record = new Array(26).fill(0);
const base = "a".charCodeAt();
for (const i of s) {
record[i.charCodeAt() - base]++;
}
for (const i of t) {
if (!record[i.charCodeAt() - base]) return false;
record[i.charCodeAt() - base]--;
}
return true;
};
349. 两个数组的交集
题目链接:https://leetcode-cn.com/problems/intersection-of-two-arrays/
解题思路
1. 暴力解法
代码
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
let len1 = nums1.length;
let len2 = nums2.length;
let set = new Set();
for (let i = 0; i < len1; i++) {
for (let j = 0; j < len2; j++) {
if (nums1[i] === nums2[j]) {
set.add(nums1[i]);
}
}
}
return Array.from(set);
};
2. 数组遍历
1)先判断nums1
与nums2
长度谁的长度短,遍历最短的数组
2)如果可以在长的数组nums1
中找到nums2[i]
,表示这是交集的数字
2)判断结果数组result
是否已经有交集数字,如果没有就新增到结果数组resu1t
中
时间复杂度为O(m*n)
代码
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
let len1 = nums1.length;
let len2 = nums2.length;
let result = [];
if (len1 > len2) {
for (let i = 0; i < len2; i++) {
if (nums1.includes(nums2[i]) && !result.includes(nums2[i])) {
result.push(nums2[i]);
}
}
} else {
for (let i = 0; i < len1; i++) {
if (nums2.includes(nums1[i]) && !result.includes(nums1[i])) {
result.push(nums1[i]);
}
}
}
return result;
};
优化:
我们定义为nums2
,如果nums1
更短,使用解构赋值进行交换
代码
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
let len1 = nums1.length;
let len2 = nums2.length;
let result = [];
// 判断nums1,nums2的长度
[nums1, nums2] = len1 > len2 ? [nums1, nums2] : [nums2, nums1];
// 只需要遍历较短的数组就行
for (let i = 0; i < len2; i++) {
// 如果可以在长的数组中找到目前的值,
// 而且在结果result中找不到,代表这个值是新的交集
if (nums1.includes(nums2[i]) && !result.includes(nums2[i])) {
result.push(nums2[i]);
}
}
return result;
};
4. 使用 set
为了在线性时间内解决这个问题,我们使用集合set
这一数据结构,该结构可以提供平均时间复杂度为O(1)
1)将两个数组都转换为集合,然后迭代较小的集合,检查其中的每个元素是否同样存在于较大的集合中
平均情况下,这种方法的时间复杂度为 O(n+m)
代码
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
let set1 = new Set(nums1);
let set2 = new Set(nums2);
let result = new Set();
[set1, set2] = set1.size > set2.size ? [set1, set2] : [set2, set1];
for (let item of set2) {
if (set1.has(item) && !result.has(item)) {
result.add(item);
}
}
return Array.from(result);
};
5. 数组的 filter 方法
代码
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
return Array.from(new Set(nums1.filter((x) => nums2.includes(x))));
};
6. 数组的 filter 方法+set 的 has 方法
代码
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
let set2 = new Set(nums2);
return [...new Set(nums1.filter((x) => set2.has(x)))];
};
7. set
代码
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
// 根据数组大小交换操作的数组
if (nums1.length < nums2.length) {
const _ = nums1;
nums1 = nums2;
nums2 = _;
}
const nums1Set = new Set(nums1);
const resSet = new Set();
// for(const n of nums2) {
// nums1Set.has(n) && resSet.add(n);
// }
// 循环 比 迭代器快
for (let i = nums2.length - 1; i >= 0; i--) {
nums1Set.has(nums2[i]) && resSet.add(nums2[i]);
}
return Array.from(resSet);
};
8. 双指针
代码
var intersection = function (nums1, nums2) {
nums1.sort((x, y) => x - y);
nums2.sort((x, y) => x - y);
const length1 = nums1.length,
length2 = nums2.length;
let index1 = 0,
index2 = 0;
const intersection = [];
while (index1 < length1 && index2 < length2) {
const num1 = nums1[index1],
num2 = nums2[index2];
if (num1 === num2) {
// 保证加入元素的唯一性
if (
!intersection.length ||
num1 !== intersection[intersection.length - 1]
) {
intersection.push(num1);
}
index1++;
index2++;
} else if (num1 < num2) {
index1++;
} else {
index2++;
}
}
return intersection;
};