算法:查找表Map与Set(四)

一、集合(无序且唯一)

1、常用操作

  • 使用Set对象: new、add、delete、has、size
  • 迭代Set: 多种迭代方法、Set与Array互转、求交集/差集
let mySet = new Set();
// 添加
mySet.add(1);
mySet.add(5);
mySet.add(5);
// 1 ,5
mySet.add('some text')
let o = {a: 1, b: 2};
mySet.add(o);
mySet.add({a: 1, b: 2});

// 判断是否包含
const has = mySet.has(3) // false
mySet.delete(5)

// 迭代
for(let item of mySet) console.log(item)

for(let item of mySet.keys()) console.log(item)

for(let item of mySet.values()) console.log(item)

for(let [ key, value] of mySet.entries()) console.log(key, value)

// Set与Array互转
const myArr = [...mySet];
const myArr  = Array.from(mySet);

const mySet2 = new Set([1,2,3,4]);

// 求交集和差集
// 交集
const intersection = new Set([...mySet].filter(x => mySet2.has(x) ))
                                          // 先变成数组,再筛选set2中的值
// 差集
const diffetence = new Set([...mySet].filter(x => !mySet2.has(x) ))

// 去重
const arr = [1, 1, 3, 3]
const arr2 = [...new Set(arr)]

二、字典(键值对,Map)

1、字典的常用操作

const m = new Map();
// 增加
m.set('a', 'aa');
// 查
m.get('a');
// 删
m.delete('b');
m.clear(); // 删除所有元素
// 改(set会覆盖)
m.set('a', 'aaa');
20. 有效的括号 - 力扣(LeetCode) (leetcode-cn.com)

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
输入:s = "()"
输出:true

var isValid = function(s) {
    if(s.length % 2 === 1) return false;
    const stack = [];
    const map = new Map();
    map.set('(', ')');
    map.set('{', '}');
    map.set('[', ']');
    for(let i = 0; i < s.length; i += 1) {
         const c = s[i];
         if(map.has(c)){
             stack.push(c)
         } else {
              const t = stack[stack.length -1];
              if(map.get(t) === c) {
                  stack.pop()
              } else{
                  return false
              }
         }
    }
    return stack.length === 0;
}
1. 两数之和 - 力扣(LeetCode) (leetcode-cn.com)

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

var twoSum = function(nums, target) {
    const map = new Map();
    for(let i = 0; i < nums.length; i += 1) {
        const n = nums[i];
        const n2 = target - n;
        if(map.has(n2)) {
            return [map.get(n2), i]
        } else {
            map.set(n, i)
        }
    }
};
3. 无重复字符的最长子串 - 力扣(LeetCode) (leetcode-cn.com)

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

滑动窗口 + 查找表

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
 let l = 0; // 左指针为0
    let res = 0; // 最大窗口的长度
    const map = new Map();
    for(let r = 0; r < s.length; r ++) { // 右指针
       if(map.has(s[r]) && map.get(s[r]) >= l) {
            l = map.get(s[r]) + 1
       }
       res = Math.max(res, r - l + 1); // 窗口长度的最大值
       map.set(s[r], r)
    } 
    return res;
};
76. 最小覆盖子串 - 力扣(LeetCode) (leetcode-cn.com)

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"

滑动窗口+查找表

/**
 * @param {string} s
 * @param {string} t
 * @return {string}
 */
var minWindow = function(s, t) {
    let l = 0;
    let r = 0;
    const need = new Map();
    for(let c of t) {
        need.set(c, need.has(c) ? need.get(c) + 1: 1);
    }
    let needType = need.size;
    let res = '';
    while( r < s.length) {
        const c = s[r]
        if(need.has(c)) {
            need.set(c, need,get(c) - 1);
            if(need.get(c) === 0) needType -= 1
        }
        while(needType === 0) {
              console.log(s.substring(l, r + 1));
              const c2 = s[l];
              if(need.has(c2)) {
                     need.set(c2, need.get(c2) + 1);
                     if(need.get(c2) === 1) needType += 1
              }
              l += 1;
         }
         r += 1
    }
    return res
}
349. 两个数组的交集 - 力扣(LeetCode) (leetcode-cn.com)

给定两个数组,编写一个函数来计算它们的交集。
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

