图的基本概念

图的基本概念

1. 图的定义

定义:图(graph)是由一些点(vertex)和这些点之间的连线(edge)所组成的;其中,点通常被成为"顶点(vertex)",而点与点之间的连线则被成为"边或弧"(edege)。通常记为,G=(V,E)。

2. 图的种类

根据边是否有方向,将图可以划分为:无向图有向图

2.1 无向图

上面的图G0是无向图,无向图的所有的边都是不区分方向的。G0=(V1,{E1})。其中,

(01) V1={A,B,C,D,E,F}。 V1表示由"A,B,C,D,E,F"几个顶点组成的集合。 
(02) E1={(A,B),(A,C),(B,C),(B,E),(B,F),(C,F), (C,D),(E,F),(C,E)}。 E1是由边(A,B),边(A,C)...等等组成的集合。其中,(A,C)表示由顶点A和顶点C连接成的边。

2.2 有向图

上面的图G2是有向图。和无向图不同,有向图的所有的边都是有方向的! G2=(V2,{A2})。其中,

(01) V2={A,C,B,F,D,E,G}。 V2表示由"A,B,C,D,E,F,G"几个顶点组成的集合。 
(02) A2={<A,B>,<B,C>,<B,F>,<B,E>,<C,E>,<E,D>,<D,C>,<E,B>,<F,G>}。 E1是由矢量<A,B>,矢量<B,C>...等等组成的集合。其中,矢量<A,B)表示由"顶点A"指向"顶点C"的有向边。

3. 邻接点和度

3.1 邻接点

一条边上的两个顶点叫做邻接点。 
例如,上面无向图G0中的顶点A和顶点C就是邻接点。

在有向图中,除了邻接点之外;还有"入边"和"出边"的概念。 
顶点的入边,是指以该顶点为终点的边。而顶点的出边,则是指以该顶点为起点的边。 
例如,上面有向图G2中的B和E是邻接点;<B,E>是B的出边,还是E的入边。

3.2 度

在无向图中,某个顶点的度是邻接到该顶点的边(或弧)的数目。 
例如,上面无向图G0中顶点A的度是2。

在有向图中,度还有"入度"和"出度"之分。 
某个顶点的入度,是指以该顶点为终点的边的数目。而顶点的出度,则是指以该顶点为起点的边的数目。 
顶点的度=入度+出度。 
例如,上面有向图G2中,顶点B的入度是2,出度是3;顶点B的度=2+3=5。

4. 路径和回路

路径:如果顶点(Vm)到顶点(Vn)之间存在一个顶点序列。则表示Vm到Vn是一条路径。 
路径长度:路径中"边的数量"。 
简单路径:若一条路径上顶点不重复出现,则是简单路径。 
回路:若路径的第一个顶点和最后一个顶点相同,则是回路。 
简单回路:第一个顶点和最后一个顶点相同,其它各顶点都不重复的回路则是简单回路。

5. 连通图和连通分量

连通图:对无向图而言,任意两个顶点之间都存在一条无向路径,则称该无向图为连通图。 对有向图而言,若图中任意两个顶点之间都存在一条有向路径,则称该有向图为强连通图。

连通分量:非连通图中的各个连通子图称为该图的连通分量。

6. 权

在学习"哈夫曼树"的时候,了解过"权"的概念。图中权的概念与此类似。

上面就是一个带权的图。

图的存储结构

上面了解了"图的基本概念",下面开始介绍图的存储结构。图的存储结构,常用的是"邻接矩阵"和"邻接表"。

1. 邻接矩阵

邻接矩阵是指用矩阵来表示图。它是采用矩阵来描述图中顶点之间的关系(及弧或边的权)。 
假设图中顶点数为n,则邻接矩阵定义为:


下面通过示意图来进行解释。

图中的G1是无向图和它对应的邻接矩阵。

图中的G2是无向图和它对应的邻接矩阵。

通常采用两个数组来实现邻接矩阵:一个一维数组用来保存顶点信息,一个二维数组来用保存边的信息。 
邻接矩阵的缺点就是比较耗费空间。

2. 邻接表

邻接表是图的一种链式存储表示方法。它是改进后的"邻接矩阵",它的缺点是不方便判断两个顶点之间是否有边,但是相对邻接矩阵来说更省空间。

