图的表示及遍历

1. 图的表示

1)临接矩阵

使用二维数组arr[N][N]表示一个图。

a. N 为图的顶点个数,矩阵的对角线全为0。

b. 两个顶点连通的话,矩阵的值为1

c. 某一行的和表示该顶点的出度。某一列的和表示该顶点的入度

d. 有权值的图,矩阵元素不再是0,1表示是否连通,而是把元素值表示为权值。不存在的边,权值记录为;对角线上的权值为0

图的表示及遍历

图的表示及遍历

使用临接矩阵表示图,直观方便,缺点是占用空间大,因为需要 N*N 个空间。对于稀疏图来说,这比较浪费空间。

2) 临接表

a. 使用数组保存顶点

b. 每个顶点的所有临接点组成一个线性表,挂在数组后面。

这种方法类似散列表的开链法。

图的表示及遍历

优点是节省空间,顶点的出度就在链表上,但是要查找入度的话,需要遍历整个图

2. 图的遍历

1)广度优先

先遍历图的所有临节点,再依次遍历临接点的临接点

2)深度优先

从某一个节点出发,一直走下去,直到找不到临接点。再从其他节点出发,继续一条道路走到黑

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>


enum Ecolor
{
    WHITE = 0,
    GRAY,
    BLACK
};

// 这里使用临接表表示图
struct graph_node_s
{
    enum Ecolor color;  // 节点颜色,用来标识该顶点是否别遍历过
    int d;
    int ftime;          // 节点访问结束时间
    int dtime;          // 节点访问开始时间
    struct graph_node_s *next;
};
typedef struct graph_node_s graph_node_t;

struct graph_s
{
    int vertice;        // 顶点数
    int edge;           // 边数
    graph_node_t *bucket[1];    // 保存顶点节点
};
typedef struct graph_s graph_t;

// 广度优先遍历使用了队列保存临接点
struct queue_node_s
{
    void *d;
    struct queue_node_s *prev;
    struct queue_node_s *next;
};
typedef struct queue_node_s queue_node_t;

struct queue_s
{
    queue_node_t *sentinel;
};
typedef struct queue_s queue_t;


queue_t *queue_create(void)
{
    queue_t *queue = (queue_t*)malloc(sizeof(queue_t));
    assert(queue);

    queue->sentinel = (queue_node_t*)malloc(sizeof(queue_node_t));
    assert(queue->sentinel);

    queue->sentinel->next = queue->sentinel;
    queue->sentinel->prev = queue->sentinel;

    return queue;
}

void queue_destroy(queue_t *q)
{
    assert(q);

    queue_node_t *nd = q->sentinel->next;
    while (nd != q->sentinel)
    {
        queue_node_t *tmp = nd;
        nd = nd->next;
        free(tmp);
    }
    free(nd);
    free(q);
}

void queue_push(queue_t *q, void *value)
{
    assert(q);
    assert(value);

    queue_node_t* nd = (queue_node_t*)malloc(sizeof(queue_node_t));
    assert(nd);

    nd->d = value;
    nd->next = q->sentinel;
    nd->prev = q->sentinel->prev;
    q->sentinel->prev->next = nd;
    q->sentinel->prev = nd;
}

void queue_pop(queue_t *q)
{
    assert(q);

    queue_node_t *tmp = q->sentinel->next;

    if (tmp != q->sentinel)
    {
        q->sentinel->next = q->sentinel->next->next;
        q->sentinel->next->prev = q->sentinel;
        free(tmp);
    }
}

void *queue_front(queue_t *q)
{
    assert(q);

    return q->sentinel->next->d;
}

int queue_empty(queue_t *q)
{
    assert(q);

    return q->sentinel->next == q->sentinel;
}

graph_t *graph_create(int vertice)
{
    graph_t *g = (graph_t*)malloc(sizeof(graph_t) + (vertice-1)*sizeof(graph_node_t*));
    assert(g);

    g->vertice = vertice;
    g->edge = 0;
    for (int i = 0; i < vertice; ++i)
    {
        g->bucket[i] = (graph_node_t*)malloc(sizeof(graph_node_t));
        assert(g->bucket[i]);

        g->bucket[i]->d = i;
        g->bucket[i]->color = WHITE;
        g->bucket[i]->next = NULL;
    }

    return g;
}

void graph_destroy(graph_t *g)
{
    assert(g);

    for (int i = 0; i < g->vertice; ++i)
    {
        graph_node_t *head = g->bucket[i];
        while (head)
        {
            graph_node_t *tmp = head;
            head = head->next;
            free(tmp);
        }
    }
    free(g);
}

void graph_add_edge(graph_t *g, int src, int dst)
{
    assert(g);
    assert(src < g->vertice);
    assert(dst < g->vertice);

    graph_node_t *nd = (graph_node_t*)malloc(sizeof(graph_node_t));
    assert(nd);
    nd->d = dst;
    nd->color = WHITE;
    nd->next = g->bucket[src]->next;
    g->bucket[src]->next = nd;
}

static void _graph_dfs(graph_t *g, int index, int *time)
{
    assert(g);
    assert(time);
    assert(index < g->vertice);

    (*time)++;
    printf("%d ", g->bucket[index]->d);
    g->bucket[index]->color = GRAY;
    g->bucket[index]->dtime = *time;

    graph_node_t *tmp = g->bucket[index]->next;
    while (tmp)
    {
        if (g->bucket[tmp->d]->color == WHITE)
        {
            // 对临接点进行深度遍历
            _graph_dfs(g, tmp->d, time);
        }
        tmp = tmp->next;
    }

    g->bucket[index]->color = BLACK;
    (*time)++;
    g->bucket[index]->ftime = *time;
}

// 深度优先遍历
void graph_dfs(graph_t *g)
{
    assert(g);

    int time = 0;
    for (int i = 0; i < g->vertice; ++i)
    {
        if (g->bucket[i]->color == WHITE)
        {
            _graph_dfs(g, i, &time);
        }
    }
}

// 广度优先遍历
void graph_bfs(graph_t *g, int index)
{
    assert(g);
    assert(index < g->vertice);

    queue_t *q = queue_create();

    queue_push(q, g->bucket[index]);
    while (!queue_empty(q))
    {
        graph_node_t *nd = queue_front(q);
        if (g->bucket[nd->d]->color == WHITE)
        {
            printf("%d ", nd->d);
            g->bucket[nd->d]->color = BLACK;

            graph_node_t *head = g->bucket[nd->d]->next;
            while (head)
            {
                // 所有临接点入队
                queue_push(q, g->bucket[head->d]);
                head = head->next;
            }
        }
        queue_pop(q);
    }

    queue_destroy(q);
}

int main()
{
    graph_t *g = graph_create(6);

    graph_add_edge(g, 0, 1);
    graph_add_edge(g, 2, 3);
    graph_add_edge(g, 4, 5);
    graph_add_edge(g, 3, 5);
    

    graph_bfs(g, 0);

    graph_destroy(g);

    return 0;
}