Map

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function(nums1, nums2){
   const map = new Map();
   nums1.forEach(n => {
       map.set(n, true)       
   })
   const res = []
   nums2.forEach(n => {
          if(map.get(n)){
               res.push(n)
               map.delete(n)
          }
   })
  return res
}

// 第2种
var intersection = function(nums1, nums2) {
     return [...[...new Set(nums1)].filter(n => new Set(nums2).has(n))]
}
// 第3种
var intersection = function(nums1, nums2) {
    return [...[...new Set(nums1)].filter(n => nums2.includes(n))]
};

Set

var intersection = function(nums1, nums2) {
    let setArray = new Set()
    for(let i = 0; i < nums1.length; i++) {
        setArray.add(nums1[i])
    }
    let setResult = new Set()
    for(let i = 0; i < nums2.length; i++) {
         if(setArray.has(nums2[i])){
               setResult.add(nums2[i])
         }
    }
   return Array.from(setResult)
}
350. 两个数组的交集 II - 力扣(LeetCode) (leetcode-cn.com)

给定两个数组,编写一个函数来计算它们的交集。
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersect = function(nums1, nums2) {
      if (nums1.length === 0 || nums2.length === 0) { return []}
        let map = new Map()
        for (let i = 0; i < nums1.length; i++) {
            if (map.has(nums1[i])){
                map.set(nums1[i], map.get(nums1[i])+1)
            }else {
                map.set(nums1[i], 1)
            }
        }
        let arr = []
        for (let i = 0; i < nums2.length; i++){
            if (map.has(nums2[i]) && map.get(nums2[i]) != 0){
                arr.push(nums2[i])
                map.set(nums2[i], map.get(nums2[i])-1)
            }
        }
        return arr;
};
242. 有效的字母异位词 - 力扣(LeetCode) (leetcode-cn.com)

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
输入: s = "anagram", t = "nagaram"
输出: true

var isAnagram = function(s, t) {
    if(s.length !== t.length) {
        return false
    }
    const map = new Map()
    s.split('').forEach(sItem => {
        map.set(sItem, map.has(sItem) ? map.get(sItem) + 1 : 1)
    })

    t.split('').forEach(tItem => {
        if(map.has(tItem)) {
            map.set(tItem, map.get(tItem) - 1)
        } else {
            return false
        }
    })
    return [...map.values()].every(item => item === 0)
};
202. 快乐数 - 力扣(LeetCode) (leetcode-cn.com)

输入:19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

/**
 * @param {number} n
 * @return {boolean}
 */
var isHappy = sum =>{
    const ARRAY = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    const ZERO = '0'.charCodeAt(0) // ZERO 为48
    let hasSeen = new Set()
    while(true){
        if(sum === 1) return true
        if (hasSeen.has(sum)){
            return false
        }
        hasSeen.add(sum)
        let str = sum.toString()
        sum = 0
        for (let i=0; i<str.length; i++){
            sum += ARRAY[str.charCodeAt(i) - ZERO]
        }
    }
}
290. 单词规律 - 力扣(LeetCode) (leetcode-cn.com)

给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
输入: pattern = "abba", str = "dog cat cat dog"
输出: true

/**
 * @param {string} pattern
 * @param {string} s
 * @return {boolean}
 */
var wordPattern = function(pattern, s) {
    var arr = s.split(' ');
    if(pattern.length != arr.length) return false;
    for(let i=0; i< pattern.length; i++){
        // indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置
        if(pattern.indexOf(pattern[i]) != arr.indexOf(arr[i])){ 
            return false
        }
    }
    return true
};
205. 同构字符串 - 力扣(LeetCode) (leetcode-cn.com)

给定两个字符串 s 和 t,判断它们是否是同构的。
输入:s = "egg", t = "add"
输出:true

/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isIsomorphic = function(s, t) {
    let hash1 = new Map()
    let hash2 = new Map()
    for(let i=0; i<s.length; i++){
        let S = s[i]
        let T = t[i]
        if(hash1.has(S)){
            if(hash1.get(S) !== T) return false
        } else if(hash2.has(T)) {
            if(hash2.get(T) !== S) return false
        } else {
            hash1.set(S, T)
            hash2.set(T,S)
        }
    }
    return true
};
451. 根据字符出现频率排序 - 力扣(LeetCode) (leetcode-cn.com)

给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
输入:"tree"
输出:"eert"

/**
 * @param {string} s
 * @return {string}
 */