图中的G1是无向图和它对应的邻接矩阵。

图中的G2是无向图和它对应的邻接矩阵。

 

 

邻接表无向图的介绍

邻接表无向图是指通过邻接表表示的无向图。

上面的图G1包含了"A,B,C,D,E,F,G"共7个顶点,而且包含了"(A,C),(A,D),(A,F),(B,C),(C,D),(E,G),(F,G)"共7条边。

上图右边的矩阵是G1在内存中的邻接表示意图。每一个顶点都包含一条链表,该链表记录了"该顶点的邻接点的序号"。例如,第2个顶点(顶点C)包含的链表所包含的节点的数据分别是"0,1,3";而这"0,1,3"分别对应"A,B,D"的序号,"A,B,D"都是C的邻接点。就是通过这种方式记录图的信息的。

邻接表无向图的代码说明

1. 基本定义

复制代码
// 邻接表中表对应的链表的顶点
typedef struct _ENode
{
    int ivex;                   // 该边所指向的顶点的位置
    struct _ENode *next_edge;   // 指向下一条弧的指针
}ENode, *PENode;

// 邻接表中表的顶点
typedef struct _VNode
{
    char data;              // 顶点信息
    ENode *first_edge;      // 指向第一条依附该顶点的弧
}VNode;

// 邻接表
typedef struct _LGraph
{
    int vexnum;             // 图的顶点的数目
    int edgnum;             // 图的边的数目
    VNode vexs[MAX];
}LGraph;
复制代码

(01) LGraph是邻接表对应的结构体。 
vexnum是顶点数,edgnum是边数;vexs则是保存顶点信息的一维数组。

(02) VNode是邻接表顶点对应的结构体。 
data是顶点所包含的数据,而first_edge是该顶点所包含链表的表头指针。

(03) ENode是邻接表顶点所包含的链表的节点对应的结构体。 
ivex是该节点所对应的顶点在vexs中的索引,而next_edge是指向下一个节点的。

2. 创建矩阵

2.1 创建图(用已提供的矩阵)

C语言实现代码:

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>

#define MAX 100

typedef struct graph
{
    char vexs[MAX];
    int vexnum;
    int edgnum;
    int matrix[MAX][MAX];
}Graph,*PGraph;

static int get_position(Graph g,char ch)
{
    int i;
    for(i=0;i<g.vexnum;i++)
        if(g.vexs[i]==ch)
            return i;
    return -1;
}

Graph* create_graph()
{
   char vexs[]={'A','B','C','D','E','F','G'};
   char edges[][2]={{'A','C'},{'A','D'},{'A','F'},{'B','C'},{'C','D'},{'E','G'},{'F','G'}};
   int vlen=sizeof(vexs)/sizeof(vexs[0]);
   int  elen=sizeof(edges)/sizeof(edges[0]);
   int i,p1,p2;
   Graph *pG;
   if((pG=(Graph*)malloc(sizeof(Graph)))==NULL)
        return NULL;
   memset(pG,0,sizeof(Graph));
   pG->vexnum=vlen;
   pG->edgnum=elen;
   for(i=0;i<pG->vexnum;i++)
   {
       pG->vexs[i]=vexs[i];
   }
   for(i=0;i<pG->edgnum;i++)
   {
       p1=get_position(*pG,edges[i][0]);
       p2=get_position(*pG,edges[i][1]);
       pG->matrix[p1][p2]=1;
       pG->matrix[p2][p1]=1;
   }
   return pG;
}

void print_graph(Graph G)
{
    int i,j;
    printf("matrix Graph:\n");
    for(i=0;i<G.vexnum;i++)
    {
        for(j=0;j<G.vexnum;j++)
            printf("%d ",G.matrix[i][j]);
        printf("\n");
    }
}

int main()
{
    Graph *pG;
    pG=create_graph();
    print_graph(*pG);
}
复制代码

运行结果:

 

 

 

邻接表有向图的介绍

邻接表有向图是指通过邻接表表示的有向图。

上面的图G2包含了"A,B,C,D,E,F,G"共7个顶点,而且包含了"<A,B>,<B,C>,<B,E>,<B,F>,<C,E>,<D,C>,<E,B>,<E,D>,<F,G>"共9条边。

