es6 Map的使用:由一道算法题引出的业务场景回顾

Posted 恪愚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了es6 Map的使用:由一道算法题引出的业务场景回顾相关的知识,希望对你有一定的参考价值。

在 leetCode 上看到了这样一道算法题:给定一个整数数组和一个整数目标,返回两个数字的索引,使它们加起来等于目标。
你可能会假设每个输入都只有一个解决方案,并且,你可能不会两次使用相同的元素。
你可以按任何顺序返回答案。

示例 1:

Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

这很容易想到 双for循环

const twoSum = (nums, target) => 
  const len = nums.length
  for (let i = 0; i < len; i++) 
    for (let j = i + 1; j < len ; j++) 
      if (nums[ i] + nums[ j ] === target) 
        return [ i, j ]
      
    
  

有两点需要注意:

  1. 通常,当使用两个“for”循环来解决一个问题时,我们需要意识到算法的时间复杂度(O(n^2))可以被优化
  2. 当一个场景足够简单时,我们应当将优化的目光放到“数据结构”上

那么,什么结构既能满足“快速查找”的目的,又能避免循环呢?Map

这里说Map只是为了引出业务场景中的使用。事实上,当数据量够小时我们通常使用的是Object,而当涉及到大量数据或对顺序有要求或需要遍历时选择 Map

const twoSum = (nums, target) => 
  const len = nums.length
  const sumCache = 
  for (let i = 0; i < len; i++) 
    const value = nums[ i ]
    const diff = target - value
    if (typeof sumCache[ diff ] !== 'undefined') 
      return [ sumCache[ diff ], i ]
     else 
      sumCache[ value ] = i
    
  

是不是非常简单明了?

在实际业务中,比如笔者所在的电商公司,有时候会涉及到这样的场景:商家设置了商品参与活动,但是活动要求不能有一件商品同时参与两个活动。所以当保存时需要拦截并反馈到页面上!

这个行为可以拆分为两步:

  1. 像后端发送请求,后端拦截
  2. 前端拿到错误的商品,让组件处理,展示指定错误文案到对应的商品下面

受历史逻辑影响,早些时候有的项目中我们的后端同学会返回这样的结构:


	result: null,
	status: 
		code: 错误状态码,
		errIds: ['xxxxx', 'xxxxxxx']
	

没错,错误文案固定为“该商品有误”,后端只返回错误商品 id 数组。

那么,我们有两种选择:1⃣️双循环商品数组和错误 id 数组,找到 id 匹配项,更改errorMessage 字段;2⃣️先循环错误 id 数组,将其改造为Object(如果数据量过大则是 Map),然后循环商品数组,找到 id 匹配项,更改errorMessage 字段。

首选2!
(如果数据量很小还没啥,数据量一大,双重 for 循环的劣势就显现出来了。我认为同一数据量下两次 for 循环效率要远高于两层 for 循环。)

let errMap = ;
res.fxCheckMarkItems.forEach(item => errMap[item] = "该商品有误")
this.fxCheckMarkItems(errMap);

// 负利润校验
fxCheckMarkItems(checkMap)
    if(checkMap)
        let changeItems = this.formData.changeItems;
        for(let i=0;i<changeItems.length;i++)
            let item = changeItems[i];
            let itemId = String(item.itemId);
            if(checkMap[itemId])
                item.itemFxErrorMessage = checkMap[itemId];
            
            if(item.skuList.length)
                // 处理sku
            
        
        this.$refs.refItemSku.$_setData(changeItems);
    
,

还有一个场景非常适合 Map:设计一个遵循最近最少使用 (LRU算法) 缓存约束的数据结构。
实现 LRUCache 类:

  • LRUCache(int capacity) 用正容量初始化 LRU 缓存。
  • int get(int key) 如果key存在则返回key的值,否则返回-1。
  • void put(int key, int value) 如果键存在,则更新键的值。否则,将键值对添加到缓存中。如果键的数量超过此操作的容量,则驱逐最近最少使用的键。

