图的相关操作(邻接矩阵表示法)

本文详细介绍了使用邻接矩阵表示法创建和操作图的各种方法,包括有向图、有向网、无向图和无向网的创建,以及顶点的插入、删除、赋值等操作。同时,提供了深度优先遍历和广度优先遍历的算法实现,帮助读者理解图的基本操作和遍历策略。
摘要由CSDN通过智能技术生成

图的相关操作(邻接矩阵表示法)

一:邻接矩阵的结点定义

#define MAX_VERTEX_NUM 20   //定义最大顶点数
#define VertexType int      //定义顶点的数据类型
#define VRType int          //定义顶点之间关系的变量类型
#define InfoTye char        //存储弧或者边的额外信息的指针变量类型
#define INFINITY 100        //定义带权值的图中不可达的无穷
typedef enum {DG,DN,UDG,UDN}GraphKind;      //定义图的类型,0有向图,1有向网,2无向图,3无向网

typedef struct {
    VRType adj;     //顶点之间的关系,如果是无向图,则两定点相邻为1,不想邻为0,若为有向图,则用顶点之间的的权值表示
    InfoTye *info;  //表示边后者弧额外含有的信息指针
}ArcCell;
typedef ArcCell AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];  //表示图的邻接矩阵
typedef struct {
    VertexType vexs[MAX_VERTEX_NUM];    //用来存储顶点的数组
    AdjMatrix arcs;                     //邻接矩阵
    GraphKind kind;                     //图的类型
    int vexnum,arcnum;                  //记录图的定点数和边数
}MGraph;

二:创建矩阵的入口函数

//构造有、无向图,有、无向网的入口函数
int CreatGraph_M(MGraph* G){
    printf("Please select the kind of Graph:");
    scanf("%d",&G->kind);
    switch (G->kind) {
        case DG:
            return CreatDG_M(G);
        case DN:
            return CreatDN_M(G);
        case UDG:
            return CreatUDG_M(G);
        case UDN:
            return CreatUDN_M(G);
        default:break;
    }
}

三:定义顶点v在顶点数组中的位置(下标)

/**
 * 确定顶点v在顶点数组中的位置
 * @param G
 * @param v		顶点数据
 * @return		顶点在顶点数组中的下标
 */
int LocateVex(MGraph G,VertexType v){
    for (int i = 0; i < G.vexs; ++i) {
        if (v == G.vexs[i]){
            return i;
        }
    }
    return -1;
}

四:创建有向图

//创建有向图
int CreatDG_M(MGraph* G){
    //输入图的顶点个数vex,边数arc
    printf("Please input the number of vex and arc:");
    scanf("%d,%d",&(G->vexnum),&(G->arcnum));
    getchar();

    //依次输入顶点数据本身
    printf("Please input the vex:\n");
    for (int i = 0; i < G->vexnum; ++i) {
        scanf("%d",&(G->vexs[i]));
        getchar();
    }

    //初始化二维矩阵数组,全为0
    for (int i = 0; i < G->vexnum; ++i) {
        for (int j = 0; j < G->vexnum; ++j) {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;  //目前没有指针所指代的内容
        }
    }

    //在二位数组中添加数据
    for (int i = 0; i < G->arcnum; ++i) {
        VertexType v1;  //弧尾
        VertexType v2;  //弧头
        //输入弧尾与弧头
        printf("Please input trail and front:");
        scanf("%d,%d",&v1,&v2);
        //定位
        int trail = LocateVex(*G,v1);   //弧尾
        int front = LocateVex(*G,v2);   //弧头

        //若有顶点不在图中
        if (front==-1||trail == -1){
            printf("No find vertex!!");
            return 0;
        }

        //将正确的弧数据加入到邻接矩阵中
        G->arcs[trail][front].adj = 1;
    }
    return 1;
}

五:创建有向网

/**
 * 创建有向网
 * 有向网与有向图的区别在与有向图中没有权值或者说权值为1
 * 简而言之有向网为带权值的有向图
 * @param G
 * @return
 */
