图:图(Graph)是由定点的有穷非空集合和定点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中的顶点的集合,E是图G中边的集合。

图的两个重要的存储方式:

1、 邻接矩阵

/**************图数据结构***********************/

#define VERTEX_MAX 26   //图的最大顶点数  

#define MAXVALUE 32767 //最大值(可设为一个最大整数)

typedef struct

{

    char Vertex[VERTEX_MAX]; //保存顶点信息(序号或字母)                                                                                                 

    int Edges[VERTEX_MAX][VERTEX_MAX]; //保存边的权

    int isTrav[VERTEX_MAX]; //遍历标志

    int VertexNum; //顶点数量

    int EdgeNum;//边数量

    int GraphType; //图的类型(0:无向图,1:有向图)   

}MatrixGraph; //定义邻接矩阵图结构

 

void CreateMatrixGraph(MatrixGraph *G);//创建邻接矩阵图

void OutMatrix(MatrixGraph *G); //输出邻接矩阵

 

/***********创建邻接矩阵存储方式的图****************/

void CreateMatrixGraph(MatrixGraph *G)//创建邻接矩阵图

{

    int i,j,k,weight;

    char start,end; //边的起始顶点

    printf("输入各顶点信息\n");

    for(i=0;i<G->VertexNum;i++) //输入顶点

    {

        getchar();

        printf("第%d个顶点:",i+1);

        scanf("%c",&(G->Vertex[i])); //保存到各顶点数组元素中

    }

    printf("输入构成各边的两个顶点及权值(用逗号分隔):\n");

    for(k=0;k<G->EdgeNum;k++)  //输入边的信息

    {

        getchar(); //暂停输入

        printf("第%d条边:",k+1);

        scanf("%c,%c,%d",&start,&end,&weight);

        for(i=0;start!=G->Vertex[i];i++); //在已有顶点中查找始点

        for(j=0;end!=G->Vertex[j];j++); //在已有顶点中查找结终点

        G->Edges[i][j]=weight; //对应位置保存权值,表示有一条边

        if(G->GraphType==0)  //若是无向图

            G->Edges[j][i]=weight;//在对角位置保存权值 

    }

}

/***********输出邻接矩阵存储方式的图****************/

void OutMatrix(MatrixGraph *G)//输出邻接矩阵

{

    int i,j;

    for(j=0;j<G->VertexNum;j++)

        printf("\t%c",G->Vertex[j]);          //在第1行输出顶点信息

    printf("\n");

    for(i=0;i<G->VertexNum;i++)

    {

        printf("%c",G->Vertex[i]);

        for(j=0;j<G->VertexNum;j++)

        {

            if(G->Edges[i][j]==MAXVALUE) //若权值为最大值

                printf("\t∞");          //输出无穷大符号

            else

                printf("\t%d",G->Edges[i][j]); //输出边的权值

        }

        printf("\n");

    }            

}

 

/*******************测试代码*******************************/

int main()

{

    MatrixGraph G; //定义保存邻接矩阵结构的图

    int i,j;

    printf("输入生成图的类型(0:无向图,1:有向图):");

    scanf("%d",&G.GraphType); //图的种类

    printf("输入图的顶点数量和边数量:");

    scanf("%d,%d",&G.VertexNum,&G.EdgeNum); //输入图顶点数和边数

    for(i=0;i<G.VertexNum;i++)  //清空矩阵

        for(j=0;j<G.VertexNum;j++)

            G.Edges[i][j]=MAXVALUE; //设置矩阵中各元素的值为最大值        

    CreateMatrixGraph(&G); //创建用邻接表保存的图

    printf("邻接矩阵数据如下:\n");

    OutMatrix(&G);

    getch();

    return 0;

}

 

2、 邻接表存储方式:

/************************图邻接表存储结构****************/

#define VERTEX_MAX 20   //图的最大顶点数  

typedef struct edgeNode  

