图论_(1)_图的基本概念

基本概念

图(graph) 是由顶点集合和顶点间的二元关系集合(即边的集合或弧的集合)组成的数据结构,通常可以用 G ( V , E ) G(V,E) G(V,E)表示
顶点集合(vertext set) V ( G ) V(G) V(G) 表示,其中元素称为顶点(vertex),用 u 、 v u、v uv 等符号表示。
边的集合(edge set) E ( G ) E(G) E(G) 表示,其中元素称为边(edge),用 e e e 等符号表示
图的阶(order):顶点的个数,通常用 n n n 表示
边数(size): 边的个数,通常用 m m m 表示

无向图

图中所有的边都没有方向性,这种图称为无向图(undirected graph).
每个元素 ( u , v ) (u,v) (u,v)为一对顶点构成的无序对(用圆括号括起来),便是与顶点 u u u v v v 相关联的一条无向边(undirected edge),这条边没有特定的方向。

E ( G 1 ) = { ( 1 , 2 ) , ( 1 , 3 ) , ( 2 , 3 ) , ( 2 , 4 ) , ( 2 , 5 ) , ( 2 , 6 ) , ( 3 , 4 ) , ( 3 , 5 ) , ( 4 , 5 ) } E(G_1)=\{ (1,2),(1,3),(2,3),(2,4),(2,5),(2,6),(3,4),(3,5),(4,5) \} E(G1)={(1,2),(1,3),(2,3),(2,4),(2,5),(2,6),(3,4),(3,5),(4,5)}
在这里插入图片描述

有向图

图中所有边都是有方向性的,这种图称为有向图,有向图的边也可以称为弧(arc)

度数

顶点的度数(degree):一个顶点 u u u的度数是与它相关联的边的数目,记作 d e g ( u ) deg(u) deg(u)
顶点的出度(outdegree):是以 u u u 为起始的顶点的有向边(即从顶点 u u u 出发的有向边)的数目,记作 o d ( u ) od(u) od(u)
顶点的入度(indegree):是以 u u u 为终点的顶点的有向边(即从顶点 u u u 进入的有向边)的数目,记作 i d ( u ) id(u) id(u)
d e g ( u ) = o d ( u ) + i d ( u ) deg(u)=od(u)+id(u) deg(u)=od(u)+id(u)
定理:在无向图和有向图中,所有顶点度数总和,等于边数的两倍
m = 1 2 { ∑ i = 1 n d e g ( u i ) } m=\frac{1}{2}\{ \sum_{i=1}^ndeg(u_i)\} m=21{i=1ndeg(ui)}
偶点(even vertex):度数为偶数的顶点称为偶点
奇点(odd vertex):度数为奇数的顶点称为奇点

推论:每个图都有偶数个奇点

孤立顶点(isolated vertex):度数为0的顶点,称为孤立顶点。孤立顶点不与其他任何顶点邻接。
叶(leaf):度数为1的顶点,称为叶顶点,也称端点(end vertex), 其他顶点称为非叶顶点
图的最小度(minimum degree):图所有顶点最小的度数,记为 δ ( G ) \delta(G) δ(G)
图的最大度(maximum degree):图所有顶点的最大的度数,记为 Δ ( G ) \Delta(G) Δ(G)

Havel-Hakimi 定理(判断是否可图)

