图的C语言描述

摘要

邻接表是标示图的标准方法。图论算法中通常需要找出与某个给定顶点v邻接的所有顶点。而这可以通过简单地扫描相应的邻接表来完成,所用时间与这些顶点的个数成正比。

C语言中我们无法通过名字作为数组的索引,因此我们必须提供从名字到数字的映射。完成这项工作最容易的方法是使用散列表(HashTable),在每条边被输入的时,我们通过该散列表检查它指向的定点是否已经存在,如果不存在我们生成并将该点加入到图中且set到散列表中,如果存在我们获>取其值,即图中的数组下标。

无权图最短路径的计算需要用到队列结构。我们先将起点的known和dist属性置为1和0,入列,然后出列,将该点的所有边指的距离未知(known属性为0)的点都计算dist(所在顶点的dist+1)并置known为1然后入列,如此反复。

我们先声明图的数据结构,和相关操作方法:

#define MAX_GRAPH_V_NODE_NUM 256
#define MAX_NAME_SIZE 32 

typedef struct e_node_s {
    int idx;
    struct e_node_s *next;
} e_node_t;

typedef struct v_node_s {
    char name[MAX_NAME_SIZE];
    int known;
    int dist;
    e_node_t *first_e_node;
    e_node_t *last_e_node;
} v_node_t;

typedef struct graph_s {
    v_node_t *v_node_list[MAX_GRAPH_V_NODE_NUM];
    int v_node_num;
    int e_node_num;
} graph_t;


e_node_t *e_node_init(int idx);
v_node_t *v_node_init(char *name);
graph_t *graph_init();
int graph_add_e_node(e_node_t *e, int idx, graph_t *g);
int graph_add_v_node(v_node_t *v, graph_t *g);
v_node_t *graph_get_v_node(int idx, graph_t *g);
int graph_set_v_node(v_node_t *v, int idx, graph_t *g);

int graph_get_shortest_path(char *vname, htable_t *htable, graph_t *g);

int graph_print(graph_t *g);

具体方法的定义实现:

e_node_t *e_node_init(int idx) {
    e_node_t *e = malloc(sizeof(e_node_t));
    e->idx = idx;
    e->next = NULL;
    return e;
}

v_node_t *v_node_init(char *name) {
    v_node_t *v = malloc(sizeof(v_node_t));
    snprintf(v->name, MAX_NAME_SIZE, "%s", name);
    v->known = 0;
    v->dist = 0;
    v->first_e_node = NULL;
    v->last_e_node = NULL;
    return v;
}

graph_t *graph_init() {
    graph_t *g = malloc(sizeof(graph_t));
    g->v_node_num = 0;
    g->e_node_num = 1;
    return g;
}

int graph_add_v_node(v_node_t *v, graph_t *g) {
    g->v_node_list[g->v_node_num] = v;
    g->v_node_num ++;
    return g->v_node_num - 1;
}

v_node_t *graph_get_v_node(int idx, graph_t *g) {
    return g->v_node_list[idx];
}

int graph_set_v_node(v_node_t *v, int idx, graph_t *g) {
    g->v_node_list[idx] = v;
    return 0;
}

int graph_add_e_node(e_node_t *e, int idx, graph_t *g) {
    v_node_t *v = g->v_node_list[idx];
    if (v == NULL) {
        perror("vex is NULL!");
        return 1;
    }
    if (v->first_e_node == NULL) {
        v->first_e_node = e;
        v->last_e_node = e;
    } else {
        v->last_e_node->next = e;
        v->last_e_node = e;
    }
    g->e_node_num ++;
    return 0;
}

int graph_print(graph_t *g) {
    int j, i = 0;
    v_node_t *v;
    while (NULL != (v = g->v_node_list[i])) {
        e_node_t *e = v->first_e_node;
        j = 0;
        while (e != NULL) {
            printf("顶点[%d]%s的第%d个邻接点:[%d]%s\n", i+1, v->name,  j+1, e->idx+1, g->v_node_list[e->idx]->name);
            e = e->next;
            j ++;
        }
        i ++;
    }
    return 0;
}

