O SGB (= Stanford GraphBase) de Knuth usa uma estrutura de dados sofisticada, definida em C, para representar grafos na memória do computador. As partes mais importantes da estrutura serão resumidas a seguir; os detalhes devem ser estudados na seção 1 do capítulo 2 do Stanford GraphBase e no módulo GB_GRAPH do SGB.
Cada grafo é representado por um struct do tipo Graph. Cada vértice é representado por um struct do tipo Vertex, e cada arco é representado por um struct do tipo Arc. Um programa que trabalhe com essa estrutura de dados poderá declarar variáveis como
Graph *g;
Vertex *v;
Arc *a;
Em lugar de
g aponta para um grafo
ou
g é o endereço de um grafo
,
diremos simplesmente
g é um grafo
.
Analogamente, diremos
v é um vértice
em lugar de
v aponta para um vértice
ou
v é o endereço de um vértice
.
O leque de saída de um vértice v é armazenado em uma lista encadeada (= linked list). Na falta de um nome melhor, vou dizer que esta é a lista de saída do vértice v. É claro que cada arco pertence a uma e uma só lista de saída.
Cada struct do tipo Vertex tem um
campo arcs
que aponta para o primeiro arco da lista de saída do vértice.
Também tem um campo name
que contém o endereço de uma cadeia de caracteres
(= string)
que é o nome
do vértice.
Assim, se v é um vértice
(ou seja, se v é do tipo
Vertex *) então
Cada struct do tipo Arc tem um campo next que contém o endereço do próximo arco na lista de saída a que o arco pertence (é claro que esse outro arco terá a mesma ponta inicial que a). Também tem um campo tip que contém o endereço da ponta final do arco. Assim, se a é um arco então
Cada struct do tipo Graph tem um campo n que armazena o número de vértices do grafo, um campo m que armazena o número de arcos, e um campo id que aponta para o nome do grafo. O conjunto de todos os vértices do grafo é armazenado em um vetor de objetos do tipo Vertex; o campo vertices de Graph contém o endereço desse vetor. Assim, se g é um grafo então
O índice que dá a posição de um vértice v no vetor de vértices é a diferença entre os endereços v e gvertices, ou seja, o número
v − gvertices.
É claro que esse número estará entre 0 e gn − 1.
Formalmente, a estrutura de dados que representa um grafo é definida assim:
typedef struct {
Vertex *vertices;
long n, m;
char *id;
} Graph;
typedef struct vertex_struct {
struct arc_struct *arcs;
char *name;
} Vertex;
typedef struct arc_struct {
struct vertex_struct *tip;
struct arc_struct *next;
} Arc;
Mas essas definições estão incompletas:
os structs Vertex, Arc,
e Graph têm mais alguns campos
auxiliares
de que falarei adiante.
Essas estruturas, bem como os protótipos de várias funções básicas de criação e manipulação de grafos, estão no header file gb_graph.h. Portanto, qualquer programa que usa as estruturas do SGB deve começar com
#include "gb_graph.h"
Para facilitar a vida do usuário, os structs Graph, Vertex e Arc têm alguns campos auxiliares conhecidos como utility fields. Cada um desses campos é um union (no sentido C) do tipo util e pode armazenar o endereço de um vértice, ou o endereço de um arco, ou o endereço de um grafo, ou um string, ou um inteiro.
typedef union {
struct vertex_struct *V;
struct arc_struct *A;
struct graph_struct *G;
char *S;
long I;
} util;
O struct Vertex tem 6 desses campos (além dos campos arcs e name):
util u, v, w, x, y, z;
Suponha, por exemplo, que os vértices do meu grafo representam cidades. Suponha que cada cidade tem uma certa altitude acima do nível do mar e pertence a uma comarca que tem uma cidade-sede. Podemos usar os utility fields z e y, por exemplo, para guardar essas informações: a altitude da cidade v será vz.I e a sede da comarca será vy.V. Podemos dizer que a altitude de v é valtitude e que a sede da comarca é vsede se adotarmos
#define altitude z.I
#define sede y.V
O struct Arc tem apenas dois utility fields, que atendem pelos nomes a e b. O struct Graph tem 6 utility fields: uu, vv, ww, xx, yy e zz.
#define saida u.I
#define entrada v.I
O SGB tem muitas funções que geram grafos interessantes.
O grafo do exemplo 1 do capítulo O que é um grafo?,
por exemplo,
foi gerado pela função
random_
random_graph (9, 10, 1, 1, 1, NULL, NULL, 0, 0, 79).
Trata-se de um grafo pseudo-aleatório
com 9 vértices e 10 arcos.
A semente
do gerador de números pseudo-aleatórios é 79.
(O grafo não é verdadeiramente aleatório:
uma vez dados os valores de todos os parâmetros,
o grafo gerado é sempre o mesmo.)
O valor 1 do terceiro parâmetro significa que o grafo
poderá ter laços;
o valor 1 do quarto parâmetro significa que o grafo poderá ter
arcos paralelos.
exibaalgum dos grafos SGB, ou seja, imprima os nomes de seus vértices e sua matriz de adjacências. Aplique essa função pelo menos aos seguintes grafos do SGB:
random_graph (n, m, 1, 1, 1, NULL, NULL, 0, 0, 0)
econ (n, 2, t, 0)
miles (n, 0, 0, 1, 0, d, 0)
games (n, 0, 0, 0, 0, 0, 0, s)
subsets (q, 1, 1−p, 0, 0, 0, 1, 0)
gunion (board(p, q, 0, 0, −1, 0, 0), board(p, q, 0, 0, −2, 0, 0))
A documentação do seu programa deve fazer uma pequena descrição de cada grafo.
econ (8, 2, 10000, 0)
subsets (2, 1, −3, 0, 0, 0, 1, 1)
subsets (2, 1, −4, 0, 0, 0, 1, 1)
board (5, 4, 0, 0, −1, 0, 1)
board (5, 4, 0, 0, −1, 1, 1)
(Sugestão: use uma adaptação do tarefa A para ver
os grafos.