• [Algorithm] Breadth First JavaScript Search Algorithm for Graphs


    Breadth first search is a graph search algorithm that starts at one node and visits neighboring nodes as widely as possible before going further down any other path. This algorithm requires the use of a queue to keep track of which nodes to visit, so it might be worth your time to brush up on that data structure before watching this lesson.

     
    const {createQueue} = require('./queue');
    
    function createNode(key) {
        let children = [];
        return {
            key,
            children,
            addChild(child) {
                children.push(child)
            }
        }
    }
    
    function createGraph(directed = false) {
        const nodes = [];
        const edges = [];
    
        return {
            nodes,
            edges,
            directed,
    
            addNode(key) {
                nodes.push(createNode(key))
            },
    
            getNode (key) {
                return nodes.find(n => n.key === key)
            },
    
            addEdge (node1Key, node2Key) {
                const node1 = this.getNode(node1Key);
                const node2 = this.getNode(node2Key);
    
                node1.addChild(node2);
    
                if (!directed) {
                    node2.addChild(node1);
                }
    
                edges.push(`${node1Key}${node2Key}`)
            },
    
            print() {
                return nodes.map(({children, key}) => {
                    let result = `${key}`;
    
                    if (children.length) {
                        result += ` => ${children.map(n => n.key).join(' ')}`
                    }
    
                    return result;
                }).join('
    ')
            },
            /**
             * Breadth First Search
             */
            bfs (startNodeKey = "", visitFn = () => {}) {
                /**
                 * Keytake away:
                 * 1. Using Queue to get next visit node
                 * 2. Enqueue the node's children for next run
                 * 3. Hashed visited map for keep tracking visited node
                 */
                const startNode =  this.getNode(startNodeKey);
               // create a hashed map to check whether one node has been visited
               const visited = this.nodes.reduce((acc, curr) => {
                   acc[curr.key] = false;
                   return acc;
               }, {});  
            
               // Create a queue to put all the nodes to be visited
               const queue = createQueue();
               queue.enqueue(startNode);
            
               // start process
               while (!queue.isEmpty()) {
                  const current = queue.dequeue();
            
                  // check wheather the node exists in hashed map
                  if (!visited[current.key]) {
                      visitFn(current);
                      visited[current.key] = true;
            
                      // process the node's children
                      current.children.map(n => {
                        if (!visited[n.key]) {
                            queue.enqueue(n);
                        }
                      });
                  }
               }
            } 
        }
    }
    
    const graph = createGraph(true)
    
    graph.addNode('Kyle')
    graph.addNode('Anna')
    graph.addNode('Krios')
    graph.addNode('Tali')
    
    graph.addEdge('Kyle', 'Anna')
    graph.addEdge('Anna', 'Kyle')
    graph.addEdge('Kyle', 'Krios')
    graph.addEdge('Kyle', 'Tali')
    graph.addEdge('Anna', 'Krios')
    graph.addEdge('Anna', 'Tali')
    graph.addEdge('Krios', 'Anna')
    graph.addEdge('Tali', 'Kyle')
    
    console.log(graph.print())
    
    
    
    const nodes = ['a', 'b', 'c', 'd', 'e', 'f']
    const edges = [
      ['a', 'b'],
      ['a', 'e'],
      ['a', 'f'],
      ['b', 'd'],
      ['b', 'e'],
      ['c', 'b'],
      ['d', 'c'],
      ['d', 'e']
    ]
    
    const graph2 = createGraph(true)
    nodes.forEach(node => {
        graph2.addNode(node)
      })
      
      edges.forEach(nodes => {
        graph2.addEdge(...nodes)
      })
    
      graph2.bfs('a', node => {
        console.log(node.key)  //a,b,e,f,d,c
      })

    A more general function:

            bfs (startNodeKey, predFn = () => {}, cb = () => {}) {
                const startNode = this.getNode(startNodeKey);
                const visited = createVistedMap(this.nodes);
                const queue = createQueue();
                startNode.children.forEach((n) => {
                    queue.enqueue(n);
                });
                while (!queue.isEmpty()) {
                    const current = queue.dequeue();
                    if (!visited[current.key]) {
                        if (predFn(current)) return cb(current);
                        else {
                            visited[current.key] = true;
                        }
                    }
                }
                cb(null)
            },
    let graph3 = createGraph(true)
    const tyler = {key: 'tyler', dog: false};
    const henry = {key: 'henry', dog: false};
    const john = {key: 'john', dog: false};
    const aimee = {key: 'aimee', dog: true};
    const peggy = {key: 'peggy', dog: false};
    const keli = {key: 'keli', dog: false};
    const claire = {key: 'claire', dog: false};
    
    graph3.addNode('tyler', tyler);
    graph3.addNode('henry', henry);
    graph3.addNode('john', john);
    graph3.addNode('claire', claire);
    graph3.addNode('aimee', aimee);
    graph3.addNode('peggy', peggy)
    graph3.addNode('keli', keli);
    
    graph3.addEdge('tyler', 'henry')
    graph3.addEdge('tyler', 'john')
    graph3.addEdge('tyler', 'aimee')
    graph3.addEdge('henry', 'keli')
    graph3.addEdge('henry', 'peggy')
    graph3.addEdge('john', 'john')
    graph3.addEdge('keli', 'claire')
    
    
    graph3.bfs2('tyler', (node) => {
        return node.dog;
    }, (node) => {
        if (node) console.log(`${node.key} has a dog`)
        else console.log('Tyler friends has no dog')
    })

    Time Complexity: O(V+E) where V is number of vertices in the graph and E is number of edges in the graph.

  • 相关阅读:
    ossec配置使用腾讯企业邮箱告警
    网络排除工具之tcping
    pyenv 安装
    CVE-2020-1472 漏洞检测
    容器技术的核心
    简述 进程、线程、协程的区别 以及应用场景--记录
    php函数使用
    php使用表单post方法进行页面
    CURL方式使用代理访问网站
    nginx下隐藏admin和当前域名下得index.php
  • 原文地址:https://www.cnblogs.com/Answer1215/p/10124035.html
Copyright © 2020-2023  润新知