哈希表
- [242. 有效的字母异位词](https://leetcode.cn/problems/valid-anagram/)
- [383. 赎金信](https://leetcode.cn/problems/ransom-note/)
- [438. 找到字符串中所有字母异位词](https://leetcode.cn/problems/find-all-anagrams-in-a-string/)
- [49. 字母异位词分组](https://leetcode.cn/problems/group-anagrams/)
- [349. 两个数组的交集](https://leetcode.cn/problems/intersection-of-two-arrays/)
- [350. 两个数组的交集 II](https://leetcode.cn/problems/intersection-of-two-arrays-ii/)
- [202. 快乐数](https://leetcode.cn/problems/happy-number/)
- [1. 两数之和](https://leetcode.cn/problems/two-sum/)
- [454. 四数相加 II](https://leetcode.cn/problems/4sum-ii/)
- [15. 三数之和](https://leetcode.cn/problems/3sum/)
- [18. 四数之和](https://leetcode.cn/problems/4sum/)
242. 有效的字母异位词
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isAnagram = function(s, t) {
if(s.length !== t.length) return false
let arr = new Array(26)
arr.fill(0)
for(let i = 0; i < s.length; i++){
arr[s[i].charCodeAt() - 97] ++
}
for(let i = 0; i < t.length; i++){
arr[t[i].charCodeAt() - 97] --
}
for(let i = 0; i < 26; i++){
if(arr[i] !== 0) return false
}
return true
};
383. 赎金信
/**
* @param {string} ransomNote
* @param {string} magazine
* @return {boolean}
*/
var canConstruct = function(ransomNote, magazine) {
if(ransomNote.length > magazine.length) return false
let arr = new Array(26)
arr.fill(0)
for(let i = 0; i < magazine.length; i++){
arr[magazine[i].charCodeAt() - 97] ++
}
for(let i = 0; i < ransomNote.length; i++){
arr[ransomNote[i].charCodeAt() - 97] --
}
for(let i = 0; i < 26; i++){
if(arr[i] < 0) return false
}
return true
};
438. 找到字符串中所有字母异位词
暴力求解:
以字符串
s
的每个下标为起始位置判断长度为p.length
的子串是否符合条件可以过,但是时间复杂度为 O(mn)mn分别为 s p 的长度
/**
* @param {string} s
* @param {string} p
* @return {number[]}
*/
var findAnagrams = function(s, p) {
if(s.length < p.length) return []
let arr = new Array(26)
arr.fill(0)
let len = p.length
for(let i = 0; i < len; i++){
arr[p[i].charCodeAt() - 97] ++
}
let ans = []
for(let i = 0; i <= s.length - p.length; i++){
let each = [...arr]
let sum = len
for(let j = 0; j < p.length; j++){
if(each[s[i+j].charCodeAt() - 97] <= 0)
break
else{
each[s[i+j].charCodeAt() - 97] --
sum --
}
}
if(sum === 0) ans.push(i)
}
return ans
};
滑动窗口
arrP
存放p
中字母出现次数,arrS
存放当前窗口s
子串中字符出现的次数- 当两者
相等
时,满足条件,将开始位置push进ans数组
/**
* @param {string} s
* @param {string} p
* @return {number[]}
*/
var findAnagrams = function(s, p) {
if(s.length < p.length) return []
let arrP = new Array(26)
arrP.fill(0)
let arrS = new Array(26)
arrS.fill(0)
for(let i = 0; i < p.length; i++){
arrP[p[i].charCodeAt() - 97] ++
arrS[s[i].charCodeAt() - 97] ++
}
let ans = []
if(arrP.toString() === arrS.toString()) ans.push(0)
for(let i = 0; i < s.length - p.length; i++){
arrS[s[i].charCodeAt() - 97] --
arrS[s[i + p.length].charCodeAt() - 97] ++
if(arrP.toString() === arrS.toString()){
ans.push(i + 1)
}
}
return ans
};
49. 字母异位词分组
- 满足异位词的字符串中每个字母出现的次数相同,我们可以将出现的次数作为map的key
- 那么出现的次数如何用什么数据结构存储呢,数组?可是在js中数组本质是对象,就算是元素都相等的数组,只要不是同时指向同一个内存都是不相等的,所以最好用字符串,将数组join()一下就行了
- 遍历
strs
,计算每个strs[i]的arr,判断map中是否有,没有就set,有的话就在对应的value中push进去当前的字符串 strs[i] - 最终将使用forEach遍历map,将value放在ans中返回
/**
* @param {string[]} strs
* @return {string[][]}
*/
var groupAnagrams = function(strs) {
let map = new Map()
let arr = new Array(26)
for(let i = 0; i < strs.length; i++){
arr.fill(0)
for(let j = 0; j < strs[i].length; j++){
arr[strs[i][j].charCodeAt() - 97]++
}
if(!map.has(arr.join())) map.set(arr.join(),[strs[i]])
else map.get(arr.join()).push(strs[i])
}
let ans = []
map.forEach((value) => {
ans.push(value)
})
return ans
};
349. 两个数组的交集
- 遍历
nums1
放入set
中(对nums1中的元素去重了) - 遍历
nums2
,如果set 中有,则为交集元素,将该元素放入ans
,并且删掉
set中的这个元素,达到对nums2 中的元素去重 - 最后返回 ans
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function(nums1, nums2) {
let set = new Set()
let ans = []
for(let i = 0; i < nums1.length; i++){
if(!set.has(nums1[i])) set.add(nums1[i])
}
for(let i = 0; i < nums2.length; i++){
if(set.has(nums2[i])){
ans.push(nums2[i])
set.delete(nums2[i])
}
}
return ans
};
350. 两个数组的交集 II
- 遍历 nums1,使用数组 arr 存储 nums1 中元素出现的次数
- 遍历 nums2,如果 arr 数组中该元素出现的次数 > 0 ,则添加进 ans,并且出现次数需要 -1
- 返回 ans
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersect = function(nums1, nums2) {
let arr = new Array(1005).fill(0)
for(let i = 0; i < nums1.length; i++){
arr[nums1[i]] ++
}
let ans = []
for(let i = 0; i < nums2.length; i++){
if(arr[nums2[i]] !== 0){
ans.push(nums2[i])
arr[nums2[i]] --
}
}
return ans
};
202. 快乐数
- set 记录出现过得数
- 不断使用每一位平方和替换当前数,如果是 1,就返回 true
- 如果不是 1,就放入 set 中,当下次再出现这个数证明进入无限循环,返回 false
/**
* @param {number} n
* @return {boolean}
*/
var isHappy = function(n) {
if(n === 1) return true
let set = new Set()
while(1){
let sum = 0
while(n){
sum += (n%10)*(n%10)
n = Math.floor(n/10)
}
n = sum
if(sum === 1) return true
if(set.has(sum)) return false
set.add(sum)
}
};
1. 两数之和
/**
* @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]
}else{
map.set(nums[i],i)
}
}
return []
};
454. 四数相加 II
- 两层 for 循环遍历 nums1 和 nums2,将和作为key存到 map中,对应的值是该和出现的次数(要记录符合情况的所有数量)
- cnt 用来计数,表示符合情况的数量
- 再来两层 for 循环遍历 nums3 和 nums4,在map中找对应的-(nums3+nums4),找到就将其value累加到cnt
var fourSumCount = function(nums1, nums2, nums3, nums4) {
let cnt = 0
let map = new Map()
let n = nums1.length
for(let i=0; i < n; i++ ){
for(let j = 0; j < n; j++){
if(!map.has(nums1[i]+nums2[j])){
map.set(nums1[i]+nums2[j],1)
}else{
map.set(nums1[i]+nums2[j],map.get(nums1[i]+nums2[j])+1)
}
}
}
for(let i=0; i < n; i++ ){
for(let j = 0; j < n; j++){
if(map.has(-nums3[i]-nums4[j])){
cnt += map.get(-nums3[i]-nums4[j])
}
}
}
return cnt
};
15. 三数之和
-
先将数组中的数字排序
-
使用一层for循环遍历数组,如果nums[i]与上一个元素相等就跳过
-
设置左右两个指针,left,right
-
判断当前三个指针i,left,right 的和与0的关系
-
如果 == 0,则将三者对应元素作为一种符合情况的答案放入ans,同时需要移动left,right,注意需要去重
-
如果 > 0 ,则 right –
-
如果 < 0 , 则 left ++
-
剪枝
:看了题解之后,给for循环入口加剪枝:-
如果当前元素已经大于目标值(0),直接break
-
当前元素加上最大的两个元素比目标值(0)还小,直接 continue;
-
当前元素加上与它连续的两个元素(当前情况的最小两个元素)比目标值(0)还大,直接break
-
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
if(nums.length <= 2) return []
let ans = []
nums.sort((a,b) => a-b)
for(let i = 0; i < nums.length - 2; i++){
if(nums[i] > 0) break
if(nums[i] + nums[nums.length - 1] + nums[nums.length - 2] < 0) continue
if(nums[i] + nums[i+1] + nums[i+2] > 0) break
if(i>0 && nums[i] === nums[i-1]) continue
let left = i + 1
let right = nums.length - 1
while(left<right){
if(nums[i] + nums[left] + nums[right] === 0){
ans.push([nums[i] , nums[left] , nums[right]])
left ++
right --
while(nums[left]==nums[left-1]){
left ++
}
while(nums[right]==nums[right+1]){
right --
}
}else if(nums[i] + nums[left] + nums[right] < 0){
left ++
}else{
right --
}
}
}
return ans
};
18. 四数之和
- 与三数之和一样,只是多一层 for 循环
/**
* @param {number[]} nums
* @param {number} target
* @return {number[][]}
*/
var fourSum = function(nums, target) {
if(nums.length <= 3) return []
let ans = []
nums.sort((a,b) => a - b)
for(let i = 0; i < nums.length - 3; i++){
if(nums[i] + nums[nums.length -1] + nums[nums.length -2] + nums[nums.length -3] < target) continue
if(nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target) break
if(i > 0 && nums[i] === nums[i-1]) continue
for(let j = i + 1; j < nums.length - 2; j++){
if(nums[i] + nums[j] + nums[nums.length -1] + nums[nums.length -2] < target) continue
if(nums[i] + nums[j] + nums[j+1] + nums[j+2] > target) break
if( j > i + 1 && nums[j] === nums[j-1]) continue
let l = j + 1
let r = nums.length - 1
while(l<r){
if(nums[i]+nums[j]+nums[l]+nums[r] === target){
ans.push([nums[i],nums[j],nums[l],nums[r]])
l ++
r --
while(nums[l]==nums[l-1]){
l ++
}
while(nums[r]==nums[r+1]){
r --
}
}else if(nums[i]+nums[j]+nums[l]+nums[r] > target){
r --
}else{
l ++
}
}
}
}
return ans
};