int CreatDN_M(MGraph* G){
   //输入图中顶点个数与边的个数
   printf("Please input the num of vexs and arc:");
   scanf("%d,%d",&(G->vexnum),&(G->arcnum));
   getchar();

   //输入顶点数据
    printf("Please input the data of vex:");
    for (int i = 0; i < G->vexnum; ++i) {
        scanf("%d",&(G->vexs[i]));
        getchar();
    }

    //初始化邻接矩阵为0
    for (int i = 0; i < G->vexnum; ++i) {
        for (int j = 0; j < G->vexnum; ++j) {
            G->arcs[i][j].adj = INFINITY;
            G->arcs[i][j].info = NULL;
        }
    }

    //确定头尾和边上的权值
    for (int i = 0; i < G->arcnum; ++i) {
        VertexType v1,v2;   //v1为尾,v2为头
        VRType m;           //表示当前以v1为尾,v2为头的弧上面的权值

        printf("Please input the trail , head and value:");
        scanf("%d,%d,%d",&v1,&v2,&m);

        int row = LocateVex(*G,v1);     //尾
        int col = LocateVex(*G,v2);     //头

        //判断是否存在
        if (row == -1||col == -1){
            return 0;
        }

        G->arcs[row][col].adj = m;
    }
    return 1;
}

六:创建无向图

int CreatUDG_M(MGraph* G){
    //输入图的顶点个数vex,边数arc
    printf("Please input the number of vex and arc:");
    scanf("%d,%d",&(G->vexnum),&(G->arcnum));
    getchar();

    //依次输入顶点数据本身
    printf("Please input the vex:\n");
    for (int i = 0; i < G->vexnum; ++i) {
        scanf("%d",&(G->vexs[i]));
        getchar();
    }

    //初始化二维矩阵数组,全为0
    for (int i = 0; i < G->vexnum; ++i) {
        for (int j = 0; j < G->vexnum; ++j) {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;  //目前没有指针所指代的内容
        }
    }

    //在二位数组中添加数据
    for (int i = 0; i < G->arcnum; ++i) {
        //在无向图中没有规定谁是弧头谁是弧尾
        VertexType v1;
        VertexType v2;
        //输入弧尾与弧头
        printf("Please input trail and front:");
        scanf("%d,%d",&v1,&v2);
        //定位
        int trail = LocateVex(*G,v1);
        int front = LocateVex(*G,v2);

        //若有顶点不在图中
        if (front==-1||trail == -1){
            printf("No find vertex!!");
            return 0;
        }

        //将正确的弧数据加入到邻接矩阵中,并且无向图是对称矩阵
        G->arcs[trail][front].adj = 1;
        G->arcs[front][trail].adj = 1;
    }
    return 1;
}

七:创建无向网

int CreatUDN_M(MGraph* G){
    //输入图中顶点个数与边的个数
    printf("Please input the num of vexs and arc:");
    scanf("%d,%d",&(G->vexnum),&(G->arcnum));
    getchar();
    //输入顶点数据
    printf("Please input the data of vex:\n");
    for (int i = 0; i < G->vexnum; ++i) {
        scanf("%d",&(G->vexs[i]));
        getchar();
    }

    //初始化邻接矩阵为0
    for (int i = 0; i < G->vexnum; ++i) {
        for (int j = 0; j < G->vexnum; ++j) {
            G->arcs[i][j].adj = INFINITY;
            G->arcs[i][j].info = NULL;
        }
    }

    //确定头尾和边上的权值
    for (int i = 0; i < G->arcnum; ++i) {
        VertexType v1,v2;   //v1为尾,v2为头
        VRType m;           //表示当前以v1为尾,v2为头的弧上面的权值

        printf("Please input the trail , head and value:");
        scanf("%d,%d,%d",&v1,&v2,&m);

        int row = LocateVex(*G,v1);     //尾
        int col = LocateVex(*G,v2);     //头

        //判断是否存在
        if (row == -1||col == -1){
            return 0;
        }

        G->arcs[row][col].adj = m;
        G->arcs[col][row].adj = m;
    }
    return 1;
}

八:获取第n个顶点的值

/**
 * 返回第n个结点的值
 * @param G
 * @param order
 * @return
 */
VertexType GetVexValue_M(MGraph G,int order){
    if (order>=1 && order<=G.vexnum){   //图中的结点是从1开始的
        return G.vexs[order-1];         //在数组中需要进行减一操作
    }
    return -1;
}

九:对顶点v进行赋值