{

    int Vertex; //顶点信息(序号或字母)

    int weight; //权值

    struct edgeNode *next; //指向下一个顶点指针 (当前顶点和指向的下一顶点构成一条边)

}EdgeNode; //邻接表边结构

 

typedef struct  

{

    EdgeNode* AdjList[VERTEX_MAX]; //指向每个顶点的指针

    int VextexNum,EdgeNum; //图的顶点的数量和边的数量                                                                                                                 

    int GraphType; //图的类型(0:无向图,1:有向图)

}ListGraph;  //图的结构

 

void CreateGraph(ListGraph *G); //生成图的邻接表  

void OutList(ListGraph *G); //输出邻接表

 

/************************创建图邻接表存储结构****************/

void CreateGraph(ListGraph *G)  //构造邻接表结构图

{

    int i,weight;

    int start,end;

    EdgeNode *s;

    for(i=1;i<=G->VextexNum;i++)//将图中各顶点指针清空

        G->AdjList[i]=NULL;

    for(i=1;i<=G->EdgeNum;i++) //输入各边的两个顶点

    {

        getchar();

        printf("第%d条边:",i);

        scanf("%d,%d,%d",&start,&end,&weight); //输入边的起点和终点

        s=(EdgeNode *)malloc(sizeof(EdgeNode)); //申请保存一个顶点的内存

        s->next=G->AdjList[start]; //插入到邻接表中

        s->Vertex=end; //保存终点编号

        s->weight=weight; //保存权值

        G->AdjList[start]=s; //邻接表对应顶点指向该点

        if(G->GraphType==0) //若是无向图,再插入到终点的边链中

        {

            s=(EdgeNode *)malloc(sizeof(EdgeNode)); //申请保存一个顶点的内存

            s->next=G->AdjList[end];

            s->Vertex=start;

            s->weight=weight;

            G->AdjList[end]=s;

        }

    }  

}

/************************输出图邻接表存储结构****************/

void OutList(ListGraph *G)

{

    int i;

    EdgeNode *s;

    for(i=1;i<=G->VextexNum;i++)

    {

        printf("顶点%d",i);

        s=G->AdjList[i];

        while(s)

        {

            printf("->%d(%d)",s->Vertex,s->weight);

            s=s->next;

        }

        printf("\n");

    }

}

 

/*****************测试代码**************************/

int main()

{

    ListGraph G; //定义保存邻接表结构的图

    printf("输入生成图的类型(0:无向图,1:有向图):");

    scanf("%d",&G.GraphType); //图的种类

    printf("输入图的顶点数量和边数量:");

    scanf("%d,%d",&G.VextexNum,&G.EdgeNum); //输入图顶点数和边数

    printf("输入构成各边的两个顶点及权值(用逗号分隔):\n");

    CreateGraph(&G); //生成邻接表结构的图

    printf("输出图的邻接表:\n");

    OutList(&G);

    getch();

    return 0;

}

 

深度优先遍历

 深度优先遍历方法类似于树的先序遍历,具体过程如下:

(1)从isTrav数组中选择一个未被访的顶点Vi,将其标记为已访问。

(2)接着从Vi的一个未被访问过的邻接点出发进行深度优先遍历。

(3)重复步骤(2),直至图中所有和Vi有路径相通的顶点都被访问过。

(4)重复步骤(1)至步骤(3)的操作,直至所有顶点都被访问过。

广度优先遍历

                  广度优先遍历类似于树的按层次遍历,具体过程如下:

(1)从isTrav数组中选择一个未被访问的顶点Vi,将其标记为已访问。

(2)接着依次访问Vi的所有未被访问的邻接点,并标记为已被访问过。

(3)从这些邻接点出发进行广度优先遍历,直至图中所有和Vi有路径相通的顶点都被访问过。

(4)重复步骤(1)至步骤(3)的操作,直至所有顶点都被访问过。

 

/*****************本程序是以邻接矩阵存储方式的图的*********************/

#include <stdio.h>

#include "3-6 AdjMatrixGraph.c"

#define QUEUE_MAXSIZE 30 //队列的最大容量

