图结构

图结构

概念

在这里插入图片描述

图结构中,一个结点可以链接到任意结点,所有结点链接而成的结构,即为图结构

图结构中的链接可以是有向的,也可以是无向的(双向链接),本文仅讨论双向链接

树结构是一种特殊的图结构

图结构没有根,可以有环,但是在一个图结构中,不能存在两个或以上的孤立结点

可以使用图中任何一个结点表示整个图结构

图结构是一种常见的数据结构,例如网络爬虫抓取的网页就是一种典型的图结构

图结构的代码可表示为:

function Node(value){
    this.value = value;
    this.neighbors = [];
}

相关算法

  1. 查询算法

和树结构一样,图结构的查询也可以分为深度优先(Depth First Search)和广度优先(Breadth First Search)查询

function Node(value) {
    this.value = value;
    this.neighbors = [];
}
A = new Node('A');
B = new Node('B');
C = new Node('C');
D = new Node('D');
E = new Node('E');
A.neighbors.push(B, C, D);
B.neighbors.push(A, D, E);
C.neighbors.push(A, E);
D.neighbors.push(A, B);
E.neighbors.push(B, C);


/**
 * @description: 深度优先查询图结构
 * @param {type} node
 * @param {type} target
 * @return: true/false
 */
function searchDeep(node, target) {
    //先查自己,再查自己的邻居
    if (!node) {
        return false;
    }
    var finded = [];//保存已经找过的节点
    function _searchDeep(node) {
        //看之前有没有找过
        if (finded.includes(node)) {
            return false;
        }
        //该节点没有验证过
        if (node.value === target) {
            return true;
        }
        //加入到已查找的节点中
        finded.push(node);
        //继续找自己的邻居,一个一个查
        for (var i = 0; i < node.neighbors.length; i++) {
            if (_searchDeep(node.neighbors[i])) {
                return true;
            }
        }
        return false;
    }
    return _searchDeep(node);

}

/**
 * @description: 广度优先查询图结构
 * @param {type} 
 * @return: 
 */
function searchBreadth(node, target) {
    //先看自己,再看自己的邻居,再看邻居的邻居……
    if (!node) {
        return false;
    }
    var finded = [];
    function _searchBreadth(node) {
        if (node.length === 0) {
            return false;
        }
        var next = [];
        //用于存放自己的邻居
        for (var i = 0; i < node.length; i++) {
            var n = node[i];
            if (n.value === target) {
                return true;
            }
            finded.push(n);//把已找过的节点放进去
            //这个节点不是,把它的邻居放入next数组中
            n.neighbors.forEach(item=>{
                if(!next.includes(item)){
                    next.push(item);
                    //避免重复
                }
            })

        }
        //要把已经找过的从下一层中删除
        for(var j = 0;j < next.length;j ++){
            if(finded.includes(next[j])){
                next.splice(j,1);
                j --;
            }
        }
        console.log(next);
    
        //开始找自己的邻居
        return _searchBreadth(next);

    }
    return _searchBreadth([node]);

}

  1. 最小生成树算法

如果一个图中结点连接而成的边具备某种数值,需要将这些边进行精简,生成一个连接全节点同时总边长最小的树结构,该树称之为最小生成树

实现最小生成树可以使用Prim算法,从任意一个点出发,连接到该点最短的点,组成一个部落,然后继续连接到该部落最短的点,直到把所有点连接完成

/**
 * 连接nodes数组中所有的点,得到一个最小生成树
 * @param {*} nodes 点的集合
 * @param sides 边的集合 二维数组
 */
function Prim(nodes, sides) {
  if (nodes.length <= 1 || nodes.length !== sides.length) {
    return; //抱歉,臣妾做不到
  }
  var hords = [nodes[0]]; //把第一个点组成一个部落
  while (hords.length < nodes.length) {
    // 向部落中增加一个点
    _addNodeToHords();
    console.log(hords.map(it => it.value));
  }

  /**
   * 添加一个点到部落
   */
  function _addNodeToHords() {
    //1. 从nodes中选出一个到部落最近的点
    var result = _chooseNearNodeToHord();
    //2. 将该点和部落中的某个点连接起来
    result.node.neighbors.push(result.target);
    result.target.neighbors.push(result.node);
    //3. 加入到部落中
    hords.push(result.node);
  }

  /**
   * 选一个距离当前部落最近的点
   * 返回:
   * {
   *    node: 到部落最近的点,
   *    target: 连接到部落的哪个点
   * }
   */
  function _chooseNearNodeToHord() {
    var result = {
      node: null,
      target: null,
      dis: Infinity
    };
    for (var i = 0; i < nodes.length; i++) {
      //一个点一个点拿出来,看哪个点离部落最近
      var n = nodes[i];
      if (hords.includes(n)) {
        //部落里面已经有这个点了
        continue; //这个点不用看了,下一个点
      }
      // 得到 n 到 部落最近的那个点的距离
      // { dis: 到部落的最短距离,  target: 到部落的点}
      var temp = _getMinDisToHord(n);
      if (temp.dis < result.dis) {
        result.node = n;
        result.target = temp.target;
        result.dis = temp.dis;
      }
    }
    return result;
  }

  /**
   * 得到指定的点到部落的最短距离,以及目标点
   * { dis: 到部落的最短距离,  target: 到部落的点}
   * @param {*} node
   */
  function _getMinDisToHord(node) {
    var result = {
      target: hords[0],
      dis: Infinity
    };
    //循环部落
    for (var i = 0; i < hords.length; i++) {
      var target = hords[i]; //拿到部落的当前点
      //计算 node 到 target 的距离
      var row = nodes.indexOf(node);
      var col = nodes.indexOf(target);
      var dis = sides[row][col];
      if (dis < result.dis) {
        result.target = target;
        result.dis = dis;
      }
    }
    return result;
  }
}

//一群孤立的节点
var a = new Node("a");
var b = new Node("b");
var c = new Node("c");
var d = new Node("d");
var e = new Node("e");

var nodes = [a, b, c, d, e];
var sides = [
  [0, 7, 9, 6, Infinity], // a到其他点的距离
  [7, 0, Infinity, 8, 4], // b到其他点的距离
  [9, Infinity, 0, Infinity, Infinity], // c到其他点的距离
  [6, 8, Infinity, 0, Infinity], // d到其他点的距离
  [Infinity, 4, 5, Infinity, 0] // e到其他点的距离
];

Prim(nodes, sides);
console.log(a);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值