DFS & BFS - 图1

深度优先遍历DFS 与树的先序遍历比较类似。
假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

  1. let deepTraversal1 = (node, nodeList = []) => {
  2. if (node !== null) {
  3. nodeList.push(node)
  4. let children = node.children
  5. for (let i = 0; i < children.length; i++) {
  6. deepTraversal1(children[i], nodeList)
  7. }
  8. }
  9. return nodeList
  10. }
  11. let deepTraversal2 = (node) => {
  12. let nodes = []
  13. if (node !== null) {
  14. nodes.push(node)
  15. let children = node.children
  16. for (let i = 0; i < children.length; i++) {
  17. nodes = nodes.concat(deepTraversal2(children[i]))
  18. }
  19. }
  20. return nodes
  21. }
  22. // 非递归
  23. let deepTraversal3 = (node) => {
  24. let stack = []
  25. let nodes = []
  26. if (node) {
  27. // 推入当前处理的node
  28. stack.push(node)
  29. while (stack.length) {
  30. let item = stack.pop()
  31. let children = item.children
  32. nodes.push(item)
  33. // node = [] stack = [parent]
  34. // node = [parent] stack = [child3,child2,child1]
  35. // node = [parent, child1] stack = [child3,child2,child1-2,child1-1]
  36. // node = [parent, child1-1] stack = [child3,child2,child1-2]
  37. for (let i = children.length - 1; i >= 0; i--) {
  38. stack.push(children[i])
  39. }
  40. }
  41. }
  42. return nodes
  43. }

DFS & BFS - 图2

广度优先遍历 BFS
从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中所有已被访问的顶点的邻接点都被访问到。 如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。

  1. let widthTraversal2 = (node) => {
  2. let nodes = []
  3. let stack = []
  4. if (node) {
  5. stack.push(node)
  6. while (stack.length) {
  7. let item = stack.shift()
  8. let children = item.children
  9. nodes.push(item)
  10. // 队列,先进先出
  11. // nodes = [] stack = [parent]
  12. // nodes = [parent] stack = [child1,child2,child3]
  13. // nodes = [parent, child1] stack = [child2,child3,child1-1,child1-2]
  14. // nodes = [parent,child1,child2]
  15. for (let i = 0; i < children.length; i++) {
  16. stack.push(children[i])
  17. }
  18. }
  19. }
  20. return nodes
  21. }

DFS & BFS - 图3

对象的深拷贝

  1. // 工具函数
  2. let _toString = Object.prototype.toString
  3. let map = {
  4. array: 'Array',
  5. object: 'Object',
  6. function: 'Function',
  7. string: 'String',
  8. null: 'Null',
  9. undefined: 'Undefined',
  10. boolean: 'Boolean',
  11. number: 'Number'
  12. }
  13. let getType = (item) => {
  14. return _toString.call(item).slice(8, -1)
  15. }
  16. let isTypeOf = (item, type) => {
  17. return map[type] && map[type] === getType(item)
  18. }

深复制 深度优先遍历

  1. let DFSdeepClone = (obj, visitedArr = []) => {
  2. let _obj = {}
  3. if (isTypeOf(obj, 'array') || isTypeOf(obj, 'object')) {
  4. let index = visitedArr.indexOf(obj)
  5. _obj = isTypeOf(obj, 'array') ? [] : {}
  6. if (~index) { // 判断环状数据
  7. _obj = visitedArr[index]
  8. } else {
  9. visitedArr.push(obj)
  10. for (let item in obj) {
  11. _obj[item] = DFSdeepClone(obj[item], visitedArr)
  12. }
  13. }
  14. } else if (isTypeOf(obj, 'function')) {
  15. _obj = eval('(' + obj.toString() + ')');
  16. } else {
  17. _obj = obj
  18. }
  19. return _obj
  20. }

