数据结构

138 阅读3分钟

哈希表

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
    1. 数组进行插入删除操作,效率低
    2. 查找 
        2.1 如果基于索引进行查找效率高
        2.2 如何基于内容去查找效率低
    3. 修改
        3.1 基于索引 修改效率高
        3.2 基于内容 效率低

  哈希表基于数组实现
   插入 删除 查找 操作非常高
   哈希表的速度比树还要快

   缺点: 数据是没有顺序的,key值不能重复

   哈希表在哈希化的时候,产生的下标值发生冲突,解决办法有
   两种: 1、链地址法(拉链法)  2.  开放地址法
</body>
<script>
  // 哈希函数
  //  将字符串转为较大的数字
  //  将大的数字hashcode 压缩到数组范围(大小)之内

  function hashFunc(str, size) {
    // 定义hashcode变量
    let hashCode = 0;
    // 霍纳算法,来计算hashcode的值
    for (let i = 0; i < str.length; i ++ ) {
      // 利用 37 这个质数
      hashCode = 37 * hashCode + str.charCodeAt(i)

    }
    //  取余
    let index = hashCode % size

    return index
  }


  // 哈希表  (采用链地址法来实现哈希表)
  function HashTable () {

    this.storage = []
    // 记录当前元素
    this.count = 0
    // 记录数组的长度
    this.limit = 7

    // 哈希函数
    HashTable.prototype.hashFunc =function (str, size) {
      // 定义hashcode变量
      let hashCode = 0;
      // 霍纳算法,来计算hashcode的值
      for (let i = 0; i < str.length; i ++ ) {
        // 利用 37 这个质数
        hashCode = 37 * hashCode + str.charCodeAt(i)

      }
      //  取余
      let index = hashCode % size

      return index
    }

    // 操作
    // 插入和修改操作是同一个方法

   /* 
     1. 根据key获取索引值(目的: 将数据插入到对应的位置)
     2. 根据索引值取出bucket
        2.1 如果桶不存在,创建桶,并将数据放置在该索引的位置
     3. 判断是新增 还是修改原来的值
        3.1 如果有值,则修改值
        3.2 如果没有值,执行添加操作
 */
    HashTable.prototype.put = function (key, value) {
      // 根据key获取index
      let index = this.hashFunc(key, this.limit)
      //  根据index 取出对应的bucket
      let bucket = this.storage[index]
      // 判断bucket是否为null
      if (bucket === null) {
        bucket = []
        this.storage[index] = bucket
      }
      // 判断是否是修改数据
      for (let i = 0; i < bucket.length; i++) {
        let tuple = bucket[i]
        if (tuple[0] == key ) {
          tuple[1] = value
          return
        }
      }
      //  进行添加操作
      bucket.push([key, value])
      this.count += 1

      //  判断是否需要扩容
      if (this.count > this.limit * 0.75) {
        let sizeNum = this.limit * 2
         // 用来保证容量一定是一个质数
        let newSize = this.getpreim(sizeNum)
        this.resize(newSize)
      }
    }

    HashTable.prototype.get = function (key) {
       // 根据key获取index
       let index = this.hashFunc(key, this.limit)
      //  根据index 取出对应的bucket
      let bucket = this.storage[index]
      if (bucket === null) {
        return null
      }
      for (let i = 0; i < bucket.length; i++) {
        let tuple = bucket[i]
        if (tuple[0] === key) {
          return tuple[1]
        } 
      }

      return null

    }


    HashTable.prototype.remove = function (key) {
         // 根据key获取index
       let index = this.hashFunc(key, this.limit)
        //  根据index 取出对应的bucket
        let bucket = this.storage[index]
        if (bucket === null) {
          return null
        }
        for (let i = 0; i < bucket.length; i++) {
          let tuple = bucket[i]
          if (tuple[0] === key) {
            // 删除
            bucket.splice(i,1)
            this.count -= 1
            return tuple[1]

            // 减小容量
            if (this.limit > 7 && this.count < this.limit * 0.25){
              let sizeNum = Math.floor(this.limit / 2)
              // 用来保证容量一定是一个质数
              let newSize = this.getpreim(sizeNum)
              this.resize(newSize)
            }

          } 
        }

        return null
      }

    
      HashTable.prototype.isEmpty = function () {
        return this.count == 0
      }

      HashTable.prototype.size = function () {
        return this.count 
      }

      // 哈希表扩容
      HashTable.prototype.resize = function (newLimit) {
        
        // 保存旧的数组内容
        let oldSrorage = this.storage

        // 重置所有的属性
        this.storage = []
        this.count = 0
        this.limit = newLimit

        // 遍历oldStorage中所有的bucket
        for (let i = 0; i< oldSrorage.length; i++) {
          let bucket = oldSrorage[i]

          if (bucket == null) {
            continue
          }

          for (let j = 0; j < bucket.length; j++) {
            let tuple = bucket[j]
            this.put(tuple[0], tuple[1])
          }

        }

      }

    // 判断是不是质数
    HashTable.prototype.isPre = function (num) {
         // 获取num平方
        let temp = parseInt(Math.sqrt(num))

        // 循环判断
        for (let i = 2; i <= temp; i++) {
          if (num % i == 0) {
              return false
            }
        }
        return true
    }

    // 获取质数方法 
    HashTable.prototype.getpreim = function (num) {
        while(!this.isPre(num)) {
          num ++
        }
        return num
    }


  }

  // 质数  只能被1和自己整除,不能被2到num-1的数整除
  function isPrime(num) {
      for( let i = 2; i< num; i++) {
        if (num % i == 0) {
          return false
        }
      }
      return true
  }


  function isPre(num) {
    // 获取num平方
    let temp = parseInt(Math.sqrt(num))

    // 循环判断
    for (let i = 2; i <= temp; i++) {
      if (num % i == 0) {
          return false
        }
    }
    return true
  }