int graph_get_shortest_path(char *vname, htable_t *htable, graph_t *g) {

    htable_node_t *node = htable_get(vname, htable);
    int idx = node->val;

    queue_t *q = queue_init();
    g->v_node_list[idx]->known = 1;
    g->v_node_list[idx]->dist = 0;
    enqueue(ele_init(idx), q);

    ele_t *ele;

    while (NULL != (ele = dequeue(q))) {
        v_node_t *v = g->v_node_list[ele->val];
        e_node_t *e = v->first_e_node;
        while (e != NULL) {
            if (g->v_node_list[e->idx]->known == 0) {
                g->v_node_list[e->idx]->known = 1;
                g->v_node_list[e->idx]->dist = v->dist + 1;
                enqueue(ele_init(e->idx), q);
                printf("点[%d]%s距离点[%d]%s:%d\n", e->idx, g->v_node_list[e->idx]->name, idx, vname, g->v_node_list[e->idx]->dist);
            }
            e = e->next;
        }
    }
    return 0;
}

声明散列表的结构和相关操作方法:

#define HASH_TABLE_SIZE 512
typedef struct htable_node_s {
    unsigned int hash;
    char key[MAX_NAME_SIZE];
    int val;
    struct htable_node_s *next;
} htable_node_t;

typedef struct htable_s {
    htable_node_t *table[HASH_TABLE_SIZE];
    int count;
} htable_t;

unsigned int BKDRHash(char *str);    //BKDRHash
htable_node_t *htable_node_init(unsigned int hash, char *key, int val);
htable_t *htable_init();
htable_node_t *htable_get(char *key, htable_t *htable);
int htable_set(char *key, int val, htable_t *htable);

具体方法定义实现:

// BKDR Hash Function
unsigned int BKDRHash(char *str) {
    unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
    unsigned int hash = 0;

    while (*str)
    {
        hash = hash * seed + (*str++);
    }

    return (hash & 0x7FFFFFFF);
}

htable_node_t *htable_node_init(unsigned int hash, char *key, int val) {
    htable_node_t *node = malloc(sizeof(htable_node_t));
    node->hash = hash;
    snprintf(node->key, MAX_NAME_SIZE, "%s", key);
    node->val = val;
    node->next = NULL;
    return node;
}

htable_t *htable_init() {
    htable_t *h = malloc(sizeof(htable_t));
    h->count = 0;
    return h;
}

int htable_set(char *key, int val, htable_t *htable) {
    unsigned int hash = BKDRHash(key);
    int idx = hash % HASH_TABLE_SIZE;
    htable_node_t *prev;
    htable_node_t *node = htable->table[idx];
    if (node == NULL) {
        htable->table[idx] = htable_node_init(hash, key, val);
        htable->count ++;
    } else {
         while (strcmp(node->key, key) != 0) {
            if (node->next != NULL) {
                node = node->next;
            } else {// scan to the end
                node->next = htable_node_init(hash, key, val);
                htable->count ++;
                break;
            }
        }
    }
    return 0;
}

htable_node_t *htable_get(char *key, htable_t *htable) {
    unsigned int hash = BKDRHash(key);
    int idx = hash % HASH_TABLE_SIZE;
    htable_node_t *node = htable->table[idx];
    while (node != NULL && strcmp(node->key, key) != 0) {
        node = node->next;
    }
    return node;
}

声明队列的数据结构和相关操作方法:

typedef struct ele_s {
    int val;
    struct ele_s *next;
} ele_t;

typedef struct queue_s {
    ele_t *head;
    ele_t *tail;
    int count;
} queue_t;

ele_t *ele_init(int val);
queue_t *queue_init();
ele_t *dequeue(queue_t *q);
int enqueue(ele_t *e, queue_t *q);

具体方法的定义实现:

ele_t *ele_init(int val) {
    ele_t *e = malloc(sizeof(ele_t));
    e->val = val;
    e->next = NULL;
    return e;
}

queue_t *queue_init() {
    queue_t *q = malloc(sizeof(queue_t));
    q->head = q->tail = NULL;
    q->count = 0;
    return q;
}

int enqueue(ele_t *e, queue_t *q) {
    if (q->tail == NULL) {
        q->head = e;
        q->tail = e;
        q->count = 1;
    } else {
        q->tail->next = e;
        q->tail = e;
        q->count ++;
    }
    return 0;
}