深复制 广度优先遍历

  1. let BFSdeepClone = (obj) => {
  2. let origin = [obj],
  3. copyObj = {},
  4. copy = [copyObj]
  5. // 去除环状数据
  6. let visitedQueue = [],
  7. visitedCopyQueue = []
  8. while (origin.length > 0) {
  9. let items = origin.shift(),
  10. _obj = copy.shift()
  11. visitedQueue.push(items)
  12. if (isTypeOf(items, 'object') || isTypeOf(items, 'array')) {
  13. for (let item in items) {
  14. let val = items[item]
  15. if (isTypeOf(val, 'object')) {
  16. let index = visitedQueue.indexOf(val)
  17. if (!~index) {
  18. _obj[item] = {}
  19. //下次while循环使用给空对象提供数据
  20. origin.push(val)
  21. // 推入引用对象
  22. copy.push(_obj[item])
  23. } else {
  24. _obj[item] = visitedCopyQueue[index]
  25. visitedQueue.push(_obj)
  26. }
  27. } else if (isTypeOf(val, 'array')) {
  28. // 数组类型在这里创建了一个空数组
  29. _obj[item] = []
  30. origin.push(val)
  31. copy.push(_obj[item])
  32. } else if (isTypeOf(val, 'function')) {
  33. _obj[item] = eval('(' + val.toString() + ')');
  34. } else {
  35. _obj[item] = val
  36. }
  37. }
  38. // 将已经处理过的对象数据推入数组 给环状数据使用
  39. visitedCopyQueue.push(_obj)
  40. } else if (isTypeOf(items, 'function')) {
  41. copyObj = eval('(' + items.toString() + ')');
  42. } else {
  43. copyObj = obj
  44. }
  45. }
  46. return copyObj
  47. }

测试

  1. /**测试数据 */
  2. // 输入 字符串String
  3. // 预期输出String
  4. let str = 'String'
  5. var strCopy = DFSdeepClone(str)
  6. var strCopy1 = BFSdeepClone(str)
  7. console.log(strCopy, strCopy1) // String String 测试通过
  8. // 输入 数字 -1980
  9. // 预期输出数字 -1980
  10. let num = -1980
  11. var numCopy = DFSdeepClone(num)
  12. var numCopy1 = BFSdeepClone(num)
  13. console.log(numCopy, numCopy1) // -1980 -1980 测试通过
  14. // 输入bool类型
  15. // 预期输出bool类型
  16. let bool = false
  17. var boolCopy = DFSdeepClone(bool)
  18. var boolCopy1 = BFSdeepClone(bool)
  19. console.log(boolCopy, boolCopy1) //false false 测试通过
  20. // 输入 null
  21. // 预期输出 null
  22. let nul = null
  23. var nulCopy = DFSdeepClone(nul)
  24. var nulCopy1 = BFSdeepClone(nul)
  25. console.log(nulCopy, nulCopy1) //null null 测试通过
  26. // 输入undefined
  27. // 预期输出undefined
  28. let und = undefined
  29. var undCopy = DFSdeepClone(und)
  30. var undCopy1 = BFSdeepClone(und)
  31. console.log(undCopy, undCopy1) //undefined undefined 测试通过
  32. //输入引用类型obj
  33. let obj = {
  34. a: 1,
  35. b: () => console.log(1),
  36. c: {
  37. d: 3,
  38. e: 4
  39. },
  40. f: [1, 2],
  41. und: undefined,
  42. nul: null
  43. }
  44. var objCopy = DFSdeepClone(obj)
  45. var objCopy1 = BFSdeepClone(obj)
  46. console.log(objCopy === objCopy1) // 对象类型判断 false 测试通过
  47. console.log(obj.c === objCopy.c) // 对象类型判断 false 测试通过
  48. console.log(obj.c === objCopy1.c) // 对象类型判断 false 测试通过
  49. console.log(obj.b === objCopy1.b) // 函数类型判断 false 测试通过
  50. console.log(obj.b === objCopy.b) // 函数类型判断 false 测试通过
  51. console.log(obj.f === objCopy.f) // 数组类型判断 false 测试通过
  52. console.log(obj.f === objCopy1.f) // 数组类型判断 false 测试通过
  53. console.log(obj.nul, obj.und) // 输出null,undefined 测试通过
  54. // 输入环状数据
  55. // 预期不爆栈且深度复制
  56. let circleObj = {
  57. foo: {
  58. name: function() {
  59. console.log(1)
  60. },
  61. bar: {
  62. name: 'bar',
  63. baz: {
  64. name: 'baz',
  65. aChild: null //待会让它指向obj.foo
  66. }
  67. }
  68. }
  69. }
  70. circleObj.foo.bar.baz.aChild = circleObj.foo
  71. var circleObjCopy = DFSdeepClone(circleObj)
  72. var circleObjCopy1 = BFSdeepClone(circleObj)
  73. console.log(circleObjCopy, circleObjCopy1) // 测试通过?

主要区别在于对于深层次以及环状数据,用深度优先遍历递归去做容易爆栈,广度优先遍历我对环状数据进行了处理,已经存在过的对象会存在数组中,下次直接赋值即可,无需继续遍历