数据结构(六)字典


字典是什么?

  • 与集合类似,字典也是一种存储唯一值的数据结构,但它是以键值对的形式来存储
  • ES6中有字典,名为Map
  • 字典的常用操作:键值对的增删改查
const m = new Map();

// 增
m.set('a', 'aa'); // key : value
m.set('b', 'bb');

// 删
m.delete('b');
// m.clear(); // 删除所有

// 改
m.set('a', 'aaaa')

// 查
m.get('a')

LeetCode #349 两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

  • 输入:nums1 = [1,2,2,1], nums2 = [2,2]
  • 输出:[2]

示例 2:

  • 输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
  • 输出:[9,4]
     

说明:

输出结果中的每个元素一定是唯一的。
我们可以不考虑输出结果的顺序。

解题思路:

  1. 求nums1和nums2都有的值
  2. 用字典建立一个映射关系,记录nums1里有的值
  3. 遍历nums2,找出nums1里边也有的值

解题步骤:

  1. 新建一个字典,遍历nums1,填充字典
  2. 遍历nums2,遇到字典里的值就选出,并从字典里删除(防止重复)

复杂度:

  1. 时间复杂度:O(m+n)
  2. 空间复杂度:O(m)
/**
 * @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;
};

LeetCode #20 有效的括号

给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 1:

  • 输入: "()"
  • 输出: true

示例 2:

  • 输入: "()[]{}"
  • 输出: true

示例 3:

  • 输入: "(]"
  • 输出: false

示例 4:

  • 输入: "([)]"
  • 输出: false

示例 5:

  • 输入: "{[]}"
  • 输出: true

解题思路:
利用map建立括号的字典集
将之前的复杂的判断条件改写

解题步骤:

复杂度:
1.时间复杂度:O(n)
2.空间复杂度:O(n)

/**
 * @param {string} s
 * @return {boolean}
 */
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 top = stack[stack.length-1];
            if(
                map.get(top) === c
            ){
                stack.pop();
            }else{
                return false;
            }
        }
    }
    return stack.length === 0;
};

LeetCode #1 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

方法一:

var twoSum = function(nums, target) {
    let result = []
    for(let i=0; i<nums.length; i++){
        for(let j=i+1; j<nums.length;j++){
            if(nums[i]+nums[j] == target){
                result.push(i)
                result.push(j)
            }
        }
    }
    return result
};

方法二:

/**
 * @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++){
        const n = nums[i]
        const n2 = target-n;
        if(map.has(n2)){
            return [map.get(n2), i]
        }else{
            map.set(n, i);
        }
    }
};

LeetCode #3 无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 :

  • 输入: "abcabcbb"
  • 输出: 3
  • 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
  • 输入: "bbbbb"
  • 输出: 1
  • 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
  • 输入: "pwwkew"
  • 输出: 3
  • 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
      请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

解题思路:

  1. 先找出所有的不包含重复字符的字串
  2. 找出长度最大那个字串,返回其长度即可

解题步骤:

  1. 用双指针维护一个滑动窗口,用来剪切字串
  2. 不断移动右指针,遇到重复字符,就把左指针移动到重复字符的下一位
  3. 过程中,记录所有窗口的长度,并返回最大值

复杂度:

  1. 时间复杂度 O(n)
  2. 空间复杂度 O(m)m即字符串中不重复的个数
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    // 左右指针
    let left = 0;
    let res = 0; 
    const map = new Map();
    for(let right=0; right<s.length; right++){
        if(map.has(s[right]) && map.get(s[right]) >= left){ // 如果遇到重复字符,左指针移动到右指针下一位 && map中的重复字符下标必须在窗口中
            left = map.get(s[right]) + 1;
        }
        res = Math.max(res, right - left + 1); // 新窗口的右指针与res的较大值
        map.set(s[right], right);
    }
    return res;
};

LeetCode #76 最小覆盖子串【Hard】

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 :

  • 输入:s = "ADOBECODEBANC", t = "ABC"
  • 输出:"BANC"
  • 输入:s = "a", t = "a"
  • 输出:"a"

 
提示:

  • 1 <= s.length, t.length <= 105
  • s 和 t 由英文字母组成

进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗?

解题思路:

  1. 先找出所有的包含T的字串
  2. 找出长度最小的那个字串,返回

解题步骤:

  1. 用双指针维护一个滑动窗口,用来枚举所有子串
  2. 移动右指针,找到包含T的字串,移动左指针,尽量减少包含T的子串的长度

复杂度:

  1. 时间复杂度 O(n+m)n是s的长度, m是t的长度
  2. 空间复杂度 O(m)m是t里边不同字符的个数
/**
 * @param {string} s
 * @param {string} t
 * @return {string}
 */
var minWindow = function(s, t) {
    // 创建左右指针
    let left = 0;
    let right = 0;
    const need = new Map();
    for(let c of t){
        need.set(c, need.has(c) ? need.get(c) + 1 :1);
    }
    // console.log(need);
    let needType = need.size;
    let res = '';
    // 移动右指针
    while(right < s.length){
        const c = s[right] // 获取右指针当前的字符
        if(need.has(c)){ // 如果右指针在我们当前的列表里面
            need.set(c, need.get(c)-1); // need中要减去1
            // 满足t所有的都在need
            if(need.get(c) === 0){
                needType -= 1;
            }
        }
        while(needType === 0){ // 缩小字符串范围
            // console.log(s.substring(left, right+1)) // 所有子串
            const newRes = s.substring(left, right+1)
            if(!res || newRes.length < res.length){ // 找出长度最小的子 如果res为空 则先赋值给它newRes
                res = newRes;
            }
            const c2 = s[left]; //左指针当前的字符
            if(need.has(c2)){
                need.set(c2, need.get(c2)+1); // 需求增加
                if(need.get(c2) === 1){ // 重新需要这个字符
                    needType += 1; // 更新needtype
                }
            }
            left += 1;
        }
        right += 1;
        // 判断移动右指针时,滑动窗口(子串)包含t的所有字符:用字典
    }
    return res
};

字典-章节总结

  • 与集合类似,字典也是一种存储唯一值的数据结构,但它是以键值对的形式来存储
  • ES6中有字典,名为Map(映射)
  • 字典的常用操作:键值对的增,删,改,查

思考

  1. 在实际工作中使用字典完成一次映射操作的场景

    有一次需求需要自己对DOM树进行遍历,由于遍历的规则可能导致重复访问节点,就用Map将访问过的DOM节点映射为true,然后处理节点前进行判断。

    const visited = new Map();
    visited.set(node, true);
    ...
    if (!visited.get(node)) {  // 当前节点未访问过,进行处理
    }
  2. 用Chrome的Profile工具或者其他任意前端性能测试工具,测试Map和Object频繁增删操作的性能,谁高谁低?

声明:Xuhao's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 数据结构(六)字典


Carpe Diem and Do what I like