typedef struct

{

    int Data[QUEUE_MAXSIZE]; //数据域

    int head; //队头指针

    int tail; //队尾指针

}SeqQueue; //队列结构

//队列操作函数

void QueueInit(SeqQueue *q); //初始化一个队列  

int QueueIsEmpty(SeqQueue q); //判断队列是否空  

int QueueIn(SeqQueue *q,int n); //将一个元素入队列  

int QueueOut(SeqQueue *q,int *ch); //将一个元素出队列                                                                                                         

 

//图操作函数

void DFSTraverse(MatrixGraph *G); //深度优先遍历

void BFSTraverse(MatrixGraph *G); //广度优先遍历

void DFSM(MatrixGraph *G,int i);

void BFSM(MatrixGraph *G,int i);

 

void QueueInit(SeqQueue *Q)    //队列初始化 

{

    Q->head=Q->tail=0;

}

int QueueIsEmpty(SeqQueue Q)   //判断队列是否已空,若空返回1,否则返回0

{

    return Q.head==Q.tail;

}

int QueueIn(SeqQueue *Q,int ch)   //入队列,成功返回1,失败返回0  

{

    if((Q->tail+1) % QUEUE_MAXSIZE ==Q->head) //若队列已满

        return 0;  //返回错误;

    Q->Data[Q->tail]=ch; //将数据ch入队列

    Q->tail=(Q->tail+1) % QUEUE_MAXSIZE; //调整队尾指针

    return 1; //成功,返回1

}

int QueueOut(SeqQueue *Q,int *ch)   //出队列,成功返回1,并用ch返回该元素值,失败返回0 

{

    if(Q->head==Q->tail) //若队列为空

        return 0; //返回错误

    *ch=Q->Data[Q->head]; //返回队首元素

    Q->head=(Q->head+1) % QUEUE_MAXSIZE; //调整队首指针

    return 1; //成功出队列,返回1  

}  

 

/*********************深度优先遍历**************************/

void DFSTraverse(MatrixGraph *G) //深度优先遍历

{

    int i;

    for(i=0;i<G->VertexNum;i++) //清除各顶点遍历标志

        G->isTrav[i]=0;

    printf("深度优先遍历结点:");

    for(i=0;i<G->VertexNum;i++)

        if(!G->isTrav[i]) //若该点未遍历

            DFSM(G,i); //调用函数遍历

    printf("\n");

 

}

void DFSM(MatrixGraph *G,int i) //从第i个结点开始,深度遍历图

{

    int j;

    G->isTrav[i]=1; //标记该顶点已处理过

    printf("->%c",G->Vertex[i]);//输出结点数据

//    printf("%d->",i); //输出结点序号

 

    //添加处理节点的操作

    for(j=0;j<G->VertexNum;j++)

        if(G->Edges[i][j]!=MAXVALUE && !G->isTrav[i])

            DFSM(G,j); //递归进行遍历

}

 

/*********************广度优先遍历**************************/

void BFSTraverse(MatrixGraph *G) //广度优先

{

    int i;

    for (i=0;i<G->VertexNum;i++) //清除各顶点遍历标志

        G->isTrav[i]=0;

    printf("广度优先遍历结点:");

    for (i=0;i<G->VertexNum;i++)

        if (!G->isTrav[i])

            BFSM(G,i);

    printf("\n");

}

void BFSM(MatrixGraph *G,int k) //广度优先遍历

{

    int i,j;

    SeqQueue Q; //创建循环队列

    QueueInit(&Q); //初始化循环队列

 

    G->isTrav[k]=1; //标记该顶点

    printf("->%c",G->Vertex[k]);  //输出第一个顶点

 

    //添加处理节点的操作

    QueueIn(&Q,k); //入队列

    while (!QueueIsEmpty(Q)) //队列不为空

    {

        QueueOut(&Q,&i); //出队列

        for (j=0;j<G->VertexNum;j++)

            if(G->Edges[i][j]!=MAXVALUE && !G->isTrav[j])

            {

                printf("->%c",G->Vertex[j]);

                G->isTrav[j]=1;  //标记该顶点

                //处理顶点

                QueueIn(&Q,j); //出队列

            }

    }

}

 

