C 图 邻接表的基本操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010095372/article/details/83546216

基本思路

强烈推荐大家看看,同胞写的,讲的特别清楚
图 – 我的理解就是若干个节点,再加上他们之间的联系;这样就是两块内容,一个放节点,一个放他们的关系。
最直观就是弄一个二维数组,行列为1的就是连着的,为0就是没有。

但是太占空间了,于是就有了这样的,前面一列就是所有顶点排列,后面每个顶点跟着的就是和这个顶点连着的节点。
在这里插入图片描述
根据这个理解,就可以把邻接表弄的七七八八了

存储结构

#define M 100
#define true 1
#define false 0

typedef char Element;
typedef int BOOL;
//邻接表的节点
typedef struct Node{
    int adj_vex_index;  //弧头的下标,也就是被指向的下标
    Element data;       //权重值
    struct Node * next; //边指针
}EdgeNode;

//顶点节点表
typedef struct vNode{
    Element date;          //顶点的权值
    EdgeNode * firstedge;  //顶点下一个是谁?
}VertexNode, Adjlist[M];

//总图的一些信息
typedef struct Graph{
    Adjlist adjlist;       //顶点表
    int arc_num;           //边的个数
    int node_num;          //节点个数
    BOOL is_directed;      //是不是有向图
}Graph, *GraphLink;

创建

void creatGraph(GraphLink *g){
    int i, j, k;
    EdgeNode *p;
    
    printf("输入顶点数目,边数和有向?:\n");
    scanf("%d %d %d", &(*g)->node_num, &(*g)->arc_num, &(*g)->is_directed);
    
    //安排一下顶点数组,有几个走几个,安排的明明白白
    printf("输入顶点信息:\n");
    for (i = 0; i < (*g)->node_num; i++) {
        getchar();
        scanf("%c", &(*g)->adjlist[i].date);
        (*g)->adjlist[i].firstedge = NULL;
    }
    printf("输入边信息:\n");
    //再安排下边,想来想去,还是教程给的最简便,就是我知道边的两端是谁就好了
    for (k = 0; k < (*g)->arc_num; k++){
        //第一个输入的是弧尾下标,第二个是弧头下标
        // i ----> j
        getchar();
        scanf("%d %d", &i, &j);
        
        //新建一个节点是必须的
        p = (EdgeNode *)malloc(sizeof(EdgeNode));
        //弧头的下标
        p->adj_vex_index = j;
        //头插法插进去,插的时候要找到弧尾,那就是顶点数组的下标i
        p->next = (*g)->adjlist[i].firstedge;
        (*g)->adjlist[i].firstedge = p;
        
        //如果无向的话得额外在加这一句,毕竟是实对称矩阵
        if(!(*g)->is_directed)
        {
            // j -----> i
            p = (EdgeNode *)malloc(sizeof(EdgeNode));
            p->adj_vex_index = i;
            p->next = (*g)->adjlist[j].firstedge;
            (*g)->adjlist[j].firstedge = p;
        }
        printf("\n");
    }
}

打印

void putGragh(GraphLink g){
    int i;
    //遍历一遍顶点坐标,每个再进去走一次
    for (i = 0; i < g->node_num; i++) {
        EdgeNode * p = g->adjlist[i].firstedge;
        while (p) {
            printf("%c->%c ", g->adjlist[i].date, g->adjlist[p->adj_vex_index].date);
            p = p->next;
        }
        printf("\n");
    }
}

广度遍历

//广度遍历
void BFSTraverse(Graph *g)
{
    int i;
    int tmp;
    EdgeNode *p;
    //初始化队列
    LinkQueue q;
    initQueue(&q);
    //防一手不连通的图
    for (i = 0; i < g->node_num; i++) {
        if(!visited[i]){
            visited[i] = true;
            printf("%c ",g->adjlist[i].date);
            
            //队列插进来一个节点
            InsertQueue(&q, i);
            //队列不是空的时候就从队列里面操作
            while (!isEmpty(&q)) {
                //从队列取一个就得把和他连着的点都填进去
                DeleteQueue(&q, &tmp);
                //添加和他连着的所有未点亮的点
                p = g->adjlist[tmp].firstedge;
                while (p) {
                    if (!visited[p->adj_vex_index]) {
                        visited[p->adj_vex_index] = true;
                        printf("%c ",g->adjlist[p->adj_vex_index].date);
                        InsertQueue(&q, p->adj_vex_index);
                    }
                    p = p->next;
                }
            }
        }
    }
}

