Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
June 21, 2022 01:48 pm GMT

Data Structures: Graphs II

This is part 2 of my article on Graphs in my data structures series. In part 1 we learned about graph terminology, types of graphs, and some real-life applications of graph data structures. In this part of the series we are going to look at graph representation, and we're going to build a graph in Javascript code.

Let us begin gif

Graph Representation

Graphs representation deals with how graphs can be written/implemented in code. The two most common ways to represent a graph are through an Adjacency list or an Adjacency matrix

Adjacency List

The adjacency list is very straight forward, a list of all vertices each with a list of their edges( adjacent nodes). A list can be any linear or iterable data structure (Arrays, Maps, Linked Lists, Queues). Below is are examples of graphs using adjacency list.

Directed Graph

Directed Graph using Adjacency list

Undirected Graph

Undirected Graph using Adjacency list

Weighted Graph

Weighted Undirected Graph using Adjacency list

Pros

  • It is very memory efficient when used on sparse graphs (graphs with few edges between the vertices).

  • It is easy to understand and implement.

  • It is fast at creating and deleting edges and vertices.

Cons

  • It is less time efficient at verifying if vertices are adjacent to each other.

Adjacency Matrix

The adjacency matrix is a Square matrix whose rows and columns are equal to the number of vertices in a graph, edges are represented by the intersections between rows and columns. Edges are denoted with the number 1 when present and 0 when not. Weighted edges are denoted by their weight. Below is are examples of graphs using adjacency matrix.

Directed Graph

Directed Graph using Adjacency Matrix

Undirected Graph

Undirected Graph using Adjacency Matrix

Weighted Graph

Weighted Undirected Graph using Adjacency Matrix

In the above images we can see that the intersections AxA, BxB, CxC, DxD and ExE are all 0; this is because there are no self-loops in these graphs. A self-loop is a vertex in a graph that points to itself.

Pros

  • It is very time efficient when used with dense or complete graphs (graphs with a lot of edges between vertices are called dense while graphs with all possible edges are called complete).

Cons

  • It is slow at creating and deleting edges and vertices.

  • It is a lot more complex than the adjacency list.

  • It is very memory inefficient when used for sparse graphs (most graphs are sparse in nature)

Javascript Implementation

We are going to be building a graph using Adjacency List representation. Our graph can be weighted, directed or undirected. Our graph methods will be:

  • AddVertex: Add a vertex to the graph.
  • RemoveVertex: Delete a vertex.
  • CreateEdge: Add an edge between 2 vertices.
  • DeleteEdge: Remove an edge between 2 edges.
  • IsPresent: Check if a vertex exists.

Graphs are made of vertices/nodes so let's first create our node constructor before moving on to the graph constructor.

class Node {  constructor(value) {    (this.value = value),    (this.edgeList = {});  }  addEdge(node, weight) {    this.edgeList[node.value] = weight ? weight : 0;  }  removeEdge(node) {    delete this.edgeList[node.value];  }}

Explanation

Our nodes will have two properties and methods.

  • The value property will store the value of our node.
  • The edgeList property stores the edges of our node.
  • AddEdge is a method that adds an edge and weight to the edgeList of a node. The value of the node passed to addEdge is used as the key and the weight is the value. if no weight is given we set it to 0.
  • DeleteEdge is a method to delete an edge from the edgeList. It uses the value of the node passed to it to access it and delete it.

Now let's take a look at our graph constructor and how our various methods work.

class Graph {  constructor(type) {    (this.nodes = new Map()),     (this.directed = type ? type : false);  }  addVertex(value) {    let node = new Node(value);    this.nodes.set(value, node);  }  removeVertex(value) {    let vertex = this.nodes.get(value);    if (vertex) {      for (const node of this.nodes.values()) {        node.removeEdge(vertex);      }    }    return vertex;  }  createEdge(startNode, endNode, weight) {    let startVertex = this.nodes.get(startNode);    let endVertex = this.nodes.get(endNode);    if (startVertex && endVertex) {      startVertex.addEdge(endVertex, weight);      if (!this.directed) {        endVertex.addEdge(startVertex, weight);      }    }else {      console.log("non-existent vertex passed")    }  }  deleteEdge(startNode, endNode) {    let startVertex = this.nodes.get(startNode);    let endVertex = this.nodes.get(endNode);    startVertex.removeEdge(endVertex);    endVertex.removeEdge(startVertex)  }  getVertex(value){    return this.nodes.get(value)  }  isPresent(value){    return this.nodes.has(value)  }}

Explanation

Our graph will have two properties a Map object to store the vertices/nodes and a boolean to determine the type of graph directed or undirected. if true is passed when the function is called, it is a directed graph; otherwise it is undirected.

  addVertex(value) {    let node = new Node(value);    this.nodes.set(value, node);  }
  • AddVertex is our first method. It simply takes a value and creates a node which it adds to the graph Map object.
 removeVertex(value) {    let vertex = this.nodes.get(value);    if (vertex) {      for (const node of this.nodes.values()) {        node.removeEdge(vertex);      }    }    this.nodes.delete(value);    return vertex;  }
  • The removeVertex deletes a vertex from a graph. It takes a value and checks if it exists in the graph, if it does, it loops through the nodes Map object and deletes the node from the edgeList of all nodes, and then deletes it from the nodes map object.
  createEdge(startNode, endNode, weight) {    let startVertex = this.nodes.get(startNode);    let endVertex = this.nodes.get(endNode);    if (startVertex && endVertex) {      startVertex.addEdge(endVertex, weight);      if (!this.directed) {        endVertex.addEdge(startVertex, weight);      }    }else {      console.log("non-existent vertex passed")    }  }
  • The createEdge method creates an edge in the graph; it takes 3 parameters when called, the start and end vertices, and the weight of the edge. First, it checks that both vertices are present in the node, if they are it; it calls addEdge on the start vertex and passes it the end vertex and the weight. if the graph is undirected it calls addEdge on the end vertex and passes it the start vertex and the weight. if either of the vertices does not exist we let the user know.
  deleteEdge(startNode, endNode) {    let startVertex = this.nodes.get(startNode);    let endVertex = this.nodes.get(endNode);    startVertex.removeEdge(endVertex);    endVertex.removeEdge(startVertex)  }
  • The deleteEdge method deletes an edge from the graph but not the vertices. It takes two parameters when called, the start and end vertices/nodes and calls the removeEdge method on both.
  getVertex(value){    return this.nodes.get(value)  }
  • The getVertex method returns a vertex from the nodes map object.
 isPresent(value){    return this.nodes.has(value)  }
  • The isPresent method simply checks if a node/vertex exists in the nodes Map object and returns a boolean.

Conclusion

The code for this article can be found here. Graphs are very complex and useful data structure, if you would to learn more about graphs, check out this video thanks for reading and see you later.

peace out


Original Link: https://dev.to/m13ha/data-structures-graphs-ii-11i9

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To