alert(hashFunc('abc', 7))
</script>
</html>

集合

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  
</body>
<script>
  // 封装集合类
  function Set() {
    // 属性
    this.items = {}

    // 方法
    Set.prototype.has = function (value) {
          return this.items.hasOwnProperty(value)
        }
    Set.prototype.add = function (value) {
      if (this.has(value)) {
        return false
      }

      this.items[value] = value
      return true
    }

    Set.prototype.remove = function (value) {
      // 集合中是否包含元素
      if (!this.has(value)) {
        return false
      }

      delete this.items[value]
      return true

    }

    Set.prototype.clear = function () {
      this.items = {}
    }

    Set.prototype.size = function (value) {
      return Object.keys(this.items).length
    }

    Set.prototype.values = function (value) {
      return Object.keys(this.items)
    }

    // 集合的操作 
    // 并集
    Set.prototype.union = function (otherSet) {
      // otherSet 其他的集合
      let unionSet = new Set()
      let values = this.values()
      for (let i = 0; i < values.length; i++) {
        unionSet.add(values[i])
      }

      values = otherSet.values()
      for (let i = 0; i< values.length; i++) {
        unionSet.add(values[i])
      }

      return unionSet

    }


    Set.prototype.intersection = function (otherSet) {
      // otherSet 其他的集合
      let intersectionSet = new Set()

      let values = this.values()
      for (let i = 0; i< values.length; i ++) {
        let item = values[i]
        if (otherSet.has(item)) {
          intersectionSet.add(item)
        }
      }
      return intersectionSet
    }

    Set.prototype.difference = function (otherSet) {
      // otherSet 其他的集合
      let differenceSet = new Set()

      let values = this.values()
      for (let i = 0; i < values.length; i++) {
        let item = values[i]
        if (!otherSet.has(item)) {
          differenceSet.add(item)
        }
      }
      return differenceSet

    }

    // 子集
    Set.prototype.subSet = function (otherSet) {
      let values = this.values()

      for (let i = 0; i< values.length; i ++) {
        let item = values[i]
        if (!otherSet.has(item)) {
          return false
        }
      }

      return true
      

    }

  }
</script>
</html>