ele_t *dequeue(queue_t *q) {
    if (q->count <= 0) {
        return NULL;
    }
    ele_t *e = q->head;
    q->head = q->head->next;
    q->count --;
    if (q->count == 0) {
        q->tail = NULL;
    }
    return e;
}

编译执行:

➜  exercises git:(master) ✗ gcc graph.c
➜  exercises git:(master) ✗ ./a.out
输入图的顶点数量:
7
输入第1个顶点名字:
v1
第1个顶点[0]v1已生成
输入第1个顶点[0]v1的邻接点数量:
2
输入第1个顶点[0]v1的第1个邻接点名字:
v2
第1个顶点[0]v1的第1个邻接点v2已生成
输入第1个顶点[0]v1的第2个邻接点名字:
v4
第1个顶点[0]v1的第2个邻接点v4已生成
输入第2个顶点名字:
v2
第2个顶点v2已存在,槽位:1
输入第2个顶点[1]v2的邻接点数量:
v4
输入第2个顶点[1]v2的第1个邻接点名字:
第2个顶点[1]v2的第1个邻接点v4已生成
输入第2个顶点[1]v2的第2个邻接点名字:
v5
第2个顶点[1]v2的第2个邻接点v5已生成
输入第3个顶点名字:
v3
第3个顶点[4]v3已生成
输入第3个顶点[4]v3的邻接点数量:
2
输入第3个顶点[4]v3的第1个邻接点名字:
v1
第3个顶点[4]v3的第1个邻接点v1已生成
输入第3个顶点[4]v3的第2个邻接点名字:
v6
第3个顶点[4]v3的第2个邻接点v6已生成
输入第4个顶点名字:
v4
第4个顶点v4已存在,槽位:2
输入第4个顶点[2]v4的邻接点数量:
4
输入第4个顶点[2]v4的第1个邻接点名字:
v3
第4个顶点[2]v4的第1个邻接点v3已生成
输入第4个顶点[2]v4的第2个邻接点名字:
v5
第4个顶点[2]v4的第2个邻接点v5已生成
输入第4个顶点[2]v4的第3个邻接点名字:
v6
第4个顶点[2]v4的第3个邻接点v6已生成
输入第4个顶点[2]v4的第4个邻接点名字:
v7
第4个顶点[2]v4的第4个邻接点v7已生成
输入第5个顶点名字:
v5
第5个顶点v5已存在,槽位:3
输入第5个顶点[3]v5的邻接点数量:
1
输入第5个顶点[3]v5的第1个邻接点名字:
v7
第5个顶点[3]v5的第1个邻接点v7已生成
输入第6个顶点名字:
v6
第6个顶点v6已存在,槽位:5
输入第6个顶点[5]v6的邻接点数量:
0
输入第7个顶点名字:
v7
第7个顶点v7已存在,槽位:6
输入第7个顶点[6]v7的邻接点数量:
1
输入第7个顶点[6]v7的第1个邻接点名字:
v6
第7个顶点[6]v7的第1个邻接点v6已生成
图生成完毕!
开始打印图——
顶点[1]v1的第1个邻接点:[2]v2
顶点[1]v1的第2个邻接点:[3]v4
顶点[2]v2的第1个邻接点:[3]v4
顶点[2]v2的第2个邻接点:[4]v5
顶点[3]v4的第1个邻接点:[5]v3
顶点[3]v4的第2个邻接点:[4]v5
顶点[3]v4的第3个邻接点:[6]v6
顶点[3]v4的第4个邻接点:[7]v7
顶点[4]v5的第1个邻接点:[7]v7
顶点[5]v3的第1个邻接点:[1]v1
顶点[5]v3的第2个邻接点:[6]v6
顶点[7]v7的第1个邻接点:[6]v6
准备计算最短路径,请输入起始点:
v3
开始计算最短路径——
点[0]v1距离点[4]v3:1
点[5]v6距离点[4]v3:1
点[1]v2距离点[4]v3:2
点[2]v4距离点[4]v3:2
点[3]v5距离点[4]v3:3
点[6]v7距离点[4]v3:3

源码:https://github.com/0x5446/exe...

参考文献

  1. (美)Mark Allen Weiss 著 冯舜玺 译. 数据结构与算法分析. 机械工业出版社. 2004 各种字符串Hash函数比较

  2. 各种字符串Hash函数比较 - BYVoid

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值