上图右边的矩阵是G2在内存中的邻接表示意图。每一个顶点都包含一条链表,该链表记录了"该顶点所对应的出边的另一个顶点的序号"。例如,第1个顶点(顶点B)包含的链表所包含的节点的数据分别是"2,4,5";而这"2,4,5"分别对应"C,E,F"的序号,"C,E,F"都属于B的出边的另一个顶点。

邻接表有向图的代码说明

1. 基本定义

复制代码
// 邻接表中表对应的链表的顶点
typedef struct _ENode
{
    int ivex;                   // 该边所指向的顶点的位置
    struct _ENode *next_edge;   // 指向下一条弧的指针
}ENode, *PENode;

// 邻接表中表的顶点
typedef struct _VNode
{
    char data;              // 顶点信息
    ENode *first_edge;      // 指向第一条依附该顶点的弧
}VNode;

// 邻接表
typedef struct _LGraph
{
    int vexnum;             // 图的顶点的数目
    int edgnum;             // 图的边的数目
    VNode vexs[MAX];
}LGraph;
复制代码

(01) LGraph是邻接表对应的结构体。 vexnum是顶点数,edgnum是边数;vexs则是保存顶点信息的一维数组。 
(02) VNode是邻接表顶点对应的结构体。 data是顶点所包含的数据,而firstedge是该顶点所包含链表的表头指针。 
(03) ENode是邻接表顶点所包含的链表的节点对应的结构体。 ivex是该节点所对应的顶点在vexs中的索引,而next
edge是指向下一个节点的。

2. 创建矩阵

邻接表C实现代码:

复制代码
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#define MAX 100

typedef struct ENode
{
    int ivex;
    struct ENode *next_edge;
}ENode;

typedef struct VNode
{
    char data;
    struct ENode *first_edge;
}VNode;

typedef struct LGraph
{
    int vexnum;
    int edgnum;
    VNode vexs[MAX];
}LGraph;

int get_position(LGraph g,char ch)
{
    int i;
    for(i=0;i<g.vexnum;i++)
        if(ch==g.vexs[i].data)
            return i;
    return -1;
}

LGraph* create_graph()
{
    char vexs[]= {'A','B','C','D','E','F','G'};
    char edges[][2]= {{'A','C'},{'A','D'},{'A','F'},{'B','C'},{'C','D'},{'E','G'},{'F','G'}};
    int vlen=sizeof(vexs)/sizeof(vexs[0]);
    int  elen=sizeof(edges)/sizeof(edges[0]);
    int i,p1,p2;
    ENode *node2;
    LGraph *pG;
    if((pG=(LGraph*)malloc(sizeof(LGraph)))==NULL)
        return NULL;
    memset(pG,0,sizeof(pG));
    pG->edgnum=elen;
    pG->vexnum=vlen;
    for(i=0;i<pG->vexnum;i++)
    {
        pG->vexs[i].data=vexs[i];
        pG->vexs[i].first_edge=NULL;
    }
    for(i=0;i<pG->edgnum;i++)
    {
        p1=get_position(*pG,edges[i][0]);
        p2=get_position(*pG,edges[i][1]);
        node2=(ENode*)malloc(sizeof(ENode));
        node2->ivex=p2;
        node2->next_edge=NULL;
        if(pG->vexs[p1].first_edge==NULL)
            pG->vexs[p1].first_edge=node2;
        else
        {
            ENode *tmp=pG->vexs[p1].first_edge;
            while(tmp->next_edge)
            {
                tmp=tmp->next_edge;
            }
            tmp->next_edge=node2;
        }
    }
    return pG;
}

void print_graph(LGraph G)
{
    int i;
    printf("List Graph:\n");
    ENode *node=NULL;
    for(i=0;i<G.vexnum;i++)
    {
        printf("%d(%c): ", i, G.vexs[i].data);
        node=G.vexs[i].first_edge;
        while(node)
        {
            printf("%d(%c) ", node->ivex, G.vexs[node->ivex].data);
            node=node->next_edge;
        }
        printf("\n");
    }
}

