你已经知道,集合表示一组互不相同的元素(不重复的元素)。在字典中,存储的是[键,值]对,其中键名是用来查询特定元素的。字典和集合很相似,集合以[值,值]的形式存储元素,字典则是以[键,值]的形式来存储元素。字典也称作映射、符号表或关联数组。
在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. 两个数组的交集
代码思路:
我们可以利用字典,将其中一个数组的项存储。然后遍历另一个数组,将其中有值的数选出来,并推入到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. 有效的括号
代码思路:
我们观察有效的括号,可以发现,只要是有效的括号,那么最后一个接触到右括号的左括号,一定是和右括号对应的。且如果是嵌套的括号。那么最初的左括号一定最后匹配右括号(先进后出)。这里就可以考虑用栈这个数据结构。即碰到左括号就入栈,碰到右括号就出栈。如果出栈的右括号和左括号不对应,则直接返回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. 两数之和
代码思路:
这道题在普通的算法题中很常见,我们可以使用类似于婚介所的方法,即,你要找个对象(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. 无重复字符的最长子串
代码思路:
类似于取子串的题目,我们首先就要想到用双指针去维护一个滑动窗口。
- 不断的移动右指针,先去判断字典中是否存在右指针划过的项,如果不存在,则将其存入字典中,如果存在,则已经碰到了重复字符。这里可以开始移动左指针,移动左指针到字典记录指针的位置+1。
- 不断记录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. 最小覆盖子串
代码思路: 利用双指针去控制一个滑动窗口
- 先将目标值转换成字典,键放子字符,值放子字符的和。
- 不断移动右指针,同时,判断右指针的值是否存在于字典中,如果存在,则将字典值-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;
};