链表

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  
</body>
<script>

  function LinkedList() {

    function Node (data) {
      this.data = data;
      this.next = null;
    }

    this.head = null;
    this.length = 0;

    // 追加方法
    LinkedList.prototype.append = function (data) {
      
      let newNode = new Node(data)


      // 判断是否添加的是第一个节点


      if (this.length === 0) { // 第一个节点
        // let newNode = new Node(data)
        this.head = newNode
      } else {
        // let newNode = new Node(data)

        // 找到最后一个节点 
        let current = this.head
        while (current.next) {
          current = current.next
        }
        // 最后节点的next指定新的节点
        current.next = newNode
      }
      
      this.length += 1;
    }

    // toString 方法
    LinkedList.prototype.toString = function() {
      let current = this.head
      let listString = ""

      while (current) {
        listString += current.data + ""
        current = current.next
      }

      return listString

    }

    // 
    LinkedList.prototype.insert = function (position, data) {
      // 对position 进行越界判断
      if (position < 0 || position > this.length) return false;
      //  根据data创建node
      let newNode = new Node(data)

      // 3. 判断插入的位置是否是第一个
      if (position === 0) {
        newNode.next = this.head
        this.head = newNode
      } else {
        let index = 0
        let current = this.head // 当前
        let previous = null   // 前一个
        while (index ++ < position) {
          previous = current
          current = current.head
        }
        newNode.next = current
        previous.next = newNode
      }

      this.length += 1

      return true

    }

    // get方法
    LinkedList.prototype.get = function (position) {
      if (position < 0 || position >= this.length) return false;

      let current = this.head
      let index = 0
      while (index++ < position) {
        current = current.next
      }
      return current.data

    }


    // indexOf
    LinkedList.prototype.indexOf = function (data) {
      let current = this.head
      let index = 0
      while (current) {

        if (current.data === data) {
          return index
        }

        current = current.next
        index += 1

      }

      return -1

    }
    
    // update
    LinkedList.prototype.update = function (position, newDate) {
      // 越界判断
      if (position < 0 || position >= this.length) return false;
      
      // 2.查找正确的节点
      let current = this.head
      let index = 0
      while (index++ < position) {
        current = current.next
      }

      //  将postionwenode的data 改为newDAta
      current.data = newDate
      return true

    }


    // removeAt(position)
    LinkedList.prototype.removeAt = function (position) {
      if (postion < 0 || position >= this.length) return null;
      let current = this.head

      if (position === 0) {
        this.head = this.head.next
      } else {
        let index = 0;
        let previous = null
        while (index ++ < position) {
          previous = current
          current = current.next
        }
        previous.next = current.next
      }

      this.length -= 1

      return current.data

    }


  // remove
  LinkedList.prototype.remove = function (data) {

    // 1. 获取data在列表中的位置
    let postion = this.indexOf(data)

    //  根据位置删除节点
    return this.removeAt(position)

  }



  }

  let list = new LinkedList()

  list.append('abc')
  list.append('ccc')
  console.log(list, '---list')
  alert(list)

</script>
</html>