函数 get 和 put 必须各自以 O(1) 平均时间复杂度运行。

使用数组和对象的组合。

let LRUCache = function (capacity) 
    this.keys = []
    this.cache = 
    this.capacity = capacity

LRUCache.prototype.get = function (key) 
    if (typeof this.cache[key] !== 'undefined') 
        remove(this.keys, key)
        this.keys.push(key)
        return this.cache[key]
    
    return -1

LRUCache.prototype.put = function (key, value) 
    if (typeof this.cache[key] !== 'undefined') 
        this.cache[key] = value
        remove(this.keys, key)
        this.keys.push(key)
     else 
        this.keys.push(key)
        this.cache[key] = value
        if (this.keys.length > this.capacity) 
            removeCache(this.cache, this.keys, this.keys[0])
        
    


function remove(arr, key) 
    if (arr.length) 
        const index = arr.indexOf(key)
        if (index > -1) 
            return arr.splice(index, 1)
        
    


function removeCache(cache, keys, key) 
    cache[key] = null
    remove(keys, key)

const lRUCache = new LRUCache(2)
console.log(lRUCache.put(1, 1)) // The cache is 1=1
console.log(lRUCache.put(2, 2)) // The cache 1=1, 2=2
console.log(lRUCache.get(1))    // Return 1
console.log(lRUCache.put(3, 3)) // This operation invalidates keyword 2 and the cache is 1=1, 3=3
console.log(lRUCache.get(2))    // Return 1(Not found)
console.log(lRUCache.put(4, 4)) // This operation invalidates keyword 1 and the cache is 4=4, 3=3
console.log(lRUCache.get(1) )   // Return -1 (not found)
console.log(lRUCache.get(3))    // Return 3
console.log(lRUCache.get(4) )   // Return 4

Map

在第一个实现中,我们使用一个数组来存储每个键被访问(get,set)的顺序,这是一种繁琐的方式。
还是那个问题:是否有任何其他解决方案可以使其更方便并且不需要对阵列进行额外维护?
我们可以使用 Map 来做到这一点!

let LRUCache = function (capacity) 
    this.cache = new Map()
    this.capacity = capacity
;

LRUCache.prototype.get = function (key) 
    if (this.cache.has(key)) 
        const value = this.cache.get(key)
        this.cache.delete(key)
        this.cache.set(key, value)
        return value
    
    return -1
;

LRUCache.prototype.put = function (key, value) 
    if (this.cache.has(key)) 
        this.cache.delete(key)
     else 
        if (this.cache.size >= this.capacity) 
            this.cache.delete(this.cache.keys().next().value)
        
    
    this.cache.set(key, value)
;
const lRUCache = new LRUCache(2)
console.log(lRUCache.put(1, 1)) // The cache is 1=1
console.log(lRUCache.put(2, 2)) // The cache 1=1, 2=2
console.log(lRUCache.get(1))    // Return 1
console.log(lRUCache.put(3, 3)) // This operation invalidates keyword 2 and the cache is 1=1, 3=3
console.log(lRUCache.get(2))    // Return 1(Not found)
console.log(lRUCache.put(4, 4)) // This operation invalidates keyword 1 and the cache is 4=4, 3=3
console.log(lRUCache.get(1) )   // Return -1 (not found)
console.log(lRUCache.get(3))    // Return 3
console.log(lRUCache.get(4) )   // Return 4

以上是关于es6 Map的使用:由一道算法题引出的业务场景回顾的主要内容,如果未能解决你的问题,请参考以下文章

es6 Map的使用:由一道算法题引出的业务场景回顾

从一道面试题掌握ES6的综合运用(有彩蛋)

每周一道算法题005:切木棒

计算机操作系统关于PV操作的一道题

每天一道算法题

由 leetcode 136. Single Number 引出的异或总结