数据结构之图

  • 图是一种元素关系多对多的数据结构;
  • 图分为有向图和无向图;
  • 图的遍历方法分为深度遍历(DFS)和广度遍历(BFS);
  • 无向图的最小生成树;
  • 有向图的拓扑排序和关键路径;
  • 有权图的单源最短路径和所有顶点的最短路径;

图数据结构 C语言实现

#define true	1
#define false	0
#define OVERFLOW 1

#define MAX_VERTEX_NUM 20 //最大顶点数
#define INFINITY INT_MAX //最大值无穷,在有权图中使用

//图邻接矩阵表示数据结构
typedef enum {DG, DN, UDG, UDN}; //有向图,有向网,无向图,无向网
typedef struct ArcCell {
    int adj;  //VRType adj 无权图用0,1表示是否相连
              //有权图用权值表示
//    InfoType *info; //表示弧相关信息
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];

//typedef struct{
//    int vexs[MAX_VERTEX_NUM];   //顶点向量 VertexType vexs
//    AdjMatrix arcs; //邻接矩阵
//    int vexnum; //顶点数
//    int arcnum; //弧数
    GraphKind kind; //图类型
//}MGraph;

typedef struct{
    int* vexs;   //顶点向量 VertexType vexs
    ArcCell** arcs; //邻接矩阵
    int vexnum; //顶点数
    int arcnum; //弧数
//    GraphKind kind; //图类型
}MGraph;

图基本方法声明和实现 C语言

  • 基本函数声明
//函数声明
void InitGraph(MGraph*);   //初始化数据
void CreateGraph(MGraph*, int*, AdjMatrix**);   //创建图
void PrintGraph(MGraph);    //输出图
void DestoryGraph(MGraph*); //销毁图

int FirstAdjVex(MGraph, int);//返回v的第一个邻接点
int NextAdjVex(MGraph, int, int);
void Visit(int);
  • 基本函数实现
//初始条件
//操作结果 开辟顶点向量数组和邻接矩阵空间和初始化
void InitGraph(MGraph* G){

    printf("%s\n", "please input the number of vertices and arcs in the graph!");
    scanf("%d%d", &G->vexnum, &G->arcnum);

    int i, j;
    //开顶点数组和邻接矩阵空间
    int* vexs = (int*)calloc(G->vexnum, sizeof(int));
    ArcCell** arcs = (ArcCell**)calloc(G->vexnum, sizeof(ArcCell));
    for(i = 0; i < G->vexnum; i++){
        arcs[i] = (ArcCell*)calloc(G->vexnum, sizeof(ArcCell));
    }

    printf("%s\n", "input vertices"); //输入顶点信息 0,1,2,3,4...
    for(i = 0; i < G->vexnum; i++)
        scanf("%d", &vexs[i]);


    //初始化邻接矩阵
    for(i = 0; i < G->vexnum; i++)
        for(j = 0; j < G->vexnum; j++){
            arcs[i][j].adj = 0;
//            arcs[i][j]->adj = INT_MAX; //有权图初始化
        }

    int x, y, adj;
    printf("%s\n", "Input arc triples!");
    for(i = 0; i < G->arcnum; i++){
        scanf("%d%d%d", &x, &y, &adj);
        arcs[x][y].adj = adj;
        arcs[y][x].adj = adj;
    }

    CreateGraph(G, vexs, arcs);
}

//初始条件 vexs图的顶点集, arcs图的弧集
//操作结果 按vexs和arcs构造图G
//函数形参传递是地址,需要使用G->vexs或者(*G).vexs,不能使用G.vexs
void CreateGraph(MGraph* G, int* vexs, AdjMatrix** arcs){

    //地址,就在它后边用->,如果它不是地址,就在它后边就用.
    G->vexs = vexs;
    G->arcs = arcs;
}

//初始条件 图存在
//操作结果 打印图的邻接矩阵
//函数形参将图本身传递进来,非地址,需要使用.而非->
void PrintGraph(MGraph G){

    if(G.vexs != NULL){

        for(int i = 0; i < G.vexnum; i++){
            for(int j = 0; j < G.vexnum; j++){
                printf("%d ", G.arcs[i][j].adj);
            }//for
            printf("\n");
        }//for

        printf("vertices %d  arcs %d\n", G.vexnum, G.arcnum);

    }else{
        printf("%s\n", "graph is not exist!");
    }
}

//初始条件 图存在
//操作结果 销毁图
void DestoryGraph(MGraph* G){

    if(G->vexs != NULL){
        free(G->vexs);
        printf("%s","vexs is free\n");
    }
    if(G->arcs != NULL){
        for(int i = 0; i < G->vexnum; i++){
            free(G->arcs[i]);
        }
        free(G->arcs);
        printf("%s","arcs is free\n");
    }

    free(G);
    printf("%s","G is free\n");
}


//初始条件 图G存在, V是图的顶点
//操作结果 返回V的第一个邻接顶点,若没有返回为-1
int FirstAdjVex(MGraph G, int v){
    for(int i = 0; i < G.vexnum; i++){
        //无权图
        if(G.arcs[v][i].adj == 1)
            return i;
    }
    return -1;
}

//初始条件 图G存在, V是图的顶点, w是v的邻接矩阵
//操作结果 返回V的相对于w下一个邻接顶点,若w是v的最后一个邻接顶点则返回为-1
int NextAdjVex(MGraph G, int v, int w){

    for(int i = w+1; i < G.vexnum; i++){
        //无权图
        if(G.arcs[v][i].adj == 1)
            return i;
    }
    return -1;
}