/*********************测试代码**************************/

int main()

{

    MatrixGraph G; //定义保存邻接表结构的图

    int path[VERTEX_MAX];

    int i,j,s,t;

    char select;

    do

    {

        printf("输入生成图的类型(0:无向图,1:有向图):");

        scanf("%d",&G.GraphType); //图的种类

        printf("输入图的顶点数量和边数量:");

        scanf("%d,%d",&G.VertexNum,&G.EdgeNum); //输入图顶点数和边数

        for(i=0;i<G.VertexNum;i++)  //清空矩阵

            for(j=0;j<G.VertexNum;j++)

                G.Edges[i][j]=MAXVALUE; //设置矩阵中各元素的值为0        

        CreateMatrixGraph(&G); //生成邻接表结构的图

        printf("邻接矩阵数据如下:\n");

        OutMatrix(&G); //输出邻接矩阵            

        DFSTraverse(&G); //深度优先搜索遍历图

        BFSTraverse(&G); //广度优先搜索遍历图

        printf("图遍历完毕,继续进行吗?(Y/N)");

        scanf(" %c",&select);

    }while(select!='N' && select!='n');   

    getch();

    return 0;

}

 

求最小生成树:

1、Prim算法

#define USED 0    //已使用,加入U集合

#define NOADJ -1  //非邻接顶点

void Prim(MatrixGraph G)//最小生成树

{

    int i,j,k,min,sum=0;

    int weight[VERTEX_MAX];//权值

    char tmpvertex[VERTEX_MAX];//临时顶点信息

   

    for(i=1;i<G.VertexNum;i++) //保存邻接矩阵中的一行数据                                                                                                      

    {

        weight[i]=G.Edges[0][i]; //权值

        if(weight[i]==MAXVALUE)

            tmpvertex[i]=NOADJ; //非邻接顶点

        else

            tmpvertex[i]=G.Vertex[0]; //邻接顶点

    }

    tmpvertex[0]=USED; //将0号顶点并入U集

    weight[0]=MAXVALUE; //设已使用顶点权值为最大值

    for(i=1;i<G.VertexNum;i++)

    {

        min=weight[0]; //最小权值

        k=i;

        for(j=1;j<G.VertexNum;j++) //查找权值最小的一个邻接边

            if(weight[j]<min && tmpvertex[j]>0) //找到具有更小权值的未使用边

            {

                min=weight[j]; //保存权值

                k=j; //保存邻接点序号

            }

        sum+=min;//累加权值

        printf("(%c,%c),",tmpvertex[k],G.Vertex[k]); //输出生成树一条边

        tmpvertex[k]=USED; //将编号为k的顶点并入U集

        weight[k]=MAXVALUE; //已使用顶点的权值为最大值

        for(j=0;j<G.VertexNum;j++) //重新选择最小边

            if(G.Edges[k][j]<weight[j] && tmpvertex[j]!=0)

            {

                weight[j]=G.Edges[k][j]; //权值

                tmpvertex[j]=G.Vertex[k]; //上一个顶点信息

            }

    }

    printf("\n最小生成树的总权值为:%d\n",sum);

}

 

/********************测试代码************************/

#include "3-6 AdjMatrixGraph.c"

#include "3-12 Prim.c"

int main()

{

    MatrixGraph G; //定义保存邻接表结构的图

    int path[VERTEX_MAX];

    int i,j,s,t;

    char select;

    do

    {

        printf("输入生成图的类型(0:无向图,1:有向图):");

        scanf("%d",&G.GraphType); //图的种类

        printf("输入图的顶点数量和边数量:");

        scanf("%d,%d",&G.VertexNum,&G.EdgeNum); //输入图顶点数和边数

 

        for(i=0;i<G.VertexNum;i++)  //清空矩阵

            for(j=0;j<G.VertexNum;j++)

                G.Edges[i][j]=MAXVALUE; //设置矩阵中各元素的值为0        

        CreateMatrixGraph(&G); //生成邻接表结构的图

        printf("邻接矩阵数据如下:\n");

        OutMatrix(&G);       

 

        printf("最小生成树的边为:\n");

        Prim(G);

 

        printf("继续进行吗?(Y/N)");

        scanf(" %c",&select);

        getchar();

    }while(select!='N' && select!='n');

    getch();

    return 0;

}

 