双向链表

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  
</body>
<script>

  function DoubleLinkedList() {

    function Node(data) {
      this.data = data
      this.prev = null
      this.next = null
    }

    this.head = null;
    this.tail = null
    this.length = 0

    // append方法

DoubleLinkedList.prototype.append = function (data) {
  let newNode = new Node(data)

  if (this.length === 0) {
    this.head = newNode
    this.tail = newNode
  } else {
    newNode.prev = this.tail
    this.tail.next =  newNode
    this.tail = newNode
  }
  this.length += 1

}

DoubleLinkedList.prototype.toString = function ()  {
  return this.backwardString()
}

DoubleLinkedList.prototype.forwardString = function ()  {
    let current = this.tail;
    let resultString = ""
    while (current) {
      resultString += current.data + " "
      current = current.prev
    }
    return resultString
}

DoubleLinkedList.prototype.backwardString = function ()  {
    let current = this.head
    let resultString = ""
    while (current) {
      resultString += current.data + ""
      current = current.next
    }
    return resultString
}

DoubleLinkedList.prototype.insert = function (position, data) {
  if (position < 0 || position > this.length) return false;
  let newNode = new Node(data)
  // 判断原来的列表是否为 空
  if (this.length === 0) {
    this.head = newNode
    this.tail = newNode
  } else {
    // 给最前面添加
    if (position === 0) {
      // 原来的第一个节点。prev = newNode
      // newNode.next = 原来的第一个节点
      this.head.prev = newNode
      newNode.next = this.head
      this.head = newNode
    } else if (position === this.length) { // 给最后面添加
      newNode.prev = this.tail
      this.tail.next = newNode
      this.tail = newNode
    } else {  // 插入到中间
      let current = this.head
      let index = 0
      while (index++ < position) {
        current = current.next
      }

      newNode.next = current
      newNode.prev = current.prev
      current.prev.next = newNode
      current.prev = newNode

    }

  }
  this.length += 1
}


// get(position)
DoubleLinkedList.prototype.get = function (position) {
  if (position < 0 || position >= this.length) return null;
  let index = 0;
  let current = this.head
  while (index++ < position) {
    current = current.next
  }
  return current.data

  // 优化 TODO
  // this.length /2 > position 从前往后遍历
  // this.length / 2 < position 从后往前

}

DoubleLinkedList.prototype.indexOf = function (data) {
  let index = 0;
  let current = this.head
  while (current) {
    if (current.data === data) {
      return index
    }
    current = current.next
    index += 1
  }
  return -1
} 


DoubleLinkedList.prototype.update = function (position, newdata) {
  if (position < 0 || position >= this.length) return false;
  let index = 0;
  let current = this.head
  while (index++ < position) {
    current = current.next
  }

  current.data = newdata

  return true
} 


DoubleLinkedList.prototype.removeAt = function (position) {
  if (position < 0 || position >= this.length) return null;

 
  let current = this.head

  if (this.length === 1) {
    this.tail = null
    this.head = null
  } else {
    // 判断是否删除的是第一个节点
    if (position == 0) {
      this.head.next.prev = null;
      this.head = this.head.next
    } else if (position === this.length -1) {
      current = this.tail
      this.tail.prev.next = null
      this.tail = this.tail.prev
    } else {
        let index = 0
         
          while (index++ < position) {
            current = current.next
          }

          current.prev.next = current.next
          current.next.prev = current.prev

    }
  }

  this.length -=  1
  return current.data
} 


DoubleLinkedList.prototype.remove = function (data) { 
  let index = this.indexOf(data)
  return this.removeAt(index)
}


}


  let DoubleList = new DoubleLinkedList()

</script>
</html>

队列(优先级队列)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
</body>
<script>

  function PriorityQueue() {

    function QueElement(element, priority) {
      this.element = element
      this.priority = priority
    }

    this.items = []

    PriorityQueue.prototype.enqueue = function (element, priority) {

      let queElement = new QueElement(element, priority)

      if (this.items.lenght === 0) {
        this.items.push(queElement)
      } else {
        let added = false
        for (let i = 0; i< this.items.length; i ++) {
          if (queElement.priority < this.items[i].priority) {
            this.items.splice(i, 0, queElement)
            added = true
            break
          }
        }

        if (!added) {
          this.items.push(queElement)
        }
      }
    }

  }

  let pq = new PriorityQueue()



  function Stack() {

    this.items = [];

    Stack.prototype.push = function(ele) {
      this.items.push(ele)
    }

    Stack.prototype.pop = function() {
      this.items.pop()
    }

    Stack.prototype.peek = function() {
      return this.items[items.lenght - 1]
    }

    Stack.prototype.isEmpty = function () {
      return this.items.lenght === 0
    }

    Stack.prototype.size = function () {
      return this.items.lenght
    }

    Stack.prototype.toString = function () {
      // return this.items.join(' ')
      return this.items.toString()
    }

  }

  let stack = new Stack()



