Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: March 18, 2024
In this tutorial, we’ll talk about weighted and unweighted graphs. We’ll explain how they differ and show how we can represent them in computer programs.
A graph is a collection of connected objects. They can be anything from purely mathematical concepts to real-world objects and phenomena. For example, a collection of people with family ties is a graph. So is a set of cities interconnected with roads.
Usually, we refer t0 the graph’s objects as nodes or vertices and to the connections between them as edges or arcs. For example, this is how we’d visualize a graph of cities and roads:
Each node represents a city, and each edge denotes a road from one city to another.
If we care only if two nodes are connected or not, we call such a graph unweighted. For the nodes with an edge between them, we say they are adjacent or neighbors of one another.
We can represent an unweighted graph with an adjacency matrix. It’s an matrix consisting of zeros and ones, where
is the number of nodes. If its element
is 1, that means that there’s an edge between the
-th and
-th nodes. If
, then there’s no edge between the two nodes.
This is how the adjacency matrix of the above roadmap graph would look like:
It’s symmetrical, as is the case with all undirected graphs.
The matrix representation has two assumptions:
When those assumptions don’t hold or the graph is sparse, we use adjacency lists.
For each node, we maintain a linked list of its neighbors:
The main advantage of this representation is that the space for storing the graph need not be consecutive. However, we lose a very useful property of matrix representation, and that’s the access complexity.
We can read the information on the adjacency of -th and
-th nodes by accessing
in a constant time, no matter the values of
and
. With linked lists, we may traverse the whole list. In the worst case, the
-th node is a neighbor to all the vertices but the
-th vertex from our query. So, we’ll check
items in the linked list to conclude that the two nodes aren’t connected.
The unweighted graphs tell us only if two nodes are linked. So, they’re suitable for queries such as:
However, in many applications, the edges have numerical properties that we need to exploit in our algorithms to solve the problem at hand. For example, we must consider road lengths and traffic density while looking for the shortest path between two cities. So, we associate each edge with a real value
that we call its weight. We call such graphs weighted.
This is how the roadmap graph looks like when we include the edge weights, that is, the lengths of the roads:
Just as nodes, the weights can be anything relevant for the problem at hand: lengths, densities, duration, costs, probabilities, and so on.
The ways to represent weighted graphs are extensions of the unweighted graph’s representations.
The weight matrix is a real matrix whose element
represents the weight of the edge between the
-th and
-th nodes:
The weights of actual edges are usually positive, so zero denotes that no edge exists between two nodes. However, if our application is such, the weights could be negative. In that case, we’ll have to define a special symbol that would indicate the absence of an edge in the matrix.
If we represent our graph via linked lists, we’ll have to store the edge weights alongside the neighbors in those lists:
Even the unweighted graphs can be considered weighted but with a special weight function :
Moreover, if the weights are all the same or the cost of a path depends only on the number of nodes it passes through, and we have the formula to calculate it, we don’t have to use the weighted representation. We can run the graph algorithms on the adjacency matrix and derive the solution with weights afterward.
Both weighted and unweighted graphs can be explicit or implicit. The explicit graphs are those whose nodes and edges we can enumerate and store in main or secondary memory, provided there’s enough space. So, before we run any algorithm on them, the explicit graphs are constructed in their entirety. The graph of cities and roads is an example of an explicit graph.
However, the graphs are sometimes so large or complicated that we can’t construct them in advance. Instead, we have a procedure that grows the graph as we explore it and constructs only the parts we need. Such graphs are known as implicit ones. We often use them in AI, where many search problems have infinite search spaces. Each candidate solution is a node in the search graph at hand, and when we want to find the neighboring solutions, we use a function that computes the neighbors on the fly.
The difference between the explicit and implicit graphs is analogous to that between eager and lazy loading.
In this article, we talked about the unweighted and weighted graphs. A graph of the former type is suitable for applications where we need to know only if two objects are directly connected via an edge. The latter we use when the edges have associated weights, such as costs, lengths, and similar.