数据结构-字典

766 阅读3分钟

你已经知道,集合表示一组互不相同的元素(不重复的元素)。在字典中,存储的是[键,值]对,其中键名是用来查询特定元素的。字典和集合很相似,集合以[值,值]的形式存储元素,字典则是以[键,值]的形式来存储元素。字典也称作映射、符号表或关联数组。

在ES6当中,新增了Map类来作为字典这种数据结构。

Map的几种方法

set(key: any, value: any)

用于设定字典的键和值

const m = new Map();
m.set('a', 'aaa');
m.set('b', 'bbb');
m.set('c', 'ccc');
console.log(m); // Map(3) {"a" => "aaa", "b" => "bbb", "c" => "ccc"}

has(key: any)

用于判定字典中是否含有某个传入的键

const m = new Map();
m.set('a', 'aaa');
m.set('b', 'bbb');
m.set('c', 'ccc');

console.log(m.has('c')); // true
console.log(m.has('d')); // false

get(key: any)

用于获取字典中某个键对应的值

const m = new Map();
m.set('a', 'aaa');
m.set('b', 'bbb');
m.set('c', 'ccc');

console.log(m.get('c')); // ccc
console.log(m.has('d')); // undefined

size

用于获取字典的长度

const m = new Map();
m.set('a', 'aaa');
m.set('b', 'bbb');
m.set('c', 'ccc');

console.log(m.size); // 3

delete(key: any)

用于删除某个字典值

const m = new Map();
m.set('a', 'aaa');
m.set('b', 'bbb');
m.set('c', 'ccc');
m.delete('c');
console.log(m); //Map(2) {"a" => "aaa", "b" => "bbb"}

clear()

用于清空字典

const m = new Map();
m.set('a', 'aaa');
m.set('b', 'bbb');
m.set('c', 'ccc');
m.clear();
console.log(m); //Map(0) {}

字典的一些算法题

349. 两个数组的交集

image.png

代码思路:

我们可以利用字典,将其中一个数组的项存储。然后遍历另一个数组,将其中有值的数选出来,并推入到res数组当中。

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function(nums1, nums2) {
    const m = new Map();
    const result = [];
    nums1.forEach(item => {
        m.set(item, true);
    })
    nums2.forEach(item => {
        if (m.has(item)) {
            result.push(item);
            m.delete(item);
        }
    })
    return result
};

20. 有效的括号

image.png

代码思路:

我们观察有效的括号,可以发现,只要是有效的括号,那么最后一个接触到右括号的左括号,一定是和右括号对应的。且如果是嵌套的括号。那么最初的左括号一定最后匹配右括号(先进后出)。这里就可以考虑用栈这个数据结构。即碰到左括号就入栈,碰到右括号就出栈。如果出栈的右括号和左括号不对应,则直接返回false。

/**
 * @param {string} s
 * @return {boolean}
 */
var isValid = function(s) {
    const arr = s.split('');
    if (arr.length % 2 !== 0){
        return false;
    }

    const m = new Map();
    m.set('(', ')');
    m.set('[', ']');
    m.set('{', '}');

    const stack = [];
    for(let i = 0 ; i < arr.length ; i++) {
        const item = arr[i];
        if (m.has(item)){
            stack.push(item);
        }
        if (m.get(stack[stack.length - 1]) === item) {
            stack.pop();
        } else if (!m.has(item)){
            return false;
        }
    }
    return stack.length === 0;
};

1. 两数之和

image.png

代码思路:

这道题在普通的算法题中很常见,我们可以使用类似于婚介所的方法,即,你要找个对象(target)。然后找的过程中,如果有你需要的对象,就返回,如果没有,你就得先登记资料(存储字典)。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    const m = new Map();
    for (let i = 0; i < nums.length; i++ ) {
        if(m.has(nums[i])){
            return [i, m.get(nums[i])];
        } else {
            m.set(target - nums[i], i);
        }
    }
};

3. 无重复字符的最长子串

image.png

代码思路:

类似于取子串的题目,我们首先就要想到用双指针去维护一个滑动窗口。

  1. 不断的移动右指针,先去判断字典中是否存在右指针划过的项,如果不存在,则将其存入字典中,如果存在,则已经碰到了重复字符。这里可以开始移动左指针,移动左指针到字典记录指针的位置+1。
  2. 不断记录r - l + 1的长度。(利用Math.max函数)。
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let l = 0;
    let max = 0;
    let m = new Map();
    for (let r = 0; r < s.length;r++){
        const c = s[r];
        if (m.has(c) && m.get(c) >= l){// 这里的注意点在于要保证字典的值位置在窗口内
            l = m.get(c) + 1;
        }
        m.set(c, r);
        max = Math.max(max, r - l + 1);

    }
    return max;
};

76. 最小覆盖子串

image.png

代码思路: 利用双指针去控制一个滑动窗口

  1. 先将目标值转换成字典,键放子字符,值放子字符的和。
  2. 不断移动右指针,同时,判断右指针的值是否存在于字典中,如果存在,则将字典值-1。当字典值为0时,证明已经有一种类型的字符已经满足要求了。当所有类型的字符满足要求后。我们就要开始缩小子串长度(移动左指针),直到左指针的字符是字典中的值,这时候子串已经不满足条件了。我们再开始继续移动右指针直到遍历完成。

代码实现:

/**
 * @param {string} s
 * @param {string} t
 * @return {string}
 */
var minWindow = function(s, t) {
    let l = 0;
    let r = 0;
    let res = '';
    let m = new Map();
    for (let v of t) {
        m.set(v, m.has(v)? m.get(v) + 1: 1);
    }
    let needType = m.size;

    while (r < s.length){
        const c1 = s[r];
        if (m.has(c1)) {
            m.set(c1, m.get(c1) - 1);
            if (m.get(c1) === 0) {
                needType -=1;
            }

            while(needType === 0) {// 找到符合条件的子串
                const c2 = s[l];
                const newRes = s.substring(l, r + 1);
                if (!res || newRes.length < res.length) {res = newRes}; // 不断刷新最小子串
                if (m.has(c2)) {
                    m.set(c2, m.get(c2) + 1);
                    if (m.get(c2) === 1){needType +=1};
                }
                l++; // 缩小窗口,减少子串长度
            }
        }
        r++;
    }
    return res;
};