var frequencySort = function(s) {
    const map = new Map()
    s.split('').forEach(item => {
        map.set(item, map.has(item) ? map.get(item) + 1: 1)
    })
    let array = [...map].sort((arr1, arr2) => arr2[1] - arr1[1])
    return array.map(item => item[0].repeat(item[1])).join("")
};
1. 两数之和 - 力扣(LeetCode) (leetcode-cn.com)

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    const map = new Map();
    for(let i = 0; i < nums.length; i += 1) {
        const n = nums[i];
        const n2 = target - n;
        if(map.has(n2)) {
            return [map.get(n2), i]
        } else {
            map.set(n, i)
        }
    }
};
15. 三数之和 - 力扣(LeetCode) (leetcode-cn.com)

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    // 从小到大排序
    nums = nums.sort((a, b) => a - b)
    let result = []
    for (let i = 0; i < nums.length - 2; i++) {
        if (nums[i] > 0) break; // 最小数字如果大于0,则后面不会有和为0的组

        if (nums[i] === nums[i - 1]) continue; // 跳过相同的数字
        // 其中,nums[i]是最小的数字,nums[k]是最大的数字
        let j = i + 1
        let k = nums.length - 1

        // 第二个数的索引不能超过第三个数的索引
        while (j < k) {
            let sum = nums[i] + nums[j] + nums[k]
            if (sum > 0) {
                // 如果结果大于0,那么最大的数字索引减一,寻找更小的数字
                do {k--} while (nums[k] === nums[k + 1]) // 跳过相同的数字
            } else {
                // 如果结果等于0,加入数组
                if (!sum) result.push([nums[i], nums[j], nums[k]])
                // 如果结果不大于0,那么次大的数字索引加一,寻找更大的数字
                do {j++} while (nums[j] === nums[j - 1]) // 跳过相同的数字
            }
        }
    }
    return result
};
18. 四数之和 - 力扣(LeetCode) (leetcode-cn.com) 【不理解】

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

思路:回溯剪枝

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
    const result = [];
    nums.sort((a, b) => a-b); // 排序
    const set = new Set(); // 去重
    // 回溯,path 路径 sum 路径和 index 数组索引
    const recursion = (path, sum, index) => {
        for(let i = index; i < nums.length; i++) {
            // 设置下一次遍历的和
            const nextSum = sum + nums[i];

            // 路径和大于目标和,终止
            if(nextSum > 0 && nextSum > target) {
                break;
            }

            // 路径path添加当前元素
            path.push(nums[i])

            // 路径长度到4
            if(path.length === 4) {
                if(nextSum === target) {
                    const pathStr = path.join('-')

                    if(!set.has(pathStr)) {
                        result.push(path.slice())
                        set.add(pathStr)
                    }
                }
            }else {
                recursion(path, nextSum, i+ 1)
            }
            path.pop()
        }
    }
    recursion([], 0, 0)
    return result
};
16. 最接近的三数之和 - 力扣(LeetCode) (leetcode-cn.com)

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

双指针

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var threeSumClosest = function(nums, target) {
    nums.sort((a,b) => a-b)
    let res = nums[0] + nums[1] + nums[2]
    let len = nums.length;
    for(let i = 0; i < len; i++){
        let left = i+1, right = len -1
        while(left < right) {
            let sum = nums[i] + nums[left] + nums[right]
            if(Math.abs(sum - target) < Math.abs(res - target)) {
                res = sum
            }
            if(sum < target){
                left ++
            } else if(sum > target) {
                right--;
            } else {
                return res
            }
        }
    }
    return res
};
454. 四数相加 II - 力扣(LeetCode) (leetcode-cn.com)

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
输出:2
解释:
(0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
(1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @param {number[]} nums3
 * @param {number[]} nums4
 * @return {number}
 */
var fourSumCount = function(nums1, nums2, nums3, nums4) {
    // 查找表
    let map = new Map()
    // 遍历A和B
    nums1.forEach(item1 => {
        nums2.forEach(item2 => {
            // nums1和nums2的和出现的次数存在map中
            const sum = item1 + item2
            map.has(sum) ? map.set(sum, map.get(sum) + 1) : map.set(sum, 1)
        })
    })
    let count = 0
    nums3.forEach(item3 => {
        nums4.forEach(item4 => {
            const sum = item3 + item4
            if(map.has(-sum)) {
                count += map.get(-sum)
            }
        })
    })
    return count
};
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容