//初始条件
//操作结果 输出访问结点的信息
void Visit(int v){
    printf("V%d -> ", v);
}

图遍历方法声明和实现 C语言

  • 遍历函数声明
void DFSTraverse(MGraph, int*);
void DFS(MGraph, int, int*); //深度遍历 递归

void DFSTraverse_Stack(MGraph, int*); //深度遍历 栈

void BFSTraverse_queue(MGraph, int*); //广度遍历 队列
  • 遍历函数实现
//初始条件 图存在, 访问标志数组
//操作结果 深度变量图
void DFSTraverse(MGraph G, int* visited){
    for(int v = 0; v < G.vexnum; ++v) visited[v] = false; //初始化visited数组
    for(int v = 0; v < G.vexnum; ++v) //对于连通无向图,不需要此循环,直接从一个顶点出发就能变量所有顶点
        if(!visited[v]) DFS(G, v, visited);
}

//初始条件 图存在, v是图的顶点, 访问标志数组
//深度遍历的递归函数
void DFS(MGraph G, int v, int* visited){
    visited[v] = true; //标记顶点v
    Visit(v); //访问顶点v
    for(int w = FirstAdjVex(G, v); w >= 0; w = NextAdjVex(G, v, w))
        if(!visited[w]) DFS(G, w, visited);
}

//初始条件 图存在, v是图的顶点, 访问标志数组
//深度遍历的非递归函数,用栈
//注:非递归DFS遍历顺序可能和递归DFS遍历不同,是由于栈的特性导致
void DFSTraverse_Stack(MGraph G, int* visited){
    //声明本函数需要使用的外部函数
    extern void InitStack(SqStack*);
    extern int StackEmpty(SqStack);
    extern void Pop(SqStack*, int*);
    extern void Push(SqStack*, int);
    extern void DestoryStack(SqStack*);

    SqStack* S = (SqStack*)malloc(sizeof(SqStack));
    int* v = (int*)calloc(1, sizeof(int));
    int* w = (int*)calloc(1, sizeof(int));
    InitStack(S);
    for(int i = 0; i < G.vexnum; i++) visited[i] = false;

    for(int i = 0; i < G.vexnum&&visited[i] == false; i++){
        //对于连通无向图不需要此循环,对非连通图避免漏顶点遍历

        Push(S, *v); visited[*v] = true; //压入一个顶点栈
        while(!StackEmpty(*S)){  //S是SqStack指针,而*S就是S指向的栈
        Pop(S, v);
        Visit(*v);
        for(*w = FirstAdjVex(G, *v); *w >= 0; *w = NextAdjVex(G, *v, *w)){
            //w是指针,*w是w指针所指向的值
            if(!visited[*w]){
                Push(S, *w);
                visited[*w] = true;
                }//if
            }//for
        }//while
    }//for

    //释放空间
    DestoryStack(S);
    free(v);
    free(w);
}

void BFSTraverse_queue(MGraph G, int* visited){
    //声明本函数需要使用的外部函数
    extern void InitQueue(SqQueue*);
    extern int QueueLength(SqQueue);
    extern void EnQueue(SqQueue*, int);
    extern void DeQueue(SqQueue*, int*);
    extern void DestoryQueue(SqQueue*);
    extern int QueueEmpty(SqQueue);

    SqQueue* Q = (SqQueue*)malloc(sizeof(SqQueue));
    int* v = (int*)calloc(1, sizeof(int));
    int* u = (int*)calloc(1, sizeof(int));
    int* w = (int*)calloc(1, sizeof(int));

    for(int i = 0; i < G.vexnum; i++) visited[i] = false;
    InitQueue(Q);
    for(*v = 0; *v < G.vexnum; ++v){
        if(!visited[*v]){
            visited[*v] = true; Visit(*v);
            EnQueue(Q, *v);
            while(!QueueEmpty(*Q)){
                DeQueue(Q, u);
                for(*w = FirstAdjVex(G, *u); *w >= 0; *w = NextAdjVex(G, *u, *w)){
                    if(!visited[*w]){
                        visited[*w] = true;
                        Visit(*w);
                        EnQueue(Q, *w);
                    } //if
                } //for
            } // while
        }//if
    }//for

    DestoryQueue(Q);
    free(v); free(u); free(w);
}
  • 图操作的例子
    1.主函数
int main(){

    MGraph *G = (MGraph*)malloc(sizeof(MGraph));

    InitGraph(G);
    PrintGraph(*G); //传递图的本身,而非图的地址

    int* visited = (int*)calloc(G->vexnum, sizeof(int));
    printf("DFS递归\n");
    DFSTraverse(*G, visited);
    printf("\n");

    printf("DFS非递归\n");
    DFSTraverse_Stack(*G, visited);
    printf("\n");

    printf("BFS队列\n");
    BFSTraverse_queue(*G, visited);
    printf("\n");

    DestoryGraph(G);
    free(visited);
    return 0;
}

2.控制台输入

5 5 //顶点数和边数
0 1 2 3 4 //顶点信息
0 1 1 //弧三元组
0 2 1
1 3 1
1 4 1
2 3 1

控制台输出
在这里插入图片描述

参照

严蔚敏 吴伟名 米宁.数据结构(C语言版)[M].出版地:北京 出版社:清华大学出版社,出版年∶2018.

注:
图的遍历需要的栈基本方法参考
图的遍历需要的队列基本方法参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值