请分别用深度优先思想和广度优先思想实现一个拷贝函数? 每日一题系列(六)

8 篇文章 0 订阅
4 篇文章 0 订阅

前言

在前一篇已经介绍过了深度优先和广度优先

现在想通过这两个去实现一个深拷贝

深拷贝需要注意一些问题:环状数据

先了解过什么是环状数据后,再来进行深拷贝的实现

环状数据

相信大多数人都是第一次听说这个词,其实挺好理解的,先上代码看一看

const obj = {
  foo: {
    name: 'foo',
    bar: {
      name: 'bar'
      baz: {
        name: 'baz',
        aChild: null  //待会让它指向obj.foo
      }
    }
  }
}
obj.foo.bar.baz.aChild = obj.foo // foo->bar->baz->aChild->foo 形成环
JSON.stringify(obj) // => TypeError: Converting circular structure to JSON

可以看到,子节点属性赋值了父节点的引用,形成了一个foo->bar->baz->aChild->foo这样封闭的环状数据结构,这样的操作会导致死循环

因此,深拷贝时,肯定要判断对象是否是环状数据,判断的方法其实就是保存每一个属性在数组中,每次都判断下一个进数组的属性是否在数组中已经存在,如果存在,那么就是环状数据了

这里可以写个demo

function cycleDetector(obj) {

    var hasCircle = false,            //  定义一个变量,标志是否有环
        cache = [];                   //  定义一个数组,来保存对象类型的属性值

    (function(obj) {
        var keys = Object.keys(obj);              //获取当前对象的属性数组
        for (var i = 0; i < keys.length; i++) {
            var key = keys[i];
            var value = obj[key];
            if (typeof value == 'object' && value !== null) {
                var index = cache.indexOf(value)
                if (~index) { // 判断环状数据
                    hasCircle = true
                    break
                } else {
                    cache.push(value)
                    arguments.callee(value)
                    cache.pop()      //  注意:这里要推出数据,因为递归返回,后面遍历的属性不是这个数据的子属性
                }
            }
        }
    })(obj)

    return hasCircle
}

工具函数

// 工具函数
let _toString = Object.prototype.toString
let map = {
  array: 'Array',
  object: 'Object',
  function: 'Function',
  string: 'String',
  null: 'Null',
  undefined: 'Undefined',
  boolean: 'Boolean',
  number: 'Number'
}
let getType = (item) => {
  return _toString.call(item).slice(8, -1)
}
let isTypeOf = (item, type) => {
  return map[type] && map[type] === getType(item)
}

深度优先的深拷贝

let DFSdeepClone = (obj, visitedArr = []) => {
  let _obj = {}
  if (isTypeOf(obj, 'array') || isTypeOf(obj, 'object')) {
    let index = visitedArr.indexOf(obj)
    _obj = isTypeOf(obj, 'array') ? [] : {}
    if (~index) { // 判断环状数据 ~-1 == 0 一个骚操作而已
      _obj = visitedArr[index]
    } else {
      visitedArr.push(obj)
      for (let item in obj) {
        _obj[item] = DFSdeepClone(obj[item], visitedArr)
      }
    }
  } else if (isTypeOf(obj, 'function')) {
    _obj = eval('(' + obj.toString() + ')');
  } else {
    _obj = obj
  }
  return _obj
}

广度优先的拷贝

let BFSdeepClone = (obj) => {
    let origin = [obj],
      copyObj = {},
      copy = [copyObj]
      // 去除环状数据
    let visitedQueue = [],
      visitedCopyQueue = []
    while (origin.length > 0) {
      let items = origin.shift(),
        _obj = copy.shift()
      visitedQueue.push(items)
      if (isTypeOf(items, 'object') || isTypeOf(items, 'array')) {
        for (let item in items) {
          let val = items[item]
          if (isTypeOf(val, 'object')) {
            let index = visitedQueue.indexOf(val)
            if (!~index) {
              _obj[item] = {}
                //下次while循环使用给空对象提供数据
              origin.push(val)
                // 推入引用对象
              copy.push(_obj[item])
            } else {
              _obj[item] = visitedCopyQueue[index]
              visitedQueue.push(_obj)
            }
          } else if (isTypeOf(val, 'array')) {
            // 数组类型在这里创建了一个空数组
            _obj[item] = []
            origin.push(val)
            copy.push(_obj[item])
          } else if (isTypeOf(val, 'function')) {
            _obj[item] = eval('(' + val.toString() + ')');
          } else {
            _obj[item] = val
          }
        }
        // 将已经处理过的对象数据推入数组 给环状数据使用
        visitedCopyQueue.push(_obj)
      } else if (isTypeOf(items, 'function')) {
        copyObj = eval('(' + items.toString() + ')');
      } else {
        copyObj = obj
      }
    }
  return copyObj
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这个问题不难,我来给您讲解一下。 首先,我们需要用一个邻接表来表示这个有向图。邻接表是一个数组,其中每个元素对应一个顶点,每个元素存储与该顶点相邻的所有顶点。例如,如果顶点 1 与顶点 2、3 相邻,那么邻接表中第 1 个元素就存储一个链表或数组,其中包含 2 和 3。 接下来,我们来看深度优先遍历(DFS)的实现。DFS 遍历过程中,从起点开始,尽可能深地访问每个可达的节点,直到到达最深的节点,然后回溯到前一个节点,继续访问其他可达节点。具体实现如下: ```python def dfs(graph, start): visited = set() # 用一个集合来记录已经访问过的节点 stack = [start] # 用一个栈来记录待访问的节点 while stack: node = stack.pop() # 取出栈顶元素 if node not in visited: print(node, end=' ') visited.add(node) for neighbor in graph[node]: # 将当前节点的所有邻居入栈 if neighbor not in visited: stack.append(neighbor) ``` 接下来,我们来看广度优先遍历(BFS)的实现。BFS 遍历过程中,从起点开始,先访问所有与起点距离为 1 的节点,然后访问所有与起点距离为 2 的节点,依此类推。具体实现如下: ```python def bfs(graph, start): visited = set() # 用一个集合来记录已经访问过的节点 queue = [start] # 用一个队列来记录待访问的节点 while queue: node = queue.pop(0) # 取出队首元素 if node not in visited: print(node, end=' ') visited.add(node) for neighbor in graph[node]: # 将当前节点的所有邻居入队 if neighbor not in visited: queue.append(neighbor) ``` 好了,以上就是建立有向图并用 DFS 和 BFS 实现图的遍历的过程,希望能对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值