int main()
{
    LGraph *pG;
    pG=create_graph();
    print_graph(*pG);
}
复制代码

运行结果:

 

 

 

邻接矩阵无向图的介绍

邻接矩阵无向图是指通过邻接矩阵表示的无向图。

上面的图G1包含了"A,B,C,D,E,F,G"共7个顶点,而且包含了"(A,C),(A,D),(A,F),(B,C),(C,D),(E,G),(F,G)"共7条边。由于这是无向图,所以边(A,C)和边(C,A)是同一条边;这里列举边时,是按照字母先后顺序列举的。

上图右边的矩阵是G1在内存中的邻接矩阵示意图。A[i][j]=1表示第i个顶点与第j个顶点是邻接点,A[i][j]=0则表示它们不是邻接点;而A[i][j]表示的是第i行第j列的值;例如,A[1,2]=1,表示第1个顶点(即顶点B)和第2个顶点(C)是邻接点。

邻接矩阵无向图的代码说明

1. 基本定义

复制代码
// 邻接矩阵
typedef struct _graph
{
    char vexs[MAX];       // 顶点集合
    int vexnum;           // 顶点数
    int edgnum;           // 边数
    int matrix[MAX][MAX]; // 邻接矩阵
}Graph, *PGraph;
复制代码

Graph是邻接矩阵对应的结构体。 
vexs用于保存顶点,vexnum是顶点数,edgnum是边数;matrix则是用于保存矩阵信息的二维数组。例如,matrix[i][j]=1,则表示"顶点i(即vexs[i])"和"顶点j(即vexs[j])"是邻接点;matrix[i][j]=0,则表示它们不是邻接点。

2. 创建矩阵

这里介绍提供了两个创建矩阵的方法。一个是用已知数据,另一个则需要用户手动输入数据

2.1 创建图(用已提供的矩阵)

复制代码
/*
 * 创建图(用已提供的矩阵)
 */
Graph* create_example_graph()
{
    char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
    char edges[][2] = {
        {'A', 'C'}, 
        {'A', 'D'}, 
        {'A', 'F'}, 
        {'B', 'C'}, 
        {'C', 'D'}, 
        {'E', 'G'}, 
        {'F', 'G'}}; 
    int vlen = LENGTH(vexs);
    int elen = LENGTH(edges);
    int i, p1, p2;
    Graph* pG;

    // 输入"顶点数"和"边数"
    if ((pG=(Graph*)malloc(sizeof(Graph))) == NULL )
        return NULL;
    memset(pG, 0, sizeof(Graph));

    // 初始化"顶点数"和"边数"
    pG->vexnum = vlen;
    pG->edgnum = elen;
    // 初始化"顶点"
    for (i = 0; i < pG->vexnum; i++)
    {
        pG->vexs[i] = vexs[i];
    }

    // 初始化"边"
    for (i = 0; i < pG->edgnum; i++)
    {
        // 读取边的起始顶点和结束顶点
        p1 = get_position(*pG, edges[i][0]);
        p2 = get_position(*pG, edges[i][1]);

        pG->matrix[p1][p2] = 1;
        pG->matrix[p2][p1] = 1;
    }

    return pG;
}
复制代码

createexamplegraph是的作用是创建一个邻接矩阵无向图。

注意:该方法创建的无向图,就是上面图G1。

邻接矩阵无向图的完整源码

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>

#define MAX 100

typedef struct graph
{
    char vexs[MAX];
    int vexnum;
    int edgnum;
    int matrix[MAX][MAX];
}Graph,*PGraph;

static int get_position(Graph g,char ch)
{
    int i;
    for(i=0;i<g.vexnum;i++)
        if(g.vexs[i]==ch)
            return i;
    return -1;
}