2、Dijkstra算法

void Dijkstra(MatrixGraph G)

{

    int weight[VERTEX_MAX];//某源点到各顶点的最短路径长度

    int path[VERTEX_MAX];//某源点到终点经过的顶点集合的数组

    int tmpvertex[VERTEX_MAX];//最短路径的终点集合

    int i,j,k,v0,min;

    printf("\n请输入源点的编号:");

    scanf("%d",&v0);

    v0--; //编号自减1(因数组是从0开始)

    for(i=0;i<G.VertexNum;i++) //初始辅助数组

    {

        weight[i]=G.Edges[v0][i]; //保存最小权值

        if(weight[i]<MAXVALUE && weight[i]>0) //有效权值

            path[i]=v0; //保存边

        tmpvertex[i]=0; //初始化顶点集合为空

    }

    tmpvertex[v0]=1; //将顶点v0添加到集合U中

    weight[v0]=0; //将源顶点的权值设为0

    for(i=0;i<G.VertexNum;i++)

    {

        min=MAXVALUE; //将min中保存一个最大值

        k=v0; //源顶点序号

        for(j=0;j<G.VertexNum;j++) //在U集合中查找未用顶点的最小权值                                                                             

            if(tmpvertex[j]==0 && weight[j]<min)

            {

                min=weight[j];

                k=j;

            }

        tmpvertex[k]=1;   //将顶点k加入集合U

        for(j=0;j<G.VertexNum;j++) //以顶点k为中间点,重新计算权值

            if(tmpvertex[j]==0 && weight[k]+G.Edges[k][j]<weight[j]) //有更小权值的路径

            {

                weight[j]=weight[k]+G.Edges[k][j]; //更新权值

                path[j]=k;

            }

    }

    printf("\n顶点%c到各顶点的最短路径为(终点 < 源点):\n",G.Vertex[v0]);

    for(i=0;i<G.VertexNum;i++)//输出结果

    {

        if(tmpvertex[i]==1)

        {

            k=i;           

            while(k!=v0)

            {

                j=k;

                printf("%c < ",G.Vertex[k]);

                k=path[k];

            }

            printf("%c\n",G.Vertex[k]);

        }else

            printf("%c<-%c:无路径\n",G.Vertex[i],G.Vertex[v0]);

    }

}

 

/********************测试代码**********************/

#include <stdio.h>

#include "3-6 AdjMatrixGraph.c"

#include "3-14 Dijkstra.c"

int main()

{

    MatrixGraph G; //定义保存邻接表结构的图

    int path[VERTEX_MAX];

    int i,j,s,t;

    char select;

    do

    {

        printf("输入生成图的类型(0:无向图,1:有向图):");

        scanf("%d",&G.GraphType); //图的种类

        printf("输入图的顶点数量和边数量:");

        scanf("%d,%d",&G.VertexNum,&G.EdgeNum); //输入图顶点数和边数

 

        for(i=0;i<G.VertexNum;i++)  //清空矩阵

            for(j=0;j<G.VertexNum;j++)

                G.Edges[i][j]=MAXVALUE; //设置矩阵中各元素的值为0        

        CreateMatrixGraph(&G); //生成邻接表结构的图

        printf("邻接矩阵数据如下:\n");

        OutMatrix(&G);       

 

        printf("最短路径:\n");

        Dijkstra(G);

 

        printf("继续进行吗?(Y/N)");

        scanf(" %c",&select);

        getchar();

    }while(select!='N' && select!='n');

    getch();

    return 0;

}