/**
 * 对顶点v进行赋值操作
 * @param G
 * @param v 顶点值为v
 * @param value     待赋的值
 * @return
 */
int PutVex_M(MGraph* G,VertexType v,VertexType value){
    //遍历图中存储节点的数组
    for (int i = 0; i < G->vexnum; ++i) {
        if (G->vexs[i] = v){
            G->vexs[i] = value;
            return 1;
        }
    }
    return 0;
}

十:将顶点v插入到图中(但是没有插入弧或边)

/**
 * 将顶点v插入到图中
 * @param G
 * @param v
 * @return
 */
int InsertVex_M(MGraph* G,VertexType v){
    //判断顶点的数量是否已经到达最大值
    if (G->vexnum >= MAX_VERTEX_NUM){
        return 0;
    }

    //判断是图还是网
    int sign;
    if (G->kind%2 == 1){
        sign = INFINITY;    //网
    } else{
        sign = 0;
    }

    //将顶点v加入到顶点数组中
    G->vexnum++;
    G->vexs[G->vexnum-1] = v;

    //给邻接矩阵加一行和一列
    for (int i = 0; i < G->vexnum; ++i) {
        G->arcs[G->vexnum-1][i].adj = sign;
        G->arcs[i][G->vexnum-1].adj = sign;
    }
    return 1;
}

十一:删除顶点v

/**
 * 删除顶点v
 * 思路:
 *      删除顶点主要有三部分操作
 *          操作顶点数组,当删除完顶点之后要将后面顶点前移一位
 *          操作邻接矩阵,对应行和列上的数据要清除并将后续行列的数据前移
 *          修改顶点数和边数
 *              注意区分有向图和无向图边数的修改
 * @param G
 * @param v
 * @return
 */
int DeleteVex_M(MGraph* G,VertexType v){
    int index = LocateVex(*G,v);
    if (index == -1){
        return 0;       //待删顶点不在图中
    }
    //删除顶点
    for (int i = index; i <G->vexnum-1 ; ++i) {
        G->vexs[i] = G->vexs[i+1];
    }
    //顶点数组的最后一位置为0
    G->vexs[G->vexnum-1] = 0;
    //统计与待删除结点相接的边数
    int rowcount = 0;
    int colcount = 0;
    int sign;
    if (G->kind%2 == 1){
        sign = INFINITY;    //网
    } else{
        sign = 0;
    }

    for (int i = 0; i < G->vexnum; ++i) {
        if (G->arcs[index][i].adj != sign){
            rowcount++;
        }
    }
    for (int i = 0; i < G->vexnum; ++i) {
        if (G->arcs[i][index].adj != sign){
            colcount++;
        }
    }
    int adjsnum;
    if (G->kind == 0 || G->kind){   //如果是有向图或者是有向网
        adjsnum = rowcount+colcount;
    } else if (G->kind == 2 || G->kind == 3){   //因为无向图或者无向网的邻接矩阵是对称的,所以需要除2
        adjsnum = (rowcount+colcount)/2;
    }

    //处理行
    for (int j = index; j < G->vexnum-1; ++j) {
        for (int i = 0; i < G->vexnum; ++i) {
            G->arcs[j][i] = G->arcs[j+1][i];
        }
    }

    //处理列
    for (int i = index; i < G->vexnum-1 ; ++i) {
        for (int j = 0; j < G->vexnum; ++j) {
            G->arcs[j][i] = G->arcs[j][i+1];
        }
    }

    //顶点数减一,边数减少
    G->vexnum--;
    G->arcnum = G->arcnum - adjsnum;
    return 1;
}

十二:深度优先遍历

/**
 * 寻找第一个与下标为index的顶点相邻的顶点的下标
 * @param G
 * @param index
 * @return  相邻顶点在数组中的下标
 */
int FirstAdjVex(MGraph G,int index){
    int sign;
    if (G.kind%2 == 1){ //表示网,有权值
        sign = INFINITY;
    } else{
        sign = 0;
    }
    for (int i = 0; i < G.vexnum; ++i) {
        if (sign!=G.arcs[index][i].adj){
            return i;
        }
    }
    return -1;
}

/**
 *
 * @param G
 * @param indexV    访问顶点的下标
 * @param indexW    相对顶点的下标
 * @return
 */