Graph* create_graph()
{
   char vexs[]={'A','B','C','D','E','F','G'};
   char edges[][2]={{'A','C'},{'A','D'},{'A','F'},{'B','C'},{'C','D'},{'E','G'},{'F','G'}};
   int vlen=sizeof(vexs)/sizeof(vexs[0]);
   int  elen=sizeof(edges)/sizeof(edges[0]);
   int i,p1,p2;
   Graph *pG;
   if((pG=(Graph*)malloc(sizeof(Graph)))==NULL)
        return NULL;
   memset(pG,0,sizeof(Graph));
   pG->vexnum=vlen;
   pG->edgnum=elen;
   for(i=0;i<pG->vexnum;i++)
   {
       pG->vexs[i]=vexs[i];
   }
   for(i=0;i<pG->edgnum;i++)
   {
       p1=get_position(*pG,edges[i][0]);
       p2=get_position(*pG,edges[i][1]);
       pG->matrix[p1][p2]=1;
       pG->matrix[p2][p1]=1;
   }
   return pG;
}

void print_graph(Graph G)
{
    int i,j;
    printf("matrix Graph:\n");
    for(i=0;i<G.vexnum;i++)
    {
        for(j=0;j<G.vexnum;j++)
            printf("%d ",G.matrix[i][j]);
        printf("\n");
    }
}

int main()
{
    Graph *pG;
    pG=create_graph();
    print_graph(*pG);
}
复制代码

运行结果:

 

 

 

邻接矩阵有向图的介绍

 

邻接矩阵有向图的介绍

邻接矩阵有向图是指通过邻接矩阵表示的有向图。

上面的图G2包含了"A,B,C,D,E,F,G"共7个顶点,而且包含了"<A,B>,<B,C>,<B,E>,<B,F>,<C,E>,<D,C>,<E,B>,<E,D>,<F,G>"共9条边。

上图右边的矩阵是G2在内存中的邻接矩阵示意图。A[i][j]=1表示第i个顶点到第j个顶点是一条边,A[i][j]=0则表示不是一条边;而A[i][j]表示的是第i行第j列的值;例如,A[1,2]=1,表示第1个顶点(即顶点B)到第2个顶点(C)是一条边。

邻接矩阵有向图的代码说明

1. 基本定义

复制代码
// 邻接矩阵
typedef struct _graph
{
    char vexs[MAX];       // 顶点集合
    int vexnum;           // 顶点数
    int edgnum;           // 边数
    int matrix[MAX][MAX]; // 邻接矩阵
}Graph, *PGraph;
复制代码

Graph是邻接矩阵对应的结构体。

vexs用于保存顶点,vexnum是顶点数,edgnum是边数;matrix则是用于保存矩阵信息的二维数组。例如,matrix[i][j]=1,则表示"顶点i(即vexs[i])"和"顶点j(即vexs[j])"是邻接点;matrix[i][j]=0,则表示它们不是邻接点。

2. 创建矩阵

C实现代码:

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
#define MAX 100
typedef struct graph
{
    char vexs[MAX];
    int vexnum;
    int edgnum;
    int matrix[MAX][MAX];
} Graph,*graph;

static int get_position(Graph g,char ch)
{
    int i;
    for(i=0;i<g.vexnum;i++)
        if(g.vexs[i]==ch)
            return i;
    return -1;
}

graph create_graph()
{
    char vexs[]= {'A','B','C','D','E','F','G'};
    char edges[][2]= {{'A','C'},{'A','D'},{'A','F'},{'B','C'},{'C','D'},{'E','G'},{'F','G'}};
    int vlen=sizeof(vexs)/sizeof(vexs[0]);
    int  elen=sizeof(edges)/sizeof(edges[0]);
    int i,p1,p2;
    Graph *pG;
    if((pG=(graph)malloc(sizeof(Graph)))==NULL)
        return NULL;
    pG->edgnum=elen;
    pG->vexnum=vlen;
    for(i=0;i<pG->vexnum;i++)
        pG->vexs[i]=vexs[i];
    for(i=0;i<pG->edgnum;i++)
    {
        p1=get_position(*pG,edges[i][0]);
        p2=get_position(*pG,edges[i][1]);
        pG->matrix[p1][p2]=1;
    }
    return pG;
}

void print_graph(Graph G)
{
    int i,j;
    for(i=0;i<G.vexnum;i++)
    {
        for(j=0;j<G.edgnum;j++)
        {
            printf("%d ",G.matrix[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

int main()
{
    Graph *pG;
    pG=create_graph();
    print_graph(*pG);
}
复制代码

运行结果:

posted on 2018-02-24 11:00  AlanTu  阅读(623)  评论(0编辑  收藏  举报

导航