</script>
</html>

 // 二叉搜索树
  class Tree {
    constructor () {
     
      this.root = null;
      this.Node = function Node (key) {
          this.key = key
          this.left = null
          this.right = null
        };
    }
 
  //  Node(key) {
  //   this.key = key
  //   this.left = null
  //   this.right = null
  //  }

    // 插入的递归函数
    insertNode (node, newNode) {
      if (newNode.key < node.key) { // 向左查找
        if (node.left === null) {
          node.left = newNode
        } else {
          this.insertNode(node.left, newNode)
        }
      } else { // 向右查找
        if (node.right === null) {
          node.right = newNode
        } else {
          this.insertNode(node.right, newNode)
        }
      }
    }


    // 插入的函数
    insert (key) {
      // 创建节点
      let newNode = new this.Node(key)

      // 判断根节点有没有值
      if (this.root === null) {
        this.root = newNode
      } else {
        this.insertNode(this.root, newNode)
      }

    }


    // 先序遍历

    preOrderTreeNode (node, handler) {

      if (node !== null) {

        //  处理进过的节点
        handler(node.key)

        // 处理进过节点的左子节点
        this.preOrderTreeNode(node.left, handler)

        // 处理进过的右子节点
        this.preOrderTreeNode(node.right, handler)
      }

    }

    // 对外暴露的方法
    preOrderTree (handler) {
      this.preOrderTreeNode(this.root, handler)
    }


    // 中序遍历
    midOrderTree(handler) {
      this.midOrderTreeNode(this.root, handler)
    }

    midOrderTreeNode(node, handler) {
      if (node !== null) {
        // 处理左子树中的节点
          this.midOrderTreeNode(node.left, handler)
        //  处理节点
          handler(node.key)
        // 处理右子树中的节点
        this.midOrderTreeNode(node.right, handler)
      }
    }


    // 后续遍历

    min() {
      let node = this.root;
      let key = null;
      while(node !== null) {
        key = node.key
        node = node.left

      }

      return key
    }

    max () {
      let node = this.root
      let key = null
      while(node !== null) {
        key = node.key
        node = node.right
      }

      return key
      
    }


    // 搜索特定的值 key
    search (key) {

      let node = this.root  

      while (node !== null) {
          if (node.key > key) {
            node = node.left
          } else if (key > node.key) {
            node = node.right
          } else if (key === node.key){
            return true
          }
      }

      return false

    }



    //  删除 

    //  先查找要删除的节点

    //  1. 删除叶子节点 判断 叶子节点 left right 为空, 删除这个节点也要判断他是父节点的左节点还是右节点
//  2. 要删除的节点只有一个左节点或者右节点,直接将父节点的左节点或者右节点指向要删除的左节点或者右节点

// 3. 删除有两个子节点的节点


    remove (key) {

      // 寻找要删除的节点,
      //  定义变量,保存一些信息
      let current = this.root
      let parent = null
      let isLeftChild = true

      // 开始寻找节点
      while (current.key !== key) {
        parent = current
        if (key < parent.key) {
          isLeftChild = true
          current = current.left
        } else {
          isLeftChild = false
          current = current.right
        }

        // 没有找到key
        if (current === null) return false
      }


      // 找到了 current.key == key
      
      //  1. 
        if (current.right === null && current.left === null) {
          if (current === this.root) {
            this.root = null
          }  else if (isLeftChild) {
            parent.left = null
          } else {
            parent.right = null
          }
          
        }

      // 2.

       else if (current.right === null && isLeftChild) {
        //  若只有一个根节点,根节点只有一个左节点或者右节点
         if (current === this.root) {
           this.root = current.left
         } else {
           parent.left = current.left
         }
           
        }
       else if (current.right === null && !isLeftChild) {
          if (current === this.root) {
            this.root = current.left
          } else {
            parent.right = current.left
          }
           
        }
       else if (current.left === null && isLeftChild) {
        if (current === this.root) {
            this.root = current.right
          } else {
           parent.left = current.right
          }
        }
       else if (current.left === null && !isLeftChild) {
        if (current === this.root) {
            this.root = current.right
          } else {
           parent.right = current.right
          }
        }


      // 3.
      // 左子树最大值(前驱) 右子树的最小值(后继)

      // 比current 小一点点 叫前驱,大一点点叫后继
        
      else {
        //  获取后继节点
        let successor = this.getend(current)
        // 判断是否是跟节点
        if (current === this.root) {
          this.root = successor
        } else if (isLeftChild) {
          parent.left = successor
        } else {
          parent.right = successor
        }
        // 将后继节点的左子树 = current.left
        successor.left = current.left
      }

    }


    // 找后继的方法
    getend(delNode) {

      // 保存找到的后继节点
      let successor = delNode
      let current = delNode.right
      let successorParent = delNode
      while (current !== null) {
        successorParent = successor
        successor = current
        current = current.left
      }

      //  判断寻找到的后继节点是否直接就是delNode的right节点

      if ( successor !== delNode.right) {
        successorParent.left = successor.right
        successor.right = delNode.right
      }

      return successor

    }



  }

  let bst = new Tree()

  bst.insert(11)
  bst.insert(7)
  bst.insert(15)
  bst.insert(5)
  bst.insert(3)
  bst.insert(9)
  bst.insert(8)
  bst.insert(10)
  bst.insert(13)
  bst.insert(12)
  bst.insert(14)
  bst.insert(20)
  bst.insert(18)
  bst.insert(25)

  console.log(bst)

  let resultString = ""
  bst.preOrderTree((key) => {
   resultString += key + " "
  })

  console.log(resultString.split(" "), '先序遍历')

  let resultString1 = ""
  bst.midOrderTree((key) => {
   resultString1 += key + " "
  })
  console.log(resultString1.split(" "), '中序遍历')
  console.log(bst.min(), bst.max())