/*****************************************Java实现图******************************/

Java实现连接矩阵存储结构的图

package com.jtlyuan.graph;

publicclass MGraph {

    publicstaticintVERTEX_MAX = 25;

    char[] Vertex = newchar[VERTEX_MAX]; // 保存顶点信息(序号或字母)

    int[][] Edges = newint[VERTEX_MAX][VERTEX_MAX]; // 保存边的权                                                                              

    boolean[] isTrav = newboolean[VERTEX_MAX]; // 遍历标志

    intVertexNum; // 顶点数量

    intEdgeNum;// 边数量

    publicchar[] getVertex() {

       returnVertex;

    }

    publicvoid setVertex(char[] vertex) {

       Vertex = vertex;

    }

    publicint[][] getEdges() {

       returnEdges;

    }

    publicvoid setEdges(int[][] edges) {

       Edges = edges;

    }

    publicint getVertexNum() {

       returnVertexNum;

    }

    publicvoid setVertexNum(int vertexNum) {

       VertexNum = vertexNum;

    }

    publicint getEdgeNum() {

       returnEdgeNum;

    }

    publicvoid setEdgeNum(int edgeNum) {

       EdgeNum = edgeNum;

    }

}

 

深度优先遍历和广度优先遍历

package com.jtlyuan.graph;

 

import java.util.LinkedList;

import java.util.Queue;

 

publicclass GraphTraDemo {

    /****深度优先遍历******/

    publicvoid DFSTra(MGraph g){

       for(int i=0;i<g.VertexNum;i++){

           g.isTrav[i]=false;

       }

       for(int i =0 ;i<g.VertexNum;i++){                                                                                                                

           if(!g.isTrav[i]){

              DFS(g,i);

           }

       }

    }

    privatevoid DFS(MGraph g, int i) {

       g.isTrav[i]=true;

       System.out.print(g.Vertex[i]+ " --> ");

       for(int j = 0; j<g.VertexNum;j++){

           if(g.Edges[i][j]!=0&&!g.isTrav[j]){

              DFS(g,j);

           }

       }

    }

    /******广度优先遍历*********/

    publicvoid BFSTra(MGraph g){

       for(int i=0;i<g.VertexNum;i++){

           g.isTrav[i]=false;

       }

       for(int i =0 ;i<g.VertexNum;i++){

           if(!g.isTrav[i]){

              BFS(g,i);

           }

       }

    }

    privatevoid BFS(MGraph g, int i) {

       Queue<Integer> q = new LinkedList<Integer>();

       q.add(i);

       g.isTrav[i]=true;

       System.out.print(g.Vertex[i]+"-->");

 

       while(!q.isEmpty()){

           q.poll();

           for(int j = 0 ;j<g.VertexNum;j++){

              if(g.Edges[i][j]!=0&&!g.isTrav[j]){

                  System.out.print(g.Vertex[j]+"-->");

                  g.isTrav[j]=true;

                  q.add(j);

              }

           }

       }

    }

    publicstaticvoid main(String[] args) {

       MGraph g = new MGraph();

       g.setVertexNum(5);

       g.setEdgeNum(6);

       g.Vertex[0]='A';

       g.Vertex[1]='B';

       g.Vertex[2]='C';

       g.Vertex[3]='D';

       g.Vertex[4]='E';

       g.Edges[0][1]=3;

       g.Edges[0][3]=2;

       g.Edges[1][2]=9;

       g.Edges[2][4]=7;

       g.Edges[3][4]=6;

       g.Edges[4][1]=3;

       System.out.println("深度遍历");

       new GraphTraDemo().DFSTra(g);

       System.out.println("\n广度遍历");

       new GraphTraDemo().BFSTra(g);

    }

}

/*打印了

深度优先遍历

A --> B --> C --> E --> D -->

广度优先遍历

A-->B-->D-->C-->E-->

*/

 


你可能感兴趣的:(数据结构,c,struct,测试,存储,Path)