由非负整数组成的非增序列 s : d 1 , d 2 , … , d n ( n ≥ 2 , d 1 ≥ 1 ) s:d_1,d_2,\ldots,d_n(n\geq2,d_1\geq1) s:d1,d2,,dn(n2,d11)是可图的,当且仅当序列
s 1 : d 2 − 1 , d 3 − 1 , … , d d 1 + 1 − 1 , d d 1 + 2 , … , d n s_1:d_2-1,d_3-1,\ldots,d_{d1+1}-1,d_{d1+2},\ldots,d_n s1:d21,d31,,dd1+11,dd1+2,,dn
是可图的。序列 s 1 s_1 s1 中有 n − 1 n-1 n1 个非负整数, s s s 序列中 d 1 d_1 d1后的前 d 1 d_1 d1 个度数(即 d 2 d_2 d2 ~ d d 1 + 1 d_{d1+1} dd1+1 减1 后构成 s 1 s1 s1 中前 d 1 d_1 d1 个数

eg:判断 s : 5 , 4 , 3 , 3 , 2 , 2 , 2 , 1 , 1 , 1 s:5,4,3,3,2,2,2,1,1,1 s:5,4,3,3,2,2,2,1,1,1 是否可图?

  1. 删除序列 s 的首项5, 对其后的5项每项减1,得到 3,2,2,1,1,2,1,1,1, 重新排序 3,2,2,2,1,1,1,1,1
  2. 继续删除序列的首项3,对其后的3项每项减1,得到:1,1,1,1,1,1,1,1
  3. 如下图推导,可判断序列可图

在这里插入图片描述
eg 判断序列 s : 7 , 7 , 4 , 3 , 3 , 3 , 2 , 1 s: 7,7,4,3,3,3,2,1 s:7,7,4,3,3,3,2,1 是否可图的。

  1. 删除序列 s 的首项7,对其后面的7项每项减1,得到 6,3,2,2,2,1,0
  2. 删除首项6,对其后面6项每项减1,得到 2,1,1,1,0,-1

出现负数,序列是不可图的
在这里插入图片描述

邻接矩阵

邻接矩阵(adjacency matrix):一个表示各顶点之间关系的二维矩阵
E d g e [ i ] [ j ] = { 1 , < i , j > ∈ E , 或 ( i , j ) ∈ E 0 Edge[i][j] = \begin{cases} 1 ,<i,j>\in E, 或(i,j) \in E\\ 0\\ \end{cases} Edge[i][j]={1,<i,j>E,(i,j)E0
无向图 的邻接矩阵是沿主对角线 对称
有向图 的邻接矩阵 不一定 沿主对角线对称
无法用邻接矩阵存储: 图中存在自身环(self loop, 连接某个顶点自身的边) 和 重边 (multiple edge, 多条边的起点一样,终点也一样,亦称平行边,parallel edge)

通过邻接矩阵求无向图的度数
d e g ( i ) = ∑ j = 0 n − 1 E d g e [ i ] [ j ] = ∑ j = 0 n − 1 E d g e [ j ] [ i ] deg(i)=\sum_{j=0}^{n-1}Edge[i][j]=\sum_{j=0}^{n-1}Edge[j][i] deg(i)=j=0n1Edge[i][j]=j=0n1Edge[j][i]
邻接矩阵第 i i i 行所有元素之和,表示顶点 i i i 的度数,或者 第 i i i 列所有元素之和,表示顶点 i i i 的度数
通过邻接矩阵求有向图的出度与入度
出度:第 i i i 所有元素之和
o d ( i ) = ∑ j = 0 n − 1 E d g e [ i ] [ j ] od(i)=\sum_{j=0}^{n-1}Edge[i][j] od(i)=j=0n1Edge[i][j]
入度:第 i i i 所有元素之和
i d ( i ) = ∑ j = 0 n − 1 E d g e [ j ] [ i ] id(i)=\sum_{j=0}^{n-1}Edge[j][i] id(i)=j=0n1Edge[j][i]

邻接表

邻接表(adjacency list): 把从同一个顶点发出的边链接在同一个称为 边链表 的单链表中
边结点: 边链表中的每个结点代表一条边,有两个域

  1. 该边终点的序号
  2. 指向下一个边结点的指针

顶点数组:用于存储顶点信息,每个元素有两个成员
3. 一个成员用于存储顶点信息
4. 一个成员为该顶点的边链表的表头指针,指向该顶点的边链表

在这里插入图片描述

出边表:在邻接表每个顶点的边链表中,各边结点所表示的边都是从该顶点发出的边。

出边表的出度 : 统计出边表每个顶点的边链表中边结点的个数
逆邻接表(入边表):把进入同一个顶点的边链接在同一个边链表中。
入边表的入度 : 统计入边表每个顶点的边链表中边结点的个数
在这里插入图片描述
使用 出边表 表示有向图的邻接表
在这里插入图片描述

使用 入边表 表示有向图的邻接表
在这里插入图片描述
无向图 如下,使用 邻接表 表示
在这里插入图片描述
在这里插入图片描述

eg1 用邻接矩阵存储有向图,并输出各顶点的出入度

在这里插入图片描述
输入 第一行为顶点个数及边数,第二行开始为边的关系

7 9
1 2
2 3
2 5
2 6
3 5
4 3
5 2
5 4
6 7
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define MAXN 100
int Edge[MAXN][MAXN];

int main(){
    int a,b;
    int i,j,count;
    int n,m;    // 顶点个数、边数
    int u,v;    // 边的起点和终点
    int od,id;  // 顶点的出度、入度


    FILE *file = fopen("1_1.txt","r");
    if(!file){
        printf("Fail to open file....\n");
        return;
    }

    memset(Edge,0,sizeof(Edge));
    count = 0;
    while(1){
        if(EOF == fscanf(file,"%d %d",&a,&b))
            break;

        count = count + 1;
        if(count==1){
            n=a;
            m=b;
        }

        if (count != 1){
            u=a;
            v=b;
            Edge[u-1][v-1]=1;
        }
    }
    fclose(file);

    printf("%d %d\n",n,m);

    // 打印邻接矩阵
    for(i=0;i<n;i++){
        for(j=0;j<n;j++){
            printf("%d,",Edge[i][j]);
        }
        printf("\n");
    }

    // 求各顶点的出度
    printf("\n各顶点的出度为:\n");
    for(i=0; i<n; i++){
        od = 0;
        for(j=0;j<n;j++){
            od = od + Edge[i][j];  // 累加第i行

        }
        printf("%d ",od);
    }
    printf("\n");

    printf("各顶点的入度为:\n");
    // 求各顶点的入度
    for(i=0;i<n;i++){
        id = 0;
        for(j=0;j<n;j++)
            id =id + Edge[j][i];   //累计第i列
        printf("%d ",id);
    }
    printf("\n");
    return 0;
}

在这里插入图片描述
在这里插入图片描述

eg2 判断是否可图

在这里插入图片描述
输入: 测试组数,每组输入顶点个数,及顶点的度数
输出: 邻接矩阵

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

#define N 15

struct vertex{
    int degree;  //顶点的度
    int index;   //顶点的序号
}v[N];


int cmp(const void *a,const void *b)
{
	return (((struct vertex*)b)->degree) -( ((struct vertex*)a)->degree);
}


int main(){
    int r,k,p,q;        // 循环遍历
    int i,j;            // 顶点序号
    int d1;             // 对剩下序列排列后的第1个顶点(度数最大的顶点)的度数
    int T,n;            // 测试数据的个数,顶点的个数
    int Edge[N][N],flag;  // 邻接矩阵,是否存在合理相邻关系的标记

    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(i=0;i<n;i++){
            scanf("%d",&v[i].degree);
            v[i].index=i;           // 按输入顺序给每个湖泊(顶点)编号
        }

        memset(Edge,0,sizeof(Edge));
        flag=1;
        for(k=0;k<n&&flag;k++){
            qsort(v+k,n-k,sizeof(struct vertex),cmp);  // 对数组进行快速排序,从大到小
            i=v[k].index;
            d1=v[k].degree;
            if(d1>n-k-1)
                flag = 0;
            for(r=1;r<=d1&&flag;r++){
                j=v[k+r].index;
                if(v[k+r].degree<=0) flag=0;
                v[k+r].degree--;
                Edge[i][j]=Edge[j][i]=1;
            }
        }
        if(flag){
            puts("Yes");
            for(p=0;p<n;p++){
                for(q=0;q<n;q++){
                    if(q) printf(" ");
                    printf("%d ",Edge[p][q]);
                }
                puts(" ");
            }
        }
        else
            puts("No");
        if(T) puts("");
    }
    return 0;
}

在这里插入图片描述

eg3 用邻接表存储有向图,求各顶点出度与入读

输入:第一行为顶点个数及边数,第二行开始为边的关系
输出:第一行为顶点的出度,第二行为顶点的入度
测试数据1

7 9
1 2
2 3
2 5
2 6
3 5
4 3
5 2
5 4
6 7

在这里插入图片描述
测试数据2:

4 7
1 4
2 1
2 2
2 3
2 3
4 2
4 3

在这里插入图片描述

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

#define MAXN 100

struct ArcNode{ //边节点
    int adjvex;     // 有向边的另一个邻接点的序号
    struct ArcNode *nextarc;   // 指向下一个边结点的指针
};

struct VNode{   //顶点
    struct ArcNode *head1;     // 出边表的表头指针
    struct ArcNode *head2;     // 入边表的表头指针
};

struct LGraph{  // 图的邻接表存储结构
    struct VNode vertexs[MAXN];    //顶点数组,每个顶点为一个结构体
    int vexnum,arcnum;      //顶点数和边数
};  //图(邻接表存储)
struct LGraph lg;

void CreateLG(){        // 采用邻接表存储表示,构造有向图 G
    int i=0,a,b;
    int v1,v2;
    struct ArcNode *pi;


    for(i=0;i<lg.vexnum;i++){
        lg.vertexs[i].head1=lg.vertexs[i].head2=NULL;
    }

    FILE *file = fopen("1_3_demo.txt","r");
    if(!file){
        printf("创建图期间 Fail to open file....\n");
        return 0;
    }
    int count = 0;
    while(1){
        if(EOF == fscanf(file,"%d %d",&a,&b))
            break;

        count = count+1;

        if(count!=1){
            v1=a;
            v2=b;
            v1--;
            v2--;
            pi = (struct ArcNode*)calloc(2,sizeof(struct ArcNode));
            pi->adjvex = v2;
            pi->nextarc = lg.vertexs[v1].head1;
            lg.vertexs[v1].head1 = pi;
      
            pi = (struct ArcNode*)calloc(2,sizeof(struct ArcNode));
            pi->nextarc = v1;
            pi->nextarc = lg.vertexs[v2].head2;
            lg.vertexs[v2].head2 = pi;
        }
    }
    fclose(file);

}

// 释放图G邻接表各顶点的边链表中的所有节点所占用的存储空间
void DeleteLG(){
    int i;
    struct ArcNode *pi;
    for(i=0;i<lg.vexnum;i++){
        pi = lg.vertexs[i].head1;
        while(pi!=NULL){
            lg.vertexs[i].head1 = pi->nextarc;
            free(pi);
            pi = lg.vertexs[i].head1;
        }
        pi = lg.vertexs[i].head2;
        while(pi!=NULL){
            lg.vertexs[i].head2 = pi->nextarc;
            free(pi);
            pi = lg.vertexs[i].head2;
        }
    }
}

int main(){
    int i,a,b;
    int id,od;
    struct ArcNode *pi;

    FILE *file = fopen("1_3_demo.txt","r");
    if(!file){
        printf("创建图期间 Fail to open file....\n");
        return 0;
    }
    int count = 0;
    while(1){
        if(EOF == fscanf(file,"%d %d",&a,&b))
            break;

        count = count + 1;
        if(count==1){
            lg.vexnum=a;
            lg.arcnum=b;
        }
    }
    fclose(file);
    printf("文件中图的顶点个数及边数读取完成...\n");
    CreateLG();

    for(i=0;i<lg.vexnum;i++){
        od=0;
        pi=lg.vertexs[i].head1;
        while(pi!=NULL){
            od++;
            pi=pi->nextarc;
        }
        if(i==0) printf("%d ",od);
        else
            printf("%d ",od);
    }
    printf("\n");
    for(i=0;i<lg.vexnum;i++){
        id=0;
        pi=lg.vertexs[i].head2;
        while(pi!=NULL){
            id++;
            pi = pi->nextarc;
        }
        if(i==0)
            printf("%d ",id);
        else
            printf("%d ",id);
    }
    printf("\n");
    DeleteLG();
    return 0;
}

核心代码计算机详细过程

pi = (struct ArcNode*)calloc(2,sizeof(struct ArcNode));
pi->adjvex = v2;
pi->nextarc = lg.vertexs[v1].head1;
lg.vertexs[v1].head1 = pi;

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值