int NextAdjVex(MGraph G,int indexV,int indexW){
    int sign;
    if (G.kind%2 == 1){
        sign = INFINITY;
    } else{
        sign = 0;
    }
    for (int i = indexW+1; i <G.vexnum ; ++i) {
        if (G.arcs[indexV][i].adj != sign){
            return i;
        }
    }
    return -1;
}

void visitVex(MGraph G,int index){
    printf("%d\t",G.vexs[index]);
}
//全局变量
bool visited[MAX_VERTEX_NUM];       //访问标志数组
/**
 * 深度优先遍历思想为:
 *      从图中的某起始顶点v开始,访问与v相邻但是并未被访问的下一顶点w1,在访问与w1相邻但是未被访问的顶点
 *      w2。。。。重复上述过程,当不能够将继续向下访问的时候,则依次回退到最近被访问的顶点,若它还有邻接
 *      顶点没有被访问,则从该顶点开始继续上述搜索。直到图中的所有顶点都被访问
 * 显然这是一个递归的过程,那么在遍历过程中要容易辨别定点是否被访问,则需要设置一个全局变量标志数组visited,
 * 来表示该顶点是否已经被访问。
 *
 * @param G
 */
void DFSTraverse(MGraph G){
    //首先初始化访问表示数组
    for (int i = 0; i < G.vexnum; ++i) {
        visited[i] = false;     //初始为false,表示没有被访问
    }

    for (int i = 0; i < G.vexnum; ++i) {
        if (!visited[i]){   //如果尚未被访问
            DFS(G,i);
        }
    }
}
/**
 * 深度优先遍历核心算法
 * @param G
 * @param v     //顶点在数组中的下标
 */
void DFS(MGraph G,int v){
    //访问当前顶点
    visited[v] = true;
    visitVex(G,v);
    int w;
    for (w = FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w)) {       //这边需要想明白
        if (!visited[w]){
            DFS(G,w);
        }
    }
}

十三:广度优先遍历

/**
 * 寻找第一个与下标为index的顶点相邻的顶点的下标
 * @param G
 * @param index
 * @return  相邻顶点在数组中的下标
 */
int FirstAdjVex(MGraph G,int index){
    int sign;
    if (G.kind%2 == 1){ //表示网,有权值
        sign = INFINITY;
    } else{
        sign = 0;
    }
    for (int i = 0; i < G.vexnum; ++i) {
        if (sign!=G.arcs[index][i].adj){
            return i;
        }
    }
    return -1;
}

/**
 *
 * @param G
 * @param indexV    访问顶点的下标
 * @param indexW    相对顶点的下标
 * @return
 */
int NextAdjVex(MGraph G,int indexV,int indexW){
    int sign;
    if (G.kind%2 == 1){
        sign = INFINITY;
    } else{
        sign = 0;
    }
    for (int i = indexW+1; i <G.vexnum ; ++i) {
        if (G.arcs[indexV][i].adj != sign){
            return i;
        }
    }
    return -1;
}

void visitVex(MGraph G,int index){
    printf("%d\t",G.vexs[index]);
}


//全局变量
bool visited[MAX_VERTEX_NUM];       //访问标志数组

/**
 * 广度优先遍历
 * @param G
 */
void BFSTraverse(MGraph G){
    //初始化标记数组
    for (int i = 0; i < G.vexnum; ++i) {
        visited[i] = false;
    }

    int queue[20];      //用来模仿队列
    int rear = 0;
    int top = 0;

    for (int v = 0; v < G.vexnum; ++v) {
        if (!visited[v]){
            visited[v] = true;
            visitVex(G,v);  //访问
            queue[rear] = G.vexs[v];    //将顶点加入到队列中
            rear = (rear+1)%20;
            while (rear-top != 0){      //当队列不为空时
                int u = queue[top];
                top = (top+1)%20;   //出队列一个顶点
                u = LocateVex(G,u); //获取顶点在数组中的下标
                for (int w = FirstAdjVex(G,u); w >=0 ; w = NextAdjVex(G,u,w)) {
                    if (!visited[w]){
                        visited[w] = true;
                        visitVex(G,w);
                        queue[rear] = G.vexs[w];
                        rear = (rear+1)%20;
                    }
                }
            }

        }
    }
}

图示

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值