180 likes | 718 Views
The Graph Data Structure. Mugurel Ionu ț Andreica Spring 2012. The Elements of a Graph. Vertices (nodes) Numbered from 0 to N-1 (N=total number of vertices) Edges Each edge connects two vertices: (i,j) = edge between i and j
E N D
The Graph Data Structure Mugurel Ionuț Andreica Spring 2012
The Elements of a Graph • Vertices (nodes) • Numbered from 0 to N-1 (N=total number of vertices) • Edges • Each edge connects two vertices: (i,j) = edge between i and j • Undirected graphs: the edge (i,j) is equivalent to the edge (j,i) • Directed graphs: the edges are directed (they are sometimes called arcs) => i->j is different from j->i (although they both connect i and j) • We will only consider graphs where no edge occurs twice (and there are no loops) • Both vertices and edges can have extra information associated to them (e.g. name, cost) • Graphs can be used for modeling many structures, for example: • Road networks • Social relationships
Undirected Graph - example • N=7 vertices (nodes) • Edges • (0,1) • (0,2) • (0,3) • (1,5) • (1,6) • (2,5) • (3,4) • (4,6)
Graph Representations • Adjacency matrix • A = an NxN matrix • A[i][j] = 1 if the edge (i,j) occurs in the graph (i.e. the vertices i and j are connected by an edge), and 0 otherwise for undirected graphs • A[i][j] = 1 if there is a directed arc from i to j for directed graphs • A is symmetrical (A[i][j] == A[j][i]) for undirected graphs • Lists of neighbors • An array of N linked-lists • The list L[i] contains all the neighbors of the vertex i • In the case of directed graphs, the neighbors of a vertex are considered to be those nodes j such that the arch i->j exists • (Simplifying) Assumptions • No multiple edges (i,j) • No loops (i,i)
Graph Traversals • Depth-First Search (DFS) • Mark all the vertices as not visited • Starting vertex S => call dfs(S) • dfs(x) • Mark x as visited • For y in Neighbors(X): • If (y has not been marked as visited) then call dfs(y) • DFS is the building block for many more advanced algorithms • A simple application: check if a graph is connected • Breadth-First Search (BFS) • Mark all the vertices as not visited and initialize an empty queue Q • Starting vertex S => insert S into a queue Q (Q.enqueue(S)) and mark S as visited • While Q is not empty: • x = Q.dequeue() • For y in Neighbors(X): • If (y has not been marked as visited) then • Mark y as visited • Q.enqueue(y) • BFS can be used for computing shortest paths in the graph
Using the Adjacency Matrix (Undirected Graph) for (i = 0; i < N; i++) for (j = 0; j < N; j++)A[i][j] = 0; // allocate the array with node information nodeInfo = new TnodeInfo[N]; // allocate the matrix of edge information edgeInfo = new TedgeInfo*[N]; for (i = 0; i < N; i++) edgeInfo[i] = new TedgeInfo[N]; } void setNodeInfo(int i, TnodeInfo info){ nodeInfo[i] = info; } TnodeInfo getNodeInfo(int i){ return nodeInfo[i]; } void addEdge(int i, int j){ A[i][j] = A[j][i] = 1;} #include <stdio.h> #include "queue1.h" template<typename TnodeInfo,typenameTedgeInfo> class Graph{ public: int N; char **A; TnodeInfo *nodeInfo; TedgeInfo **edgeInfo; Graph(int numNodes){ int i, j; N = numNodes; // allocate the adjacency matrix A = new char*[N]; for (i = 0; i < N; i++) A[i] = new char[N];
Using the Adjacency Matrix (cont.) void removeEdge(int i, int j){ A[i][j] = A[j][i] = 0;} void setEdgeInfo(int i, int j, TedgeInfo info){ edgeInfo[i][j] = edgeInfo[j][i] = info;} TedgeInfo getEdgeInfo(int i, int j){ return edgeInfo[i][j];} ~Graph() { int i; for (i = 0; i < N; i++) { delete A[i]; delete edgeInfo[i]; } delete A; delete edgeInfo; delete nodeInfo; } }; Graph<int, int> g(7); char* visited; void dfs(int x){ int y; printf("%d\n", x); visited[x] = 1; for (y = 0; y < g.N; y++) if (g.A[x][y] && !visited[y]) dfs(y); } int *dist; void bfs(int S){ Queue<int> Q; int x, y; Q.enqueue(S); visited[S] = 1; dist[S] = 0; while (!Q.isEmpty()){ x = Q.dequeue(); printf("%d: dist=%d\n", x, dist[x]);
Using the Adjacency Matrix (cont.) visited = new char[g.N]; for (i = 0; i < g.N; i++) visited[i] = 0; printf("DFS:\n"); dfs(4); for (i = 0; i < g.N; i++) visited[i] = 0; dist = new int[g.N]; printf("BFS:\n"); bfs(4); return 0; } for (y = 0; y < g.N; y++) if (g.A[x][y] && !visited[y]){ visited[y] = 1; dist[y] = dist[x] + 1; Q.enqueue(y); } } } int main(){ int i; g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(0, 3); g.addEdge(1, 6); g.addEdge(2, 5); g.addEdge(3, 4); g.addEdge(4, 6); g.addEdge(5, 1);
Using the Lists of Neighbors (Undirected Graph) #include <stdio.h> #include "linked_list.h" #include "queue1.h" template<typename TedgeInfo> struct list_elem_info { int node; TedgeInfo edgeInfo; }; template<typename TnodeInfo, typename TedgeInfo> class Graph{ public: int N; LinkedList<struct list_elem_info<TedgeInfo> > *L; TnodeInfo *nodeInfo; Graph(int numNodes){ N = numNodes; L = new LinkedList<struct list_elem_info<TedgeInfo> > [N]; nodeInfo = new TnodeInfo[N]; } void setNodeInfo(int i, TnodeInfo info){ nodeInfo[i] = info; } TnodeInfo getNodeInfo(int i){ return nodeInfo[i]; } void addEdge(int i, int j){ struct list_elem_info<TedgeInfo> lei_i, lei_j; lei_i.node = j; lei_j.node = i; L[i].addFirst(lei_i); L[j].addFirst(lei_j); } void removeEdge(int i, int j){ struct list_elem<struct list_elem_info<TedgeInfo> > *p;
Using the Lists of Neighbors (cont.) p = L[i].pfirst; while (p != NULL){ if (p->info.node == j) break; p = p->next; } p->info.edgeInfo = info; p = L[j].pfirst; while (p != NULL){ if (p->info.node == i) break; p = p->next; } p->info.edgeInfo = info; } TedgeInfo getEdgeInfo(int i, int j){ struct list_elem<struct list_elem_info<TedgeInfo> > *p; p = L[i].pfirst; while (p != NULL){ if (p->info.node == j) break; p = p->next; } // remove the element pointed to by p from L[i] -- code ommitted p = L[j].pfirst; while (p != NULL){ if (p->info.node == i) break; p = p->next; } // remove the element pointed to by p from L[j] -- code ommitted } void setEdgeInfo(int i, int j, TedgeInfo info){ struct list_elem<struct list_elem_info<TedgeInfo> > *p;
Using the Lists of Neighbors (cont.) void dfs(int x){ int y; struct list_elem<struct list_elem_info<int> > *p; printf("%d\n", x); visited[x] = 1; p = g.L[x].pfirst; while (p != NULL){ y = p->info.node; if (!visited[y]) dfs(y); p = p->next; } } int *dist; void bfs(int S){ Queue<int> Q; int x, y; struct list_elem<struct list_elem_info<int> > *p; p = L[i].pfirst; while (p != NULL){ if (p->info.node == j) break; p = p->next; } return p->info.edgeInfo; } ~Graph() { int i; delete nodeInfo; for (i = 0; i < N; i++) while (!L[i].isEmpty()) L[i].removeFirst(); delete L; } }; Graph<int, int> g(7); char* visited;
Using the Lists of Neighbors (cont.) Q.enqueue(S); visited[S] = 1; dist[S] = 0; while (!Q.isEmpty()){ x = Q.dequeue(); printf("%d: dist=%d\n", x, dist[x]); p = g.L[x].pfirst; while (p != NULL){ y = p->info.node; if (!visited[y]){ visited[y] = 1; dist[y] = dist[x] + 1; Q.enqueue(y); } p = p->next; } } } int main(){ int i; g.addEdge(0, 1);g.addEdge(0, 2); g.addEdge(0, 3);g.addEdge(1, 6); g.addEdge(2, 5);g.addEdge(3, 4); g.addEdge(4, 6);g.addEdge(5, 1); visited = new char[g.N]; for (i = 0; i < g.N; i++) visited[i] = 0; printf("DFS:\n"); dfs(4); for (i = 0; i < g.N; i++) visited[i] = 0; dist = new int[g.N]; printf("BFS:\n"); bfs(4); return 0; }
Implementing Directed Graphs • What needs to be changed in the previous two implementations in order to have directed graphs instead of undirected graphs ?