深度遍历

//得有个数组记录一下我走过的点点
int visited[M];
//经典算法
/*
a| a->b a->d a->c 
b| b->c b->g b->a 
c| c->b c->a 
d| d->a 
e| e->f 
f| f->e 
g| g->b 

从顶点数组里找,找第一个a,然后记录了a已经点亮了
接着从a连着的点里往下找,找到了b,b也被点亮
然后再从b里往下找,找到c,c点亮
从c往下找,找到b,判断并pass,又找到a并判断,c连接的点已经都找过了
再回到b,往下找找到g...

就这样以此类推
用递归很巧妙的解决了这个问题,递归真屌!!!
*/

void DFS(Graph *g, int i){
    visited[i] = true;
    printf("%c ",g->adjlist[i].date);
    EdgeNode *p = g->adjlist[i].firstedge;
    while (p) {
        if(visited[p->adj_vex_index] == 0){
            DFS(g, p->adj_vex_index);
        }
        p = p->next;
    }
}
//防一手不连通的图
void DFSTraverse(Graph *g)
{
    int i;
    for (i = 0; i < g->node_num; i++)
    {
        if (!visited[i])
            DFS(g, i);
    }
}

完整代码

//
//  main.c
//  图邻接表
//
//  Created by 赫凯 on 2018/10/28.
//  Copyright © 2018 赫凯. All rights reserved.
//

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

#define M 10
#define true 1
#define false 0

////////////
typedef int Elemtype;
typedef struct QNode{
    Elemtype data;
    struct QNode *next;
}QNode, *QueuePrt;

typedef struct {
    QueuePrt front, rear;
}LinkQueue;
//初始化
void initQueue(LinkQueue *q){
    q->front = q->rear = (QueuePrt)malloc(sizeof(QNode));
    if(!q->front)
        exit(0);
    q->front->next = NULL;
}
//插入一个节点
void InsertQueue(LinkQueue *q, Elemtype e){
    QueuePrt p;
    p = (QueuePrt)malloc(sizeof(QNode));
    if(p == NULL)
        exit(0);
    p->data = e;
    p->next = NULL;
    
    //插进去
    q->rear->next = p;
    q->rear = p;
}
//出队列
void DeleteQueue(LinkQueue *q, Elemtype *e){
    QueuePrt p;
    if( q->front == q->rear ){
        return;
    }
    
    p = q->front->next;
    *e = p->data;
    
    q->front->next = p->next;
    if(q->rear == p)
        q->rear = q->front;
    free(p);
}
//销毁一个队列
void DestroyQueue(LinkQueue *q){
    while (q->front) {
        q->rear = q->front->next;
        free(q->front);
        q->front = q->rear;
    }
}
//队列是否为空
int isEmpty(LinkQueue* a)
{
    if(a->front == a->rear)
        return 1;
    return 0;
}

////////////
typedef char Element;
typedef int BOOL;
//邻接表的节点
typedef struct Node{
    int adj_vex_index;  //弧头的下标,也就是被指向的下标
    Element data;       //权重值
    struct Node * next; //边指针
}EdgeNode;

//顶点节点表
typedef struct vNode{
    Element date;          //顶点的权值
    EdgeNode * firstedge;  //顶点下一个是谁?
}VertexNode, Adjlist[M];

//总图的一些信息
typedef struct Graph{
    Adjlist adjlist;       //顶点表
    int arc_num;           //边的个数
    int node_num;          //节点个数
    BOOL is_directed;      //是不是有向图
}Graph, *GraphLink;

