Problem: You want to create a weighted graph data structure.
Solution: Create structs for nodes and edges and place them in a Graph struct. Create and attach functions to Graph to create nodes and edges for the graph.
Graphs are very common nonlinear data structures that are used everywhere to map relationships between entities. A graph consists of nodes and edges where edges connect two nodes and often represent the relationship between two nodes. Nodes are often given a name and sometimes a value.
You’ll implement a type of graph called the undirected weighted graph , where the edges are also associated with a value, and the edges connect the nodes both ways. As you probably realize, there are many other kinds of graphs, but you can use the same techniques to implement them.
Here are the basic functions of a graph:
AddNode
• Add a new node into the graph
AddEdge
• Add a new edge that connects two nodes
RemoveNode
• Remove an existing node from the graph
RemoveEdge
• Remove an existing edge from the graph
You will use three structs to implement the weighted graph. The Node struct represents a node, with a given name. The Edge struct has a pointer to a node and also a weight. It might look odd but there is a reason for this:
type Node struct { name string } type Edge struct { node *Node weight int } type Graph struct { Nodes []*Node Edges map[string][]*Edge // key is node name }
The last is the Graph struct, which represents the weighted graph. The Graph struct has a slice of pointers to Node structs. It also has a map that has a string key that associates with a slice of pointers to Edge structs. The key for this map is the name of the node and the value is a slice of Edge structs.
Edges are implemented using a map so you can’t create a new Graph struct without also initializing the Edges field. To do this you have a function to create a new Graph struct:
func NewGraph() *Graph { return &Graph{ Edges: make(map[string][]*Edge), } }
func (g *Graph) AddNode(n *Node) { g.Nodes = append(g.Nodes, n) } func (g *Graph) AddEdge(n1, n2 *Node, weight int) { g.Edges[n1.name] = append(g.Edges[n1.name], &Edge{n2, weight}) g.Edges[n2.name] = append(g.Edges[n2.name], &Edge{n1, weight}) } func (g *Graph) RemoveEdge(n1, n2 string) { removeEdge(g, n1, n2) removeEdge(g, n2, n1) } func removeEdge(g *Graph, m, n string) { edges := g.Edges[m] r := -1 for i, edge := range edges { if edge.node.name == n { r = i } } if r > -1 { edges[r] = edges[len(edges)-1] g.Edges[m] = edges[:len(edges)-1] } } func (g *Graph) RemoveNode(name string) { r := -1 for i, n := range g.Nodes { if n.name == name { r = i } } if r > -1 { g.Nodes[r] = g.Nodes[len(g.Nodes)-1] // remove the node g.Nodes = g.Nodes[:len(g.Nodes)-1] } delete(g.Edges, name) // remove the edge from one side // remove the edge from the other side for n := range g.Edges { removeEdge(g, n, name) } }
Removing an edge is a bit more involved. The edges are in a slice that are the values in the Edges map, so you need to iterate through them and mark it if it’s to be removed. Once you find out where the edge is, take the last element in the slice and place it at the index, effectively removing that edge. After that, reslice to truncate the last element. You have to do this twice to remove edges from both directions since this is an undirected graph.
Removing nodes is also a bit more involved but very much the same as removing edges. First, you need to find out the index of the node to remove. Once you do that you take the last element in the slice and place it at the index, then truncate the last element. You also need to remove the edges that are connected to the node. First, delete the edge from the Edges map. Then go through the rest of the other key-value pairs and remove the node accordingly.