带你读《图解算法小抄》七、图(1)

简介: 带你读《图解算法小抄》七、图(1)

七、

访问 www.coding-time.cn 阅读原文动画效果,体验更佳。

 

在计算机科学中, 图(graph) 是一种抽象数据类型, 旨在实现数学中的无向图和有向图概念,特别是图论领域。

 

一个图数据结构是一个(由有限个或者可变数量的)顶点/节点/点和边构成的有限集。

 

如果顶点对之间是无序的,称为无序图,否则称为有序图;

如果顶点对之间的边是没有方向的,称为无向图,否则称为有向图;

如果顶点对之间的边是有权重的,该图可称为加权图。

 image.png

Graph

1. 完整代码

Graph

export default class Graph {
  /**
   * @param {boolean} isDirected
   */
  // 构造函数,参数表示图是否是有向的
  constructor(isDirected = false) {
    this.vertices = {}; // 顶点集
    this.edges = {}; // 边集
    this.isDirected = isDirected; // 是否为有向图
  }
  /**
   * @param {GraphVertex} newVertex
   * @returns {Graph}
   */
  // 添加顶点
  addVertex(newVertex) {
    this.vertices[newVertex.getKey()] = newVertex;
    return this;
  }
  /**
   * @param {string} vertexKey
   * @returns GraphVertex
   */
  // 根据键值获取顶点
  getVertexByKey(vertexKey) {
    return this.vertices[vertexKey];
  }
  /**
   * @param {GraphVertex} vertex
   * @returns {GraphVertex[]}
   */
  // 获取顶点的邻居
  getNeighbors(vertex) {
    return vertex.getNeighbors();
  }
  /**
   * @return {GraphVertex[]}
   */
  // 获取所有的顶点
  getAllVertices() {
    return Object.values(this.vertices);
  }
  /**
   * @return {GraphEdge[]}
   */
  // 获取所有的边
  getAllEdges() {
    return Object.values(this.edges);
  }
  /**
   * @param {GraphEdge} edge
   * @returns {Graph}
   */
  // 添加边
  addEdge(edge) {
    // 尝试查找起始和结束顶点
    let startVertex = this.getVertexByKey(edge.startVertex.getKey());
    let endVertex = this.getVertexByKey(edge.endVertex.getKey());
    // 如果起始顶点未插入,插入起始顶点
    if (!startVertex) {
      this.addVertex(edge.startVertex);
      startVertex = this.getVertexByKey(edge.startVertex.getKey());
    }
    // 如果结束顶点未插入,插入结束顶点
    if (!endVertex) {
      this.addVertex(edge.endVertex);
      endVertex = this.getVertexByKey(edge.endVertex.getKey());
    }
    // 检查边是否已经添加过
    if (this.edges[edge.getKey()]) {
      throw new Error('边已经被添加过了');
    } else {
      this.edges[edge.getKey()] = edge;
    }
    // 将边添加到顶点中
    if (this.isDirected) {
      // 如果图是有向的,那么只添加到起始顶点
      startVertex.addEdge(edge);
    } else {
      // 如果图是无向的,那么添加到两个顶点
      startVertex.addEdge(edge);
      endVertex.addEdge(edge);
    }
    return this;
  }
  /**
   * @param {GraphEdge} edge
   */
  // 删除边
  deleteEdge(edge) {
    // 从边集中删除边
    if (this.edges[edge.getKey()]) {
      delete this.edges[edge.getKey()];
    } else {
      throw new Error('在图中找不到边');
    }
    // 尝试查找起始和结束顶点,并从这些顶点中删除边
    const startVertex = this.getVertexByKey(edge.startVertex.getKey());
    const endVertex = this.getVertexByKey(edge.endVertex.getKey());
    startVertex.deleteEdge(edge);
    endVertex.deleteEdge(edge);
  }
  /**
   * @param {GraphVertex} startVertex
   * @param {GraphVertex} endVertex
   * @return {(GraphEdge|null)}
   */
  // 查找边
  findEdge(startVertex, endVertex) {
    const vertex = this.getVertexByKey(startVertex.getKey());
    if (!vertex) {
      return null;
    }
    return vertex.findEdge(endVertex);
  }
  /**
   * @return {number}
   */
  // 获取图的权重(所有边的权重之和)
  getWeight() {
    return this.getAllEdges().reduce((weight, graphEdge) => {
      return weight + graphEdge.weight;
    }, 0);
  }
  /**
   * 在有向图中反转所有的边
   * @return {Graph}
   */
  reverse() {
    /** @param {GraphEdge} edge */
    this.getAllEdges().forEach((edge) => {
      // 从图和顶点中删除直边
      this.deleteEdge(edge);
      // 反转边
      edge.reverse();
      // 将反转的边重新添加到图和顶点中
      this.addEdge(edge);
    });
    return this;
  }
  /**
   * @return {object}
   */
  // 获取顶点索引
  getVerticesIndices() {
    const verticesIndices = {};
    this.getAllVertices().forEach((vertex, index) => {
      verticesIndices[vertex.getKey()] = index;
    });
    return verticesIndices;
  }
  /**
   * @return {*[][]}
   */
  // 获取邻接矩阵
  getAdjacencyMatrix() {
    const vertices = this.getAllVertices();
    const verticesIndices = this.getVerticesIndices();
    // 使用Infinity初始化矩阵,表示尚未找到从一个顶点到另一个顶点的路径
    const adjacencyMatrix = Array(vertices.length).fill(null).map(() => {
      return Array(vertices.length).fill(Infinity);
    });
    // 填充列
  vertices.forEach((vertex, vertexIndex) => {
      vertex.getNeighbors().forEach((neighbor) => {
        const neighborIndex = verticesIndices[neighbor.getKey()];
        adjacencyMatrix[vertexIndex][neighborIndex] = this.findEdge(vertex, neighbor).weight;
      });
    });
    return adjacencyMatrix;
  }
  /**
   * @return {string}
   */
  // 将图转换为字符串
  toString() {
    return Object.keys(this.vertices).toString();
  }
}

