Daily Tech Brief

Top startup stories in your inbox

Subscribe Free

© 2026 rakrisi Daily

Graph Representations - How to Store Connections in Memory

Graph Representations: How to Store Connections in Memory 📚

Now that you understand what graphs are, let’s explore different ways to store them in your computer’s memory. Just like you can organize your books in different ways (alphabetical, by color, by size), there are different ways to store graph connections in code.

Each representation has its own advantages and disadvantages, so choosing the right one depends on what you want to do with your graph!

Method 1: Adjacency List (Most Common) 📝

An adjacency list is like each person having a list of their friends. This is the most common way to store graphs because it’s memory-efficient and fast for most operations.

package main

import "fmt"

// Adjacency List representation
type Graph struct {
    vertices int           // Number of nodes
    adjList  [][]int       // For each vertex, list of connected vertices
}

func NewGraph(vertices int) *Graph {
    return &Graph{
        vertices: vertices,
        adjList:  make([][]int, vertices),
    }
}

func (g *Graph) AddEdge(from, to int) {
    // Add edge from -> to
    g.adjList[from] = append(g.adjList[from], to)

    // For undirected graphs, also add reverse edge
    // g.adjList[to] = append(g.adjList[to], from)
}

func (g *Graph) GetNeighbors(vertex int) []int {
    return g.adjList[vertex]
}

func (g *Graph) PrintGraph() {
    for i := 0; i < g.vertices; i++ {
        fmt.Printf("Vertex %d: ", i)
        for _, neighbor := range g.adjList[i] {
            fmt.Printf("%d ", neighbor)
        }
        fmt.Println()
    }
}

func main() {
    // Create a graph with 5 vertices
    g := NewGraph(5)

    // Add edges (undirected graph)
    g.AddEdge(0, 1)
    g.AddEdge(0, 4)
    g.AddEdge(1, 2)
    g.AddEdge(1, 3)
    g.AddEdge(1, 4)
    g.AddEdge(2, 3)
    g.AddEdge(3, 4)

    // Add reverse edges for undirected graph
    g.AddEdge(1, 0)
    g.AddEdge(4, 0)
    g.AddEdge(2, 1)
    g.AddEdge(3, 1)
    g.AddEdge(4, 1)
    g.AddEdge(3, 2)
    g.AddEdge(4, 3)

    g.PrintGraph()
    // Output:
    // Vertex 0: 1 4
    // Vertex 1: 2 3 4 0
    // Vertex 2: 3 1
    // Vertex 3: 4 1 2
    // Vertex 4: 0 1 3
}

Pros of Adjacency List:

  • Memory efficient (only stores existing connections)
  • Fast to iterate through neighbors
  • Easy to add/remove edges

Cons of Adjacency List:

  • Slower to check if two vertices are connected
  • Not great for dense graphs

Method 2: Adjacency Matrix (Easy to Understand) 📊

An adjacency matrix is like a big grid where rows and columns represent vertices, and a 1 means “connected”, 0 means “not connected”.

package main

import "fmt"

// Adjacency Matrix representation
type MatrixGraph struct {
    vertices int
    matrix   [][]int // 2D grid of connections
}

func NewMatrixGraph(vertices int) *MatrixGraph {
    matrix := make([][]int, vertices)
    for i := range matrix {
        matrix[i] = make([]int, vertices)
    }

    return &MatrixGraph{
        vertices: vertices,
        matrix:   matrix,
    }
}

func (mg *MatrixGraph) AddEdge(from, to int) {
    mg.matrix[from][to] = 1

    // For undirected graphs
    mg.matrix[to][from] = 1
}

func (mg *MatrixGraph) IsConnected(from, to int) bool {
    return mg.matrix[from][to] == 1
}

func (mg *MatrixGraph) GetDegree(vertex int) int {
    degree := 0
    for i := 0; i < mg.vertices; i++ {
        if mg.matrix[vertex][i] == 1 {
            degree++
        }
    }
    return degree
}

func (mg *MatrixGraph) PrintMatrix() {
    for i := 0; i < mg.vertices; i++ {
        for j := 0; j < mg.vertices; j++ {
            fmt.Printf("%d ", mg.matrix[i][j])
        }
        fmt.Println()
    }
}

func main() {
    mg := NewMatrixGraph(4)

    // Add edges
    mg.AddEdge(0, 1)
    mg.AddEdge(0, 2)
    mg.AddEdge(1, 2)
    mg.AddEdge(2, 3)

    fmt.Println("Adjacency Matrix:")
    mg.PrintMatrix()
    // Output:
    // 0 1 1 0
    // 1 0 1 0
    // 1 1 0 1
    // 0 0 1 0

    fmt.Printf("Is 0 connected to 2? %t\n", mg.IsConnected(0, 2)) // true
    fmt.Printf("Degree of vertex 2: %d\n", mg.GetDegree(2)) // 3
}

Pros of Adjacency Matrix:

  • Very fast to check if two vertices are connected
  • Simple to understand and implement
  • Good for dense graphs

