map和set的用法及树形的深度优先(dfs)和广度优先算法(bfs)

javascript基础

Map and Set(映射和集合)

map

是一个带键的数据项的集合,就像一个 Object 一样。 但是它们 的差别是 Map 允许任何类型的键(key)。

它的方法和属性如下:
  • new Map() —— 创建 map。

  • map.set(key, value) —— 根据键存储值。返回 map 自身。

  • map.get(key) —— 根据键来返回值,如果 map 中不存在对应的 key,则返回 undefined

  • map.has(key) —— 如果 key 存在则返回 true,否则返回 false

  • map.delete(key) —— 删除指定键的值。

  • map.clear() —— 清空 map。

  • map.size —— 返回当前元素个数。

如果要在 map 里使用循环,可以使用以下三个方法:
  • map.keys() —— 遍历并返回所有的键(returns an iterable for keys),
  • map.values() —— 遍历并返回所有的值(returns an iterable for values),
  • map.entries() —— 遍历并返回所有的实体(returns an iterable for entries)[key, value]for..of 在默认情况下使用的就是这个。

   let recipeMap = new Map([
    ['cucumber', 500],
    ['tomatoes', 350],
    ['onion',    50]
  ]);
  
  // 遍历所有的键(vegetables)
  for (let vegetable of recipeMap.keys()) {
    alert(vegetable); // cucumber, tomatoes, onion
  }
  
  // 遍历所有的值(amounts)
  for (let amount of recipeMap.values()) {
    alert(amount); // 500, 350, 50
  }
  
  // 遍历所有的实体 [key, value]
  for (let entry of recipeMap) { // 与 recipeMap.entries() 相同
    alert(entry); // cucumber,500 (and so on)
  }

// 对每个键值对 (key, value) 运行 forEach 函数
recipeMap.forEach( (value, key, map) => {
  alert(`${key}: ${value}`); // cucumber: 500 etc
});

set

是一个特殊的类型集合 —— “值的集合”(没有键),它的每一个值只能出现一次。

var set = new Set([1,2,3]); // {1,2,3}
var arr = Array.from(set);//[1,2,3]

它的主要方法如下:
  • new Set(iterable) —— 创建一个 set,如果提供了一个 iterable 对象(通常是数组),将会从数组里面复制值到 set 中。

  • set.add(value) —— 添加一个值,返回 set 本身

  • set.delete(value) —— 删除值,如果 value 在这个方法调用的时候存在则返回 true ,否则返回 false

  • set.has(value) —— 如果 value 在 set 中,返回 true,否则返回 false

  • set.clear() —— 清空 set。

  • set.size —— 返回元素个数。

如果要在 set 里使用循环,可以使用以下三个方法:
  • set.keys() —— 遍历并返回所有的值(returns an iterable object for values),
  • set.values() —— 与 set.keys() 作用相同,这是为了兼容 Map
  • set.entries() —— 遍历并返回所有的实体(returns an iterable object for entries)[value, value],它的存在也是为了兼容 Map

树形数据

深度优先算法(dfs)

遍历规则:不断地沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。

最后得出的结果为:ABDECFHG。

深度优先算法-递归

function treeIterator(tree: any[], func: (arg0: any) => boolean) {
      tree.forEach((node) => {
        if(!func(node)){
            return;
        }
        node.children && treeIterator(node.children, func)
      })
    }

广度优先算法(bfs)

遍历规则:

  1. 先访问完当前顶点的所有邻接点。(应该看得出广度的意思)

  2. 先访问顶点的邻接点先于后访问顶点的邻接点被访问。

    最后得出的结果为:ABCDEFGH。

    广度优先算法 利用队列先进先出的方式进行遍历。

    function treeIteratorBfs(tree:any[], func: (arg0: any) => boolean) {
          let node, nodeQueue = [...tree]
          while ((node = nodeQueue.shift())) {
            if(!func(node)){
                return;
              }
            node.children && nodeQueue.push(...node.children)
          }
        }
    
N叉树的最大深度–广度优先
function treeIteratorMaxDepth(tree:any[]) {
      let node = tree[0], index = 1;
      while(node.children.length>0){
        index++;
        const children = node.children.reduce((pre: any[],cur: { children: any; })=>{
             cur.children &&  (pre = pre.concat(cur.children))
             return pre
          },[])
       node = {children};
      }
      return index;
    }

思考

1、Map 和 Object 的区别是什么,什么情况下需要使用 Map ?
Map 和 Object 的区别
  • 键:Object遵循普通的字典规则,键必须是单一类型,并且只能是整数、字符串或是Symbol类型。但在Map中,key可以为任意数据类型(Object, Array等)。
  • 元素顺序:Map会保留所有元素的顺序,而Object并不会保证属性的顺序。
  • 继承:Map是Object的实例对象,而Object显然不可能是Map的实例对象。
什么情况下需要使用 Map(映射)
  • Map是一个纯哈希结构,而Object不是(它拥有自己的内部逻辑)。使用delete对Object的属性进行删除操作存在很多性能问题。所以,针对于存在大量增删操作的场景,使用Map更合适。
  • 不同于Object,Map会保留所有元素的顺序。Map结构是在基于可迭代的基础上构建的,所以如果考虑到元素迭代或顺序,使用Map更好,它能够确保在所有浏览器中的迭代性能。
  • Map在存储大量数据的场景下表现更好,尤其是在key为未知状态,并且所有key和所有value分别为相同类型的情况下。
  • 获取对象的键值对的个数,建议map。map有size属性。
2、Set 和 Array 的区别是什么,什么情况下需要使用 Set ?
Set 和 Array 的区别

它们之间最大的差别就是Array中的元素是可以重复的,而Set中的元素不可重复。除此之外,Array被认为是一种索引集合,而Set是一种键的集合。

  • 索引集合是指按下标作为索引值排序的数据结构
  • 键的集合使用key访问元素,访问顺序与元素插入顺序一致。
什么情况下需要使用 Set
  • 数组去重
  • 获取两个集合的并集,交集,差集。
  • 判断两个集合是否为子集或超集。
3、不用递归如何实现树的深度优先(BFS)搜索?

使用递归方法实现对树的遍历效率非常低,下面利用栈的特性来实现对树的深度优先遍历;

function dfsD(headerNode: any| any[], func: (arg0: any) => boolean){
  const stack: any[] = Array.isArray(headerNode)
  ? headerNode.reverse(): [headerNode];

while (stack.length > 0) {
  const node = stack.pop();
  func(node);
  if (Array.isArray(node.children)) {
    stack.push(...node.children.reverse());
  }
}
}
4、JSON.prase 和 JSON.stringify 有哪些缺点?

JSON.prase 和 JSON.stringify可以实现深拷贝。

  • 如果obj里面存在时间对象,JSON.parse(JSON.stringify(obj))之后,时间对象变成了字符串。
  • 如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象。
  • 如果obj里有属性值是函数,undefined,则序列化的结果该属性丢失。
  • 如果obj里有属性值是NaN、Infinity和-Infinity,则序列化的结果会变成null。
  • JSON.stringify()只能序列化对象的可枚举的自有属性。如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor。
  • 如果对象中存在循环引用的情况也无法正确实现深拷贝。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值