字典

function Dictionay() {
    
    this.items = {}

    Dictionay.prototype.set = function (key, value) {
      this.items[key] = value
    }

    Dictionay.prototype.has = function (key) {
      return this.items.hasOwnProperty(key)
    }

    Dictionay.prototype.remove = function (key) {
      if (!this.has(key)) return false
      delete this.items[key]
      return true
    }

    Dictionay.prototype.get = function (key) {
      return this.has(key) ? this.items[key] : undefined
    }

    Dictionay.prototype.keys = function () {
      return Object.keys(this.items)
    }

    Dictionay.prototype.size = function () {
      return this.keys().length
    }

    Dictionay.prototype.clear = function () {
      this.items = {}
    }

  }

  function Graph() {
    // 属性: 顶点(数组) 边(字典)
    this.vertexes = []
    this.edges = new Dictionay()

    // 添加顶点的方法
    Graph.prototype.addVertex = function (v) {
      this.vertexes.push(v)
      this.edges.set(v, [])
    }

    // 添加边的方法
    Graph.prototype.addEdges = function (v1, v2) {
      // 无向图
      this.edges.get(v1).push(v2)
      this.edges.get(v2).push(v1)
    }

    Graph.prototype.toString = function () {

      let resultString = ""

      // 遍历所有的顶点以及顶点对应的边

      for (let i = 0; i< this.vertexes.length; i ++) {
        resultString += this.vertexes[i] + '--->'
        let vEdges = this.edges.get(this.vertexes[i])
        for (let j = 0; j< vEdges.length; j ++) {
          resultString += vEdges[j] + ' '
        }
        resultString += '\n'

      }
      return resultString

    }

    // BFS 广度优先搜索
    // DFS 深度优先搜索
    // 两种遍历算法都需要指定第一个被访问的顶点

    // 初始化状态颜色
    Graph.prototype.initColor = function () {
      let colors  = []
      for (let i = 0; i< this.vertexes.length; i++) {
        colors[this.vertexes[i]] = 'white'
      }
      return colors
    }

    // 广度优先搜索 基于队列
    Graph.prototype.bfs = function (initV, handler) {
      // 初始化颜色
      let colors = this.initColor()
      // 创建队列
      let queue = new Queue()
      //  将顶点加入到队列
      queue.enqueue(initV)
      // 循环从队列中取出元素
      while (!queue.isEmpty()) {
        // 从队列中取出一个顶点
        let v = queue.dequeue()

        // 获取和顶点相连的另外顶点
        let vList = this.edges.get(v)

        // 将v的颜色设置为灰色
        colors[v] = 'gray'

        // 遍历所有的顶点,加入到队列中
        for (let i = 0; i< vList.length; i ++) {
          let e = vList[i]
          if (colors[e] === 'white') {
            colors[e] = 'gray'
            queue.enqueue(e)
          }
        }

        // v已经被探测, 访问顶点
        handler(v)

        // 将顶点设置为黑色
        colors[v] = 'black'

      }



    }

    // 深度优先搜索
    Graph.prototype.dfs = function (initV, handler) {
      // 初始化颜色
      let colors  =this.initColor()

      // 从某个顶点开始依次递归 
      this.dfsVisit(initV, colors, handler)
    }

    Graph.prototype.dfsVisit  =function (v, colors, handler) {
      // 将颜色设置为灰色
      colors[v] = 'gray'

      // 处理v顶点
      handler(v)

      // 访问v相连的顶点
      let vList = this.edges.get(v)
      for (let i = 0; i< vList.length; i++) {
        let e = vList[i]
        if (colors[e] === 'white') {
          this.dfsVisit(e, colors, handler)
        }
      }

      // 将v设置成黑色
      colors[v] = 'black'
    }

  }