Cons of Adjacency Matrix:

  • Uses lots of memory (especially for sparse graphs)
  • Slower to find all neighbors of a vertex
  • Fixed size - can’t easily add new vertices

Method 3: Edge List (Simple Storage) 📋

An edge list is just a list of all the connections in the graph. Simple but not very efficient for most operations.

package main

import "fmt"

// Edge representation
type Edge struct {
    From, To int
}

// Edge List representation
type EdgeListGraph struct {
    vertices int
    edges    []Edge
}

func NewEdgeListGraph(vertices int) *EdgeListGraph {
    return &EdgeListGraph{
        vertices: vertices,
        edges:    make([]Edge, 0),
    }
}

func (elg *EdgeListGraph) AddEdge(from, to int) {
    elg.edges = append(elg.edges, Edge{from, to})

    // For undirected graphs
    elg.edges = append(elg.edges, Edge{to, from})
}

func (elg *EdgeListGraph) GetAllEdges() []Edge {
    return elg.edges
}

func (elg *EdgeListGraph) CountEdges() int {
    return len(elg.edges)
}

func main() {
    elg := NewEdgeListGraph(4)

    elg.AddEdge(0, 1)
    elg.AddEdge(0, 2)
    elg.AddEdge(1, 2)
    elg.AddEdge(2, 3)

    fmt.Printf("Total edges: %d\n", elg.CountEdges())
    fmt.Println("All edges:")
    for _, edge := range elg.GetAllEdges() {
        fmt.Printf("%d -> %d\n", edge.From, edge.To)
    }
}

Method 4: Weighted Graphs (With Costs/Distances) ⚖️

Sometimes connections have weights - like distances between cities or costs of flights.

package main

import "fmt"

// Weighted Edge
type WeightedEdge struct {
    From, To, Weight int
}

// Weighted Adjacency List
type WeightedGraph struct {
    vertices int
    adjList  [][]WeightedEdge
}

func NewWeightedGraph(vertices int) *WeightedGraph {
    return &WeightedGraph{
        vertices: vertices,
        adjList:  make([][]WeightedEdge, vertices),
    }
}

func (wg *WeightedGraph) AddWeightedEdge(from, to, weight int) {
    wg.adjList[from] = append(wg.adjList[from], WeightedEdge{from, to, weight})

    // For undirected weighted graphs
    wg.adjList[to] = append(wg.adjList[to], WeightedEdge{to, from, weight})
}

func (wg *WeightedGraph) GetWeightedNeighbors(vertex int) []WeightedEdge {
    return wg.adjList[vertex]
}

func main() {
    wg := NewWeightedGraph(4)

    // Add weighted edges (flight costs)
    wg.AddWeightedEdge(0, 1, 100) // NYC to Boston: $100
    wg.AddWeightedEdge(0, 2, 500) // NYC to LA: $500
    wg.AddWeightedEdge(1, 2, 400) // Boston to LA: $400
    wg.AddWeightedEdge(2, 3, 200) // LA to Seattle: $200

    fmt.Println("Flights from NYC (0):")
    for _, edge := range wg.GetWeightedNeighbors(0) {
        fmt.Printf("To city %d: $%d\n", edge.To, edge.Weight)
    }
}

Choosing the Right Representation 🎯

Use CaseBest RepresentationWhy
Sparse graph (few connections)Adjacency ListMemory efficient
Dense graph (many connections)Adjacency MatrixFast lookups
Need to store edge weightsWeighted Adjacency ListNatural fit
Simple storageEdge ListMinimal memory
Need fast connectivity checksAdjacency MatrixO(1) lookup
Need to iterate neighbors oftenAdjacency ListFast iteration

Real-World Examples 🌍

Social Network (Sparse) → Adjacency List

socialGraph := NewGraph(1000000) // 1 million users
// Most users have <100 friends, so adjacency list saves memory

Flight Network (Dense) → Adjacency Matrix

flightMatrix := NewMatrixGraph(100) // 100 airports
// Many airports connect to many others, matrix is fine

Road Network (Weighted) → Weighted Adjacency List

roadGraph := NewWeightedGraph(50000) // 50k intersections
// Each road has distance/length as weight

Practice Problems 🎯

  1. Memory Comparison: Compare memory usage of adjacency list vs matrix for a graph with 100 vertices and 200 edges
  2. Fastest Check: Which representation is fastest to check if vertex A connects to vertex B?
  3. Neighbor Iteration: Which representation is fastest to list all friends of a person?
  4. Weighted Social: Modify the friendship graph to include “closeness” weights (1-10)

What’s Next? 🚀

Now that you know how to store graphs in memory, the next lesson will teach you how to traverse graphs - visiting all the nodes in different orders. We’ll learn Breadth-First Search (BFS) and Depth-First Search (DFS), which are fundamental graph algorithms!

Understanding graph representations is crucial because it affects the efficiency of all your graph algorithms. Choose wisely based on your use case! 📊