void creatGraph(GraphLink *g){
    int i, j, k;
    EdgeNode *p;
    
    printf("输入顶点数目,边数和有向?:\n");
    scanf("%d %d %d", &(*g)->node_num, &(*g)->arc_num, &(*g)->is_directed);
    
    //安排一下顶点数组,有几个走几个,安排的明明白白
    printf("输入顶点信息:\n");
    for (i = 0; i < (*g)->node_num; i++) {
        getchar();
        scanf("%c", &(*g)->adjlist[i].date);
        (*g)->adjlist[i].firstedge = NULL;
    }
    printf("输入边信息:\n");
    //再安排下边,想来想去,还是教程给的最简便,就是我知道边的两端是谁就好了
    for (k = 0; k < (*g)->arc_num; k++){
        //第一个输入的是弧尾下标,第二个是弧头下标
        // i ----> j
        getchar();
        scanf("%d %d", &i, &j);
        
        //新建一个节点是必须的
        p = (EdgeNode *)malloc(sizeof(EdgeNode));
        //弧头的下标
        p->adj_vex_index = j;
        //头插法插进去,插的时候要找到弧尾,那就是顶点数组的下标i
        p->next = (*g)->adjlist[i].firstedge;
        (*g)->adjlist[i].firstedge = p;
        
        //如果无向的话得额外在加这一句,毕竟是实对称矩阵
        if(!(*g)->is_directed)
        {
            // j -----> i
            p = (EdgeNode *)malloc(sizeof(EdgeNode));
            p->adj_vex_index = i;
            p->next = (*g)->adjlist[j].firstedge;
            (*g)->adjlist[j].firstedge = p;
        }

    }
}

void putGragh(GraphLink g){
    int i;
    //遍历一遍顶点坐标,每个再进去走一次
    for (i = 0; i < g->node_num; i++) {
        EdgeNode * p = g->adjlist[i].firstedge;
        while (p) {
            printf("%c->%c ", g->adjlist[i].date, g->adjlist[p->adj_vex_index].date);
            p = p->next;
        }
        printf("\n");
    }
}

int visited[M];
//深度遍历
void DFS(Graph *g, int i){
    visited[i] = true;
    printf("%c ",g->adjlist[i].date);
    EdgeNode *p = g->adjlist[i].firstedge;
    while (p) {
        if(visited[p->adj_vex_index] == 0){
            DFS(g, p->adj_vex_index);
        }
        p = p->next;
    }
}

void DFSTraverse(Graph *g)
{
    int i;
    for (i = 0; i < g->node_num; i++)
    {
        if (!visited[i])
            DFS(g, i);
    }
}

//广度遍历
void BFSTraverse(Graph *g)
{
    int i;
    int tmp;
    EdgeNode *p;
    //初始化队列
    LinkQueue q;
    initQueue(&q);
    
    for (i = 0; i < g->node_num; i++) {
        if(!visited[i]){
            visited[i] = true;
            printf("%c ",g->adjlist[i].date);
            
            //队列插进来一个节点
            InsertQueue(&q, i);
            //队列不是空的时候就从队列里面操作
            while (!isEmpty(&q)) {
                //从队列取一个就得把和他连着的点都填进去
                DeleteQueue(&q, &tmp);
                //添加和他连着的所有未点亮的点
                p = g->adjlist[tmp].firstedge;
                while (p) {
                    if (!visited[p->adj_vex_index]) {
                        visited[p->adj_vex_index] = true;
                        printf("%c ",g->adjlist[p->adj_vex_index].date);
                        InsertQueue(&q, p->adj_vex_index);
                    }
                    p = p->next;
                }
            }
        }
    }

}

int main(int argc, const char * argv[]) {
    // insert code here...
    
    GraphLink g = (Graph *)malloc(sizeof(Graph));
    //数目:7 6 0
    //顶点信息:a b c d e f g
    //边的信息:0 2 0 3 0 1 4 5 1 6 1 2
    
    creatGraph(&g);
    putGragh(g);
    int i;
    for (i = 0; i < M; i++) {
        visited[i] = false;
    }
    printf("深度优先遍历:");
    DFSTraverse(g);

    for (i = 0; i < M; i++) {
        visited[i] = false;
    }
    printf("广度优先遍历:");
    BFSTraverse(g);
    printf("\nHello, World!\n");
    return 0;
}


//输入顶点数目,边数和有向?:
//7 6 0
//输入顶点信息:
//a b c d e f g
//输入边信息:
//0 2 0 3 0 1 4 5 1 6 1 2
//a->b a->d a->c
//b->c b->g b->a
//c->b c->a
//d->a
//e->f
//f->e
//g->b
//深度优先遍历:a b c g d e f 广度优先遍历:a b d c g e f 
// Hello, World!

猜你喜欢

转载自blog.csdn.net/u010095372/article/details/83546216