带你读《图解算法小抄》七、图(2)https://developer.aliyun.com/article/1348216?groupCode=tech_library

相关文章
|
7月前
|
存储 人工智能 算法
图与树的遍历:探索广度优先、深度优先及其他遍历算法的原理与实现
图与树的遍历:探索广度优先、深度优先及其他遍历算法的原理与实现
425 0
|
6月前
|
存储 算法 Java
Java中,树与图的算法涉及二叉树的前序、中序、后序遍历以及DFS和BFS搜索。
【6月更文挑战第21天】Java中,树与图的算法涉及二叉树的前序、中序、后序遍历以及DFS和BFS搜索。二叉树遍历通过访问根、左、右子节点实现。DFS采用递归遍历图的节点,而BFS利用队列按层次访问。以下是简化的代码片段:[Java代码略]
48 4
|
5月前
|
存储 算法 Python
“解锁Python高级数据结构新姿势:图的表示与遍历,让你的算法思维跃升新高度
【7月更文挑战第13天】Python中的图数据结构用于表示复杂关系,通过节点和边连接。常见的表示方法是邻接矩阵(适合稠密图)和邻接表(适合稀疏图)。图遍历包括DFS(深度优先搜索)和BFS(广度优先搜索):DFS深入探索分支,BFS逐层访问邻居。掌握这些技巧对优化算法和解决实际问题至关重要。**
45 1
|
5月前
|
数据采集 存储 算法
「AIGC算法」图搜索算法详解
本文探讨了图搜索算法,包括遍历和最短路径搜索。DFS和BFS是遍历算法,前者使用栈深入搜索,后者用队列逐层遍历。Dijkstra、Bellman-Ford、A*、Floyd-Warshall和Johnson算法则解决最短路径问题。文中还给出了DFS的Python实现示例。这些算法在路径规划、网络分析等领域有重要应用。
103 0
|
7月前
|
存储 算法 C++
c++算法学习笔记 (8) 树与图部分
c++算法学习笔记 (8) 树与图部分
|
7月前
|
算法 数据可视化 大数据
圆堆图circle packing算法可视化分析电商平台网红零食销量采集数据
圆堆图circle packing算法可视化分析电商平台网红零食销量采集数据
|
6月前
|
算法 计算机视觉
图像处理之基于图的广度优先搜索组件标记算法
图像处理之基于图的广度优先搜索组件标记算法
33 0
|
6月前
|
存储 算法
数据结构学习记录——图应用实例-六度空间(题目描述、算法思路、伪代码及解读、图解)
数据结构学习记录——图应用实例-六度空间(题目描述、算法思路、伪代码及解读、图解)
62 0
|
7月前
|
算法 数据可视化
圆填充( CIRCLE PACKING)算法圆堆图圆形空间填充算法可视化
圆填充( CIRCLE PACKING)算法圆堆图圆形空间填充算法可视化
|
7月前
|
存储 算法
图的深度优先算法
图的深度优先算法
42 0