第七章 图

邻接矩阵(二维数组)MGraph( Matrix Graph )

测试建立无向网

#include<stdio.h>
typedef int Status;
//--------------数组(邻接矩阵)表示法  (二维数组)--------------
#define INFINITY 9999    //最大值
#define MAX_VERTEX_NUM 20   //最大顶点个数
typedef enum{DG, DN, UDG, UDN}GraphKind;    //{有向图,有向网,无向图,无向网}
typedef int VRType;
typedef struct InfoType{    //弧的信息  没用到
    int start;
    int end;
}InfoType;
typedef struct VertexType{  //顶点信息  没用到
    int pos;
}VertexType;
typedef struct ArcCell{
    VRType adj;         //VRType顶点关系类型。对无权图,用1或0表示相邻否
                        //对带权图,则为权值类型
    InfoType * info;    //该弧相关信息的指针
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct {
    VertexType vexs[MAX_VERTEX_NUM];    //顶点向量 没用到
    AdjMatrix arcs;                     //邻接矩阵  二维数组,存储点和点之间的关系:有无边,有无权
    int vexnum, arcnum;                 //当前顶点数,弧数
    GraphKind kind;                     //图的种类
}MGraph;

/*
 * 存储无向图
 * ->对称矩阵
 * 压缩存储下(上)三角元素
 * 顶点Vi的出度:第i行的元素之和
 * 顶点Vi的入度:第i列的元素之和
 *
 * 网的邻接矩阵可定义为:A[i][j]=  Wij         顶点Vi和Vj连通,即<Vi, Vj>属于VR
 *                               INFINITY   不连通
 *
 *
 */
Status InputInfo(InfoType * info){
    return 1;
}//输入弧的信息
Status CreateDG(MGraph &G){
    return 1;
}
Status CreateDN(MGraph &G){
    return 1;
}
Status CreateUDG(MGraph &G){;
    return 1;
}
Status CreateUDN(MGraph &G){
    //采用二维数组(邻接矩阵),构造有无向网G
    int IncInfo, i, j, k, w;
    printf("Init the num:\n");
    scanf("%d %d %d",&G.vexnum, &G.arcnum, &IncInfo);   //顶点数,弧数,IncInfo为0则弧不含其他信息
    //for(i=0;i<G.vexnum;i++) scanf("%d",&G.vexs[i].pos); //构造顶点向量 没用到
    for(i=0;i<G.vexnum;i++)
        for(j=0;j<G.vexnum;j++) G.arcs[i][j]={INFINITY, NULL};  //初始化顶点和顶点的关系,弧的信息
    printf("Init the vertex and the weight(%d):\n",G.arcnum);
    for(k=0; k<G.arcnum; k++){
        scanf("%d %d %d",&i, &j, &w);
        G.arcs[i][j].adj=w;     //构造二维数组以及权重
        G.arcs[j][i]=G.arcs[i][j];

        if(IncInfo) InputInfo(G.arcs[i][j].info);
    }
    return 1;
}
Status CreateGraph(MGraph &G){
    //采用二维数组(邻接矩阵),构造图G
    printf("Input the kind of Graph: \n");
    scanf("%d",&G.kind);
    switch (G.kind) {
        case 0:return CreateDG(G);  //有向图
        case 1:return CreateDN(G);  //有向网
        case 2:return CreateUDG(G); //无向图
        case 3:return CreateUDN(G); //无向网:有权重
        default: return 0;
    }
}
void Print(MGraph G){
    printf("Output:\n");
    for(int i=0;i<G.vexnum;i++){
        for(int j=0;j<G.vexnum;j++)
            if(G.arcs[i][j].adj!=INFINITY) printf("%-3d",G.arcs[i][j].adj);
            else printf("%s ","OO");
        printf("\n");
    }
    printf("\n");
}

int main(){
    MGraph G;
    CreateGraph(G);
    Print(G);

    return 1;
}
/*
3

6 10 0

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

OO 5  8  7  OO 3
5  OO 4  OO OO OO
8  4  OO 5  OO 9
7  OO 5  OO 5  6
OO OO OO 5  OO 1
3  OO 9  6  1  OO
 */
邻接表存储(链式存储)ALGraph( Adjacency List Graph )

测试建立无向图

#include<stdio.h>
#include <cstdlib>

#define MAX_VERTEX_NUM 20   //最大顶点个数
/*
 * ----------------------------邻接表----------------------------
 * 链式存储
 *
 * 每个边建立一个单链表:(该边属于某一个点)
 *      邻接点域adjvex:指示与该顶点通过该边邻接的点。如果是有向图,就存储该边指向的顶点
 *      链域nextarc:指示该点的下一条边的节点
 *      数据域info:存储和边相关的信息,如权值
 * 每个链表上附设一个表头节点:
 *      链域firstarc:指向链表中第一条边
 *      数据域data:存储顶点的名,或其他信息,也就是存储一个顶点。链域存储的是该点的所有边
 *
 * 图是一个数组,存储每一个点,也就是存储每一个表头节点。顺序存储,可以访问任一顶点的链表
 *      任一头节点,存储该点的名称,next指向该点的所有邻边
 */
typedef int InfoType;
typedef int Status;
typedef struct ArcNode{
    int adjvex;     //该弧所指向的顶点
    struct ArcNode * nextarc;   //指向下一条弧
    InfoType        *info;      //弧相关信息,在这里暂定为权值,如果有需要扩展为结构体
}ArcNode;
typedef struct VertexType{
    int data;   //序号
    char ch;    //代表字母
}VertexType; //顶点信息
typedef struct VNode{   //头节点,存储该点的信息,指向该点的邻边
    VertexType data;    //顶点信息
    ArcNode * firstarc; //该点的第一条邻边
}VNode, AdjList[MAX_VERTEX_NUM];
typedef struct {
    AdjList vertices;   //顺序表,存储每一个顶点的头结点
    int vexnum,arcnum;  //顶点数,边数
    int kind;           //图类型
}ALGraph;

//若是无向图,n个顶点e条边,需要n个头结点和2e个链表节点
//在边稀疏( e << n(n-1)/2 )的情况下,用邻接表比邻接矩阵节省存储空间。如果弧的信息更多是更是如此

//对于无向图 第i个头结点的链表节点就是该点的出度
//          为了便于求入度即以该点为头的弧,建立逆邻接表
//在邻接表上容易找到任一顶点的第一邻接点和下一邻接点。但是如果想判断两个顶点之间是否有边,需要搜索这两个头结点,不及邻接矩阵方便

Status CreateUDG(ALGraph &G){    //构造无向图  G.kind=UDG(2)
    int i, k;
    printf("Init the num:\n");
    scanf("%d %d",&G.vexnum, &G.arcnum);
    for (i = 1; i <= G.vexnum; ++i)         //---------------下标从1开始
        G.vertices[i].firstarc=NULL;

    VertexType vt1,vt2;
    int v1,v2;
    printf("Init the double vertex(%d):\n",G.arcnum);
    for(k=0;k<G.arcnum;++k){
        getchar();
        scanf("%c %c", &vt1.ch, &vt2.ch);
        vt1.data = v1 = (int)vt1.ch-64;
        vt2.data = v2 = (int)vt2.ch-64;
        G.vertices[v1].data = vt1;
        ArcNode *p = (ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex = v2;
        p->nextarc = G.vertices[v1].firstarc;
        G.vertices[v1].firstarc=p;

        G.vertices[v2].data=vt2;
        ArcNode *q = (ArcNode*)malloc(sizeof(ArcNode));
        q->adjvex = v1;
        q->nextarc = G.vertices[v2].firstarc;
        G.vertices[v2].firstarc = q;
    }
    return 1;
}
void Print(ALGraph G){
    printf("Output Adjacency List Graph:\n");
    ArcNode *p;
    for(int i=1;i<=G.vexnum;i++){
        printf("%c %d ",G.vertices[i].data.ch, G.vertices[i].data.data);
        p = G.vertices[i].firstarc;
        while (p){
            printf("->%d ",p->adjvex);
            p = p->nextarc;
        }
        printf("\n");
    }
    printf("\n");
}

int main(){
    ALGraph G;
    CreateUDG(G);
    Print(G);
    return 1;
}
/*
 * 无向图G3
13 13
A B
A C
A F
A L
B M
D E
G H
G I
G K
H K
J L
J M
L M
 */

十字链表OLGraph( Orthogonal List Graph)

测试构造有向网

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

#define MAX_VERTEX_NUM 20   //最大顶点个数
#define INFINITY 9999    //最大值
typedef int Status;
/*
 * ----------------------------十字链表----------------------------
 * 链式存储
 * 可以看作是邻接表和逆邻接表结合起来的一种链接
 * 在十字链表中,弧和顶点都有一个链表节点
 *      弧(链表节点):
 *          尾域tailvex和头域headvex:分别指示弧尾顶点和弧头顶点
 *          链域hlink:指向弧头相同的下一条弧
 *          链域tlink:指向弧尾相同的下一条弧
 *          info域:表示弧的信息
 *      顶点(链表头节点):
 *          data域:存储顶点信息,如序号名称
 *          firstin和firstout链域:分别指向以该顶点为弧头或弧尾的第一个弧节点
 *
 * 有向图的OLGraph结构体包含三个数据:
 *      顶点数,边数
 *      头结点数组[MAX_VERTEX_NUM],每一个数据是一个顶点(头结点)
 */
typedef struct InfoType{
    int weight;
}InfoType;
typedef struct ArcBox{
    int tailvex,headvex;
    ArcBox *hlink, *tlink;
    InfoType * info;
}ArcBox;
typedef int VertexType;
typedef struct VexNode{ //头结点
    VertexType data;
    ArcBox *firstin, *firstout;
}VexNode;
typedef struct{
    VexNode xlist[MAX_VERTEX_NUM];
    int vexnum, arcnum;
    int kind;
}OLGraph;

InfoType * InputInfo(){
    InfoType * info = (InfoType*)malloc(sizeof(InfoType));
    scanf("%d",&(info->weight));
    return info;
}//输入弧的信息,即权重
Status CreateDN(OLGraph &G){    //构造有向网  G.kind=DN(1)
    int IncInfo, i, k;
    printf("Init the num:\n");
    scanf("%d %d %d",&G.vexnum, &G.arcnum, &IncInfo);
    for(i=0;i<G.vexnum;++i){
//        scanf("%d",G.xlist[i].data);
        G.xlist[i].data=i;
        G.xlist[i].firstin = NULL;
        G.xlist[i].firstout = NULL;
    }

    VertexType v1,v2;
    printf("Init the vertex and the weight(%d):\n",G.arcnum);
    for(k=0;k<G.arcnum;++k){
        scanf("%d %d", &v1, &v2);
        ArcBox *p = (ArcBox*)malloc(sizeof(ArcBox));
        *p = {v1, v2, G.xlist[v2].firstin, G.xlist[v1].firstout};
        if(IncInfo) p->info=InputInfo(); //输入弧的信息,当前设置的弧信息只有权重
        G.xlist[v2].firstin=p;  G.xlist[v1].firstout=p;
    }
}
Status CreateGraph(OLGraph &G){
    //采用二维数组(邻接矩阵),构造图G
    printf("Input the kind of Graph: \n");
    scanf("%d",&G.kind);
    switch (G.kind) {
        case 0:  //有向图
        case 1:return CreateDN(G);  //有向网
        case 2: //无向图
        case 3: //无向网
        default: return 0;
    }
}
void PrintOLG(OLGraph G){
    printf("Output:\n");
    int gra[G.vexnum][G.vexnum];
    for(int i=0;i<G.vexnum;i++)
        for(int j=0;j<G.vexnum;j++) gra[i][j]=INFINITY;
    printf("Adjacency table:\n");
    for(int i=0;i<G.vexnum;i++){
        VexNode p = G.xlist[i]; //头结点
        printf("%d ",p.data);
        ArcBox *q = p.firstout;
        while (q){
            printf("->[%d|",q->headvex);
            printf("%d]",q->info->weight);
            gra[i][q->headvex]=q->info->weight;
            q = q->tlink;
        }
        printf("\n");
    }
    printf("Inverse Adjacency table:\n");
    for(int i=0;i<G.vexnum;i++){
        VexNode p = G.xlist[i]; //头结点
        printf("%d ",p.data);
        ArcBox *q = p.firstin;
        while (q){
            printf("->[%d|",q->tailvex);
            printf("%d]",q->info->weight);
            q = q->hlink;
        }
        printf("\n");
    }
    printf("Matrix:\n");
    for(int i=0;i<G.vexnum;i++){
        for(int j=0;j<G.vexnum;j++)
            if(gra[i][j]!=INFINITY) printf("%-3d",gra[i][j]);
            else printf("%s ","OO");
        printf("\n");
    }
    printf("\n");
}
int main(){
    OLGraph G;
    CreateGraph(G);
    PrintOLG(G);
    return 1;
}
/*
 * 图7.9 网N
1

6 10 1

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

OO 5  OO 7  OO OO
OO OO 4  OO OO OO
8  OO OO OO OO 9
OO OO 5  OO OO 6
OO OO OO 5  OO OO
3  OO OO OO 1  OO

 */
邻接多重表AMLGraph( Adjacency Multiple List Graph )
#include<stdio.h>
#include<stdlib.h>

#define MAX_VERTEX_NUM 20   //最大顶点个数
#define INFINITY 9999    //最大值
typedef int Status;
/*
 * --------------------邻接多重表--------------------
 * 无向图 链式存储
 * 用途:如果需要对边进行某种操作,例如删除边或者做记号,此时需要找到边的两个顶点。采用邻接多重表更适宜
 *
 * 数据结构:
 * 每一条边用一个节点表示:
 *      mark:标识域,标志这条边是否被搜索过
 *      ivex,jvex:该边的两个顶点
 *      ilink:下一条依附于点ivex的边
 *      jlink:下一条依附于点jvex的边
 *      info:指向边的信息
 * 每一个顶点用一个头结点表示:
 *      data:存储顶点信息
 *      firstedge:指向第一条依附于该顶点的边
 *
 * 邻接多重表中,所有依附于一个顶点的边链在一条链表中。
 * 一条边同时被两个顶点所链接。
 * 邻接多重表和邻接表的区别就是,一条边在邻接多重表中只有一个节点,而在邻接表中是两个节点。
 */
typedef struct InfoType{
    ;
}InfoType;
typedef enum{unvisited, visited} VisitIf;
typedef struct EBox{
    VisitIf mark;
    int ivex,jvex;
    struct EBox *ilink,*jlink;
    InfoType *info;     //边的信息
};
typedef int VertexType;
typedef struct VexBox{
    VertexType data;   //顶点信息
    EBox *firstedge;    //指向第一条依附于该顶点的边
};
typedef struct {
    VexBox adjmulist[MAX_VERTEX_NUM];
    int vexnum,degenum;
}AMLGraph;

图的遍历DFS+BFS

DFS:邻接表存储 无向图 时间复杂度O(n+e)
BFS:时间复杂度O(n+e)

#include<stdio.h>
#include<stdlib.h>
typedef int Status;
#define INFINITY 9999    //最大值
#define MAX_VERTEX_NUM 20   //最大顶点个数
/*
 * --------------------DFS递归遍历无向图--------------------
 * Status (*VisitFunc)(int v);
 * void DFS(Graph G, int v){    //从第v个顶点出发递归地深度优先遍历图G
 *      visited[v] = True;  VisitFunc(v);
 *      for(w=FirstAdjVex(G,v); w>0; w=NextAdjVex)
 *          if(visited[w]) DFS(G,w)
 * }
 * void DFSTraverse(Graph G, Status (*Visit)(int v)){
 *      VisitFunc = visit;
 *      for(v=0;v<G.vexnum;v++) visited[v]=Flase;
 *      for(v=0;v<G.vexnum;v++) if(!visited[v]) DFS(G,v);
 * }
 *
 * 在遍历时,对每个顶点至多调用一次DFS。遍历图的实质就是对每个顶点寻找其邻接点的过程。耗费的时间取决于采用的存储结构。
 * 用二维表时,查找每个顶点的邻接点O(n*n),n为顶点数
 * 用邻接表时,查找每个顶点的邻接点是O(e),e为边数.DFS总时间是O(n+e)
 *
 */

//--------------------构造无向图的邻接表--------------------
//无向图,无权重
typedef struct ArcNode{
    int adjvex;     //该弧所指向的顶点
    struct ArcNode * nextarc;   //指向下一条弧
}ArcNode;
typedef int VertexType; //顶点信息,在这里暂定为序号
typedef struct VNode{   //头节点,存储该点的信息,指向该点的邻边
    VertexType data;    //顶点信息
    ArcNode * firstarc; //该点的第一条邻边
}VNode, AdjList[MAX_VERTEX_NUM+1];
typedef struct {
    AdjList vertices;   //顺序表,存储每一个顶点的头结点
    int vexnum,arcnum;  //顶点数,边数
}ALGraph;

Status CreateUDG(ALGraph &G){    //构造无向图  G.kind=UDG(2)
    int i, k;
    printf("Init the num:\n");
    scanf("%d %d",&G.vexnum, &G.arcnum);
    for (i = 1; i <= G.vexnum; ++i)         //---------------下标从1开始
        G.vertices[i].firstarc=NULL;

    VertexType v1,v2;
    printf("Init the double vertex(%d):\n",G.arcnum);
    for(k=0;k<G.arcnum;++k){
        scanf("%d %d", &v1, &v2);
        G.vertices[v1].data=v1;
        ArcNode *p = (ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex = v2;
        p->nextarc = G.vertices[v1].firstarc;
        G.vertices[v1].firstarc=p;

        G.vertices[v2].data=v2;
        ArcNode *q = (ArcNode*)malloc(sizeof(ArcNode));
        q->adjvex = v1;
        q->nextarc = G.vertices[v2].firstarc;
        G.vertices[v2].firstarc = q;
    }
    return 1;
}
void Print(ALGraph G){
    printf("Output Adjacency List Graph:\n");
    ArcNode *p;
    for(int i=1;i<=G.vexnum;i++){
        printf("%d ",G.vertices[i].data);
        p = G.vertices[i].firstarc;
        while (p){
            printf("->%d ",p->adjvex);
            p = p->nextarc;
        }
        printf("\n");
    }
    printf("\n");
}

/*
 * --------------------DFS递归遍历无向图--------------------
 */

Status Visit(VertexType v){
    printf("%d ",v);
    return 1;
}
Status (*VisitFunc)(VertexType v);
int visited[MAX_VERTEX_NUM+1];
void DFS(ALGraph G, int v) {
    visited[v] = 1;
    VisitFunc(v);
    for(ArcNode *p = G.vertices[v].firstarc; p;){
        if(!visited[p->adjvex]) DFS(G,p->adjvex);
        p=p->nextarc;
    }
}
void DFSTraverse(ALGraph G, Status (*vis)(VertexType v)){
    printf("DFS:\n");
    VisitFunc = vis;
    for(int v=1;v<=G.vexnum;++v) visited[v]=0;
    for(int v=1;v<=G.vexnum;++v) if(!visited[v]) DFS(G,v);
}

/*
 * --------------------BFS非递归遍历无向图--------------------
 * 使用辅助顺序队列Q,访问标志数组visited[]
 *
 * 每个顶点最多进一次队列。遍历图的过程实质上是通过邻接表寻找某一个顶点的全部邻接点的过程。
 * 复杂度为O(n+e)
 * 与DFS不同之处在于对顶点的访问顺序不同
 */
#define MAXSIZE MAX_VERTEX_NUM //队列最大长度
typedef VertexType QElemType;
typedef struct {
    QElemType *base;
    int front;
    int rear;
}SqQueue;
//头指针Q.front指向队列第一个元素,尾指针Q.rear指向最后一个元素的下一位置
Status InitQueue(SqQueue &Q){
    Q.base = (QElemType*)malloc(MAXSIZE*sizeof(QElemType));
    Q.front = Q.rear = 0;
    return 1;
}
Status EnQueue(SqQueue &Q, QElemType e){
    if((Q.rear+1)%MAXSIZE==Q.front)
        Q.base = (QElemType*)realloc(Q.base,MAXSIZE*sizeof(QElemType));
    Q.base[Q.rear] = e;
    Q.rear = (Q.rear+1)%MAXSIZE;
    return 1;
}
Status DeQueue(SqQueue &Q, QElemType &e){
    if(Q.front==Q.rear) return 0;
    e = Q.base[Q.front];
    Q.front = (Q.front+1)%MAXSIZE;
    return 1;
}
Status QueueLength(SqQueue Q){
    return (Q.rear-Q.front+MAXSIZE)%(MAXSIZE);//如果rear追上front,二者相等,这个公式就出错;并且也无法出现长度为MAXSIZE的情况
    //所以浪费front前面的一个位置,如果rear出现在front前面一个位置就判定队列满,即:
    //数据入队前判断:
    //如果(Q.rear+1)%MAXSIZE==Q.front则队列满。扩展队列再入队。

    //判断队列为空则是:Q.front==Q.rear
}
void BFSTraverse(ALGraph G, Status (*Visit)(VertexType)){
    printf("BFS:\n");
    SqQueue Q; InitQueue(Q);
    for(int v=1;v<=G.vexnum;++v) visited[v]=0;
    int v,e,u;
    ArcNode *w;
    for(v=1; v<=G.vexnum; ++v)
        if(!visited[v]){
            visited[v]=1; Visit(v);
            EnQueue(Q,v);   //入队
            while (QueueLength(Q)>0){
                DeQueue(Q,u);
                for(w=G.vertices[u].firstarc; w ; w = w->nextarc)
                    if(!visited[e=w->adjvex]){
                        Visit(e); visited[e]=1;
                        EnQueue(Q,e);
                    }
            }
        }
}

int main(){
    ALGraph G;	//G.vertices[]下标从1开始
    CreateUDG(G);
    Print(G);
    DFSTraverse(G,Visit);
    printf("\n");
    BFSTraverse(G,Visit);
    return 1;
}
/*
 * 无向图G4
8 9

1 2
1 3
2 4
2 5
3 6
3 7
4 8
5 8
6 7

 */

图的连通性问题

非连通无向图(邻接表),的连通分量的生成树,组成深度优先生成森林(存储:孩子兄弟表示法(二叉树))

#include<stdio.h>
#include<stdlib.h>
typedef int Status;
#define INFINITY 9999    //最大值
#define MAX_VERTEX_NUM 20   //最大顶点个数
/*
 * ----------------------------------------
 * 建立 非连通 无向图G3 的 深度优先生成森林
 * 对于非连通图,每个连通分量中的顶点集和遍历时走过的边,一起构成了若干棵生成树,也就是生成森林
 * 以孩子兄弟链表作为生成森林的存储结构
 *
 */

//-----森林
typedef char ElemType;
typedef struct CSNode{
    ElemType data;
    struct CSNode *firstchild, *nextsibling;
}CSNode, *CSTree;
//-----无向图邻接表
typedef int InfoType;
typedef int Status;
typedef struct ArcNode{
    int adjvex;     //该弧所指向的顶点
    struct ArcNode * nextarc;   //指向下一条弧
    InfoType        *info;      //弧相关信息,在这里暂定为权值,如果有需要扩展为结构体
}ArcNode;
typedef struct VertexType{
    int data;   //序号
    char ch;    //代表字母
}VertexType; //顶点信息
typedef struct VNode{   //头节点,存储该点的信息,指向该点的邻边
    VertexType data;    //顶点信息
    ArcNode * firstarc; //该点的第一条邻边
}VNode, AdjList[MAX_VERTEX_NUM];
typedef struct {
    AdjList vertices;   //顺序表,存储每一个顶点的头结点
    int vexnum,arcnum;  //顶点数,边数
    int kind;           //图类型
}ALGraph;
//-----深度优先生成森林
int visited[MAX_VERTEX_NUM];
void DFSTree(ALGraph G, int v, CSTree &T){
    //从第v个顶点出发深度优先遍历G,建立以T为根节点的生成树(二叉树,左孩子右兄弟)
    visited[v]=1;
    ArcNode * w;
    CSTree p,q;
    int e, first=1;
    for(w=G.vertices[v].firstarc; w; w=w->nextarc){
        e = w->adjvex;
        if(!visited[e]){
            p = (CSTree)malloc(sizeof(CSNode));
            * p= {G.vertices[e].data.ch, NULL, NULL};
            if(first) {T->firstchild = p; first=0;} //对于v的孩子e,如果first=1表示e是第一个孩子
            else q->nextsibling=p;                  //否则就是第二个孩子,也就是第一个孩子q的兄弟,链接在q的右边
            q = p;                  //始终使得q是v的且已经链接在树上的孩子中,最后一个孩子
            DFSTree(G,e,q);     //深度优先,顺着这个新孩子p也就是q,建立以q为根节点的生成树
        }
    }
}
void DFSForest(ALGraph G, CSTree &T){
    //建立 非连通 无向图G3 的 深度优先生成森林
    T = NULL;
    int v;
    for(v=1;v<=G.vexnum;++v) visited[v]=0;
    CSTree p, q;
    for(v=1;v<=G.vexnum;++v){
        if(!visited[v]){
            p = (CSTree)malloc(sizeof(CSNode));
            *p = {G.vertices[v].data.ch, NULL, NULL};
            if(!T) T = p;
            else q->nextsibling=p;  //此时p已经是另一棵生成树的根了,所以链接在q的兄弟枝也就是右兄弟
            q = p;  //q始终是最后一个生成树的根,也就是第一棵生成树的最右节点
            DFSTree(G,v,p);     //以一个点为出发点构建一个生成树
        }
    }
}


Status CreateUDG(ALGraph &G){    //构造无向图  G.kind=UDG(2)
    int i, k;
    printf("Init the num:\n");
    scanf("%d %d",&G.vexnum, &G.arcnum);
    for (i = 1; i <= G.vexnum; ++i)         //---------------下标从1开始
        G.vertices[i].firstarc=NULL;

    VertexType vt1,vt2;
    int v1,v2;
    printf("Init the double vertex(%d):\n",G.arcnum);
    for(k=0;k<G.arcnum;++k){
        getchar();
        scanf("%c %c", &vt1.ch, &vt2.ch);
        vt1.data = v1 = (int)vt1.ch-64;
        vt2.data = v2 = (int)vt2.ch-64;
        G.vertices[v1].data = vt1;
        ArcNode *p = (ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex = v2;
        p->nextarc = G.vertices[v1].firstarc;
        G.vertices[v1].firstarc=p;

        G.vertices[v2].data=vt2;
        ArcNode *q = (ArcNode*)malloc(sizeof(ArcNode));
        q->adjvex = v1;
        q->nextarc = G.vertices[v2].firstarc;
        G.vertices[v2].firstarc = q;
    }
    return 1;
}
void Print(ALGraph G){
    printf("Output Adjacency List Graph:\n");
    ArcNode *p;
    for(int i=1;i<=G.vexnum;i++){
        printf("%c %d ",G.vertices[i].data.ch, G.vertices[i].data.data);
        p = G.vertices[i].firstarc;
        while (p){
            printf("->%d ",p->adjvex);
            p = p->nextarc;
        }
        printf("\n");
    }
    printf("\n");
}
Status Visit(ElemType e){
    printf("%c ",e);
    return 1;
}
//中序遍历
Status InOrderTraverse(CSTree T, Status (*visit)(ElemType e)){
    if(!T) return 1;
    if(visit(T->data))
        if(InOrderTraverse(T->firstchild,visit))
            if(InOrderTraverse(T->nextsibling,visit))
                return 1;
    return 0;
}
int main(){
    ALGraph G; CSTree T;
    CreateUDG(G);
    Print(G);
    DFSForest(G,T);
    InOrderTraverse(T,Visit);
    return 1;
}

/*
 * 非连通 无向图G3
13 13
A B
A C
A F
A L
B M
D E
G H
G I
G K
H K
J L
J M
L M

Output Adjacency List Graph:
A 1 ->12 ->6 ->3 ->2
B 2 ->13 ->1
C 3 ->1
D 4 ->5
E 5 ->4
F 6 ->1
G 7 ->11 ->9 ->8
H 8 ->11 ->7
I 9 ->7
J 10 ->13 ->12
K 11 ->8 ->7
L 12 ->13 ->10 ->1
M 13 ->12 ->10 ->2

A L M J B F C D E G K H I
 */
图的连通性问题

连通无向图,的深度优先生成树、广度优先生成树(存储:邻接表)

#include<stdio.h>
#include<stdlib.h>
typedef int Status;
#define INFINITY 9999    //最大值
#define MAX_VERTEX_NUM 20   //最大顶点个数
/*
 * ----------------------------------------
 * 建立 连通 无向图G4 的 深度优先生成树 广度优先生成树
 * 且用邻接表表示生成树 *
 */

//-----无向图邻接表
typedef int InfoType;
typedef int Status;
typedef struct ArcNode{
    int adjvex;     //该弧所指向的顶点
    struct ArcNode * nextarc;   //指向下一条弧
    InfoType        *info;      //弧相关信息,在这里暂定为权值,如果有需要扩展为结构体
}ArcNode;
typedef int VertexType; //顶点信息
typedef struct VNode{   //头节点,存储该点的信息,指向该点的邻边
    VertexType data;    //顶点信息
    ArcNode * firstarc; //该点的第一条邻边
}VNode, AdjList[MAX_VERTEX_NUM];
typedef struct {
    AdjList vertices;   //顺序表,存储每一个顶点的头结点
    int vexnum,arcnum;  //顶点数,边数
    int kind;           //图类型
}ALGraph;
//---------------深度优先生成树 递归---------------
int visited[MAX_VERTEX_NUM+1]={0};
void DFSTree(ALGraph G, int v, ALGraph &T){
    //从第v个顶点出发深度优先遍历G,建立以邻接表表示的生成树
    int e;
    visited[v]=1;
    ArcNode * w,*p;
    for(w=G.vertices[v].firstarc; w; w=w->nextarc){
        e = w->adjvex;
        if(!visited[e]){
            p = (ArcNode*)malloc(sizeof(ArcNode));
            *p = {e,NULL,NULL};
            p->nextarc = T.vertices[v].firstarc;
            T.vertices[v].firstarc = p;
            DFSTree(G,e,T);
        }
    }
}
//---------------广度优先生成树 非递归---------------
#define MAXSIZE MAX_VERTEX_NUM //队列最大长度
typedef VertexType QElemType;
typedef struct {
    QElemType *base;
    int front;
    int rear;
}SqQueue;
//头指针Q.front指向队列第一个元素,尾指针Q.rear指向最后一个元素的下一位置
Status InitQueue(SqQueue &Q){
    Q.base = (QElemType*)malloc(MAXSIZE*sizeof(QElemType));
    Q.front = Q.rear = 0;
    return 1;
}
Status EnQueue(SqQueue &Q, QElemType e){
    if((Q.rear+1)%MAXSIZE==Q.front)
        Q.base = (QElemType*)realloc(Q.base,MAXSIZE*sizeof(QElemType));
    Q.base[Q.rear] = e;
    Q.rear = (Q.rear+1)%MAXSIZE;
    return 1;
}
Status DeQueue(SqQueue &Q, QElemType &e){
    if(Q.front==Q.rear) return 0;
    e = Q.base[Q.front];
    Q.front = (Q.front+1)%MAXSIZE;
    return 1;
}
Status QueueLength(SqQueue Q){
    return (Q.rear-Q.front+MAXSIZE)%(MAXSIZE);//如果rear追上front,二者相等,这个公式就出错;并且也无法出现长度为MAXSIZE的情况
    //所以浪费front前面的一个位置,如果rear出现在front前面一个位置就判定队列满,即:
    //数据入队前判断:
    //如果(Q.rear+1)%MAXSIZE==Q.front则队列满。扩展队列再入队。

    //判断队列为空则是:Q.front==Q.rear
}
void BFSTree(ALGraph G, int v, ALGraph &T){
    //从第v个顶点出发广度优先遍历G,建立以邻接表表示的生成树
    T.vexnum = G.vexnum; T.arcnum = T.vexnum-1;
    int j,e,u,i;
    for(i=1;i<=T.vexnum;++i) {T.vertices[i]={i,NULL}; visited[i]=0;}
    SqQueue Q; InitQueue(Q);
    ArcNode *w,*p;
    for(j=1; j <= G.vexnum; ++j)
        if(!visited[j]){
            visited[j]=1;
            EnQueue(Q, j);   //入队
            while (QueueLength(Q)>0){
                DeQueue(Q,u);
                for(w=G.vertices[u].firstarc; w ; w = w->nextarc)
                    if(!visited[e=w->adjvex]){
                        p = (ArcNode*)malloc(sizeof(ArcNode));
                        *p = {e,NULL,NULL};
                        p->nextarc = T.vertices[u].firstarc;
                        T.vertices[u].firstarc = p;
                        visited[e]=1;
                        EnQueue(Q,e);
                    }
            }
        }
}

Status CreateUDG(ALGraph &G){    //构造无向图  G.kind=UDG(2)
    int i, k;
    printf("Init the num:\n");
    scanf("%d %d",&G.vexnum, &G.arcnum);
    for (i = 1; i <= G.vexnum; ++i)         //---------------下标从1开始
        G.vertices[i].firstarc=NULL;

    VertexType v1,v2;
    printf("Init the double vertex(%d):\n",G.arcnum);
    for(k=0;k<G.arcnum;++k){
        getchar();
        scanf("%d %d", &v1, &v2);
        G.vertices[v1].data = v1;
        ArcNode *p = (ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex = v2;
        p->nextarc = G.vertices[v1].firstarc;
        G.vertices[v1].firstarc=p;

        G.vertices[v2].data=v2;
        ArcNode *q = (ArcNode*)malloc(sizeof(ArcNode));
        q->adjvex = v1;
        q->nextarc = G.vertices[v2].firstarc;
        G.vertices[v2].firstarc = q;
    }
    return 1;
}
void Print(ALGraph G){
    printf("Output Adjacency List Graph:\n");
    ArcNode *p;
    for(int i=1;i<=G.vexnum;i++){
        printf("%d ", G.vertices[i].data);
        p = G.vertices[i].firstarc;
        while (p){
            printf("->%d ",p->adjvex);
            p = p->nextarc;
        }
        printf("\n");
    }
    printf("\n");
}
int main(){
    //连通图的生成树
    ALGraph G,M;
    CreateUDG(G);
    Print(G);

    //深度优先生成树
    M.vexnum = G.vexnum; M.arcnum = M.vexnum-1;
    for(int i=1;i<=M.vexnum;++i) M.vertices[i]={i,NULL};
    DFSTree(G,1,M);
    Print(M);
    printf("\n");

    //广度优先生成树
    BFSTree(G, 1, M);
    Print(M);
    return 1;
}

/*
 * 连通无向图G4
8 9

1 2
1 3
2 4
2 5
3 6
3 7
4 8
5 8
6 7
 */
Output Adjacency List Graph:
1 ->3 ->2
2 ->5 ->4 ->1
3 ->7 ->6 ->1
4 ->8 ->2
5 ->8 ->2
6 ->7 ->3
7 ->6 ->3
8 ->5 ->4

Output Adjacency List Graph:
1 ->2 ->3
2 ->5
3 ->7
4
5 ->8
6
7 ->6
8 ->4


Output Adjacency List Graph:
1 ->2 ->3
2 ->4 ->5
3 ->6 ->7
4
5 ->8
6
7
8


Process finished with exit code 1

图的连通性问题

有向图的强连通分量

在这里插入代码片
无向网的最小生成树 Prim 算法
#include<stdio.h>
typedef int Status;
//--------------数组(邻接矩阵)表示法  (二维数组)--------------
#define INFINITY 9999    //最大值
#define MAX_VERTEX_NUM 20   //最大顶点个数
typedef enum{DG, DN, UDG, UDN}GraphKind;    //{有向图,有向网,无向图,无向网}
typedef struct InfoType{    //弧的信息  没用到
    int start;
    int end;
}InfoType;
typedef struct VertexType{  //顶点信息
    int pos;
}VertexType;

typedef int VRType;
typedef struct ArcCell{
    VRType adj;         //VRType顶点关系类型。对无权图,用1或0表示相邻否
                        //对带权图,则为权值类型
    InfoType * info;    //该弧相关信息的指针 没用到
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct {
    VertexType vexs[MAX_VERTEX_NUM];    //顶点向量 没用到
    AdjMatrix arcs;                     //邻接矩阵  二维数组,存储点和点之间的关系:有无边,有无权
    int vexnum, arcnum;                 //当前顶点数,弧数
    GraphKind kind;                     //图的种类
}MGraph;

/*
 * 构造无向连通网UDN 的 最小代价生成树(简称最小生成树)
 *
 * MST性质:
 *
 * PRIM算法复杂度O(n*n),与边无关,适合于边稠密的网
 * Kruskal算法复杂度O(eloge)(用MFSet类型描述树),适合于边稀疏的网
 *                  O(n+e)(用邻接表描述树)
 */

//-------------------------PRIM算法------------------------------
//记录 从顶点集U到V-U的代价最小边  的辅助数组定义
typedef struct {
    VertexType adjvex;
    VRType lowcost;
}closedge[MAX_VERTEX_NUM];
int minimum(closedge close, int n){
    int min = INFINITY, j;
    for(int i=2;i<=n;i++){
        if (min>close[i].lowcost && close[i].lowcost) { min = close[i].lowcost; j=i; }
    }
    return j;
}
void MiniSpanTree_PRIM(MGraph G, VertexType u){
    //用Prim算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边
    int k = u.pos, j, i;

    //辅助数组初始化
    closedge close;
    for(j=1;j<=G.vexnum;j++)
        if(j!=k) close[j]={u, G.arcs[k][j].adj};
    close[k].lowcost = 0;

    for(i=2;i<=G.vexnum;i++){//选择其余vexnum-1个顶点,构造vexnum-1条边
        k = minimum(close, G.vexnum);   //寻找V-U中,与U中顶点 权重最小的那个顶点
        printf("[%d, %d]",close[k].adjvex.pos,k);
        close[k].lowcost=0;
        for(j=2;j<=G.vexnum;j++)        //由于加入了新顶点,更新close
            if(G.arcs[k][j].adj<close[j].lowcost) close[j] = {k, G.arcs[k][j].adj};
    }
}

//--------------------邻接矩阵构造无向网--------------------------------------
Status InputInfo(InfoType * info){
    return 1;
}//输入弧的信息,没用到
Status CreateUDN(MGraph &G){
    //采用二维数组(邻接矩阵),构造有无向网G
    int IncInfo, i, j, k, w;
    printf("Init the num:\n");
    scanf("%d %d %d",&G.vexnum, &G.arcnum, &IncInfo);   //顶点数,弧数,IncInfo为0则弧不含其他信息
    for(i=1;i<=G.vexnum;i++)
        for(j=1;j<=G.vexnum;j++) G.arcs[i][j]={INFINITY, NULL};  //初始化顶点和顶点的关系,弧的信息
    printf("Init the vertex and the weight(%d):\n",G.arcnum);
    for(k=1; k<=G.arcnum; k++){
        scanf("%d %d %d",&i, &j, &w);
        G.arcs[i][j].adj=w;     //构造二维数组以及权重
        G.arcs[j][i]=G.arcs[i][j];

        if(IncInfo) InputInfo(G.arcs[i][j].info);
    }
    return 1;
}
Status CreateGraph(MGraph &G){
    //采用二维数组(邻接矩阵),构造图G
    printf("Input the kind of Graph: \n");
    scanf("%d",&G.kind);
    switch (G.kind) {
        case 0:  //有向图
        case 1:  //有向网
        case 2:  //无向图
        case 3:return CreateUDN(G); //无向网:有权重
        default: return 0;
    }
}
void Print(MGraph G){
    printf("Output:\n");
    for(int i=1;i<=G.vexnum;i++){
        for(int j=1;j<=G.vexnum;j++)
            if(G.arcs[i][j].adj!=INFINITY) printf("%-3d",G.arcs[i][j].adj);
            else printf("%s ","OO");
        printf("\n");
    }
    printf("\n");
}

//--------------------------------------------------------
int main(){
    MGraph G;
    CreateGraph(G);
    Print(G);
    VertexType u={1};
    MiniSpanTree_PRIM(G,u);
    return 1;
}

/*
3
6 10 0

1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
4 6 2
5 6 6
 */

无向网的最小生成树 Kruskal 算法
#include<stdio.h>
#include<stdlib.h>
typedef int Status;
#define INFINITY 9999    //最大值
#define MAX_VERTEX_NUM 20   //最大顶点个数

//--------------------------邻接表存储无向网-----------------------------
typedef int VertexType;
typedef struct ArcNode{
    int adjvex;     //该弧所指向的顶点
    struct ArcNode * nextarc;   //指向下一条弧
    int info;      //弧相关信息,在这里为权值
}ArcNode;
typedef struct VNode{   //头节点,存储该点的信息,指向该点的邻边
    VertexType data;    //顶点信息
    ArcNode * firstarc; //该点的第一条邻边
}VNode, AdjList[MAX_VERTEX_NUM];
typedef struct {
    AdjList vertices;   //顺序表,存储每一个顶点的头结点
    int vexnum,arcnum;  //顶点数,边数
    int kind;           //图类型
}ALGraph;

//---------
Status CreateUDN(ALGraph &G){    //构造无向图  G.kind=UDG(2)
    int i, k;
    printf("Init the num:\n");
    scanf("%d %d",&G.vexnum, &G.arcnum);
    for (i = 1; i <= G.vexnum; ++i)         //---------------下标从1开始
        G.vertices[i].firstarc=NULL;

    VertexType v1,v2;
    int weight;
    printf("Init the double vertex(%d):\n",G.arcnum);
    for(k=1;k<=G.arcnum;++k){
        getchar();
        scanf("%d %d %d", &v1, &v2, &weight);
        G.vertices[v1].data = v1;
        ArcNode *p = (ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex = v2;
        p->info  = weight;
        p->nextarc = G.vertices[v1].firstarc;
        G.vertices[v1].firstarc=p;

        G.vertices[v2].data=v2;
        ArcNode *q = (ArcNode*)malloc(sizeof(ArcNode));
        q->adjvex = v1;
        q->info = weight;
        q->nextarc = G.vertices[v2].firstarc;
        G.vertices[v2].firstarc = q;
    }
    return 1;
}
void Print(ALGraph G){
    printf("Output Adjacency List Graph:\n");
    ArcNode *p;
    for(int i=1;i<=G.vexnum;i++){
        printf("%d ",G.vertices[i].data);
        p = G.vertices[i].firstarc;
        while (p){
            printf("->[%d, %d] ",p->adjvex,p->info);
            p = p->nextarc;
        }
        printf("\n");
    }
    printf("\n");
}

//---------------------------MFSet构造非连通图----------------------------------
#define MAX_TREE_SIZE 20
typedef char TElemType;
typedef struct PTNode{
    TElemType data;
    int parent;
}PTNode;
typedef struct {
    PTNode nodes[MAX_TREE_SIZE];
    int r,n;    //根的位置,节点数
}PTree;
typedef PTree MFSet;
//S.nodes[i]和S.nodes[j]分别为S中互不相交的两个子集Si和Sj的根节点。
//求并集
int merge_mfset(MFSet &S, int i, int j){     //复杂度为O(1)
    if(i<1 || i> S.n || j<1 || j>S.n) return 0;
    S.nodes[i].parent = j;
    return 1;
}
//改进的find操作,进一步减少确定元素所在集合的时间
//当所查找的元素不在树的第二层时,在算法中增加一个“压缩路径”功能,即讲所有从根到元素i路径上的元素都变成根的孩子
int fix_mfset(MFSet &S, int i){
    //确定i所在子集,并将从i到根节点路径上所有节点都变成根节点的孩子节点
    if(i<1 || i>S.n) return 0;
    int j, k, t;
    for(j=i;S.nodes[j].parent>0;j=S.nodes[j].parent);
    for(k=i;k!=j;k=t){
        t = S.nodes[k].parent;
        S.nodes[k].parent = j;
    }
    return j;
}
int Initial(MFSet &S, int n){
    S.r = 0; S.n = n;
    for(int i=0;i<=S.n;i++){
        S.nodes[i].parent = -1;
        S.nodes[i].data = i;
    }
}
//-------------------------Kruskal算法------------------------------
/*
 *
 * 步骤:
 * 1初始化MFSet,每个顶点自成一个连通分量。
 * 1.5对所有的边按照代价进行递增排序
 * 2在所有边中选择代价最小的边,如果它的顶点分别落在不同的连通分量中,就把这条边加入连通分量T中
 *                          否则选择下一条代价最小的边
 * 3直到所有顶点都在同一连通分量中
 *
 * 复杂度:在选择代价最小的边时:如果采用堆,每次寻找只需O(loge)
 *                          如果排序,采用冒泡,O(e*e)
 *                          如果每次都从头到尾扫描,O(e)
 *         在生成树的每个连通分量,使用MFSet,寻找顶点属于哪个连通分量,合并两个连通分量,复杂度O(eloge)
 */

//寻找G中代价最小的边
void minweight(ALGraph G, int node[]){
    int w=INFINITY,s,e;
    ArcNode *p;
    for(int i=1;i<=G.vexnum;i++){
        p = G.vertices[i].firstarc;
        while (p){
            if(p->info<w){
                w = p->info;
                s = i;
                e = p->adjvex;
            }
            p = p->nextarc;
        }
    }
    *node = s;
    *(node+1) = e;
    *(node+2) = w;
}
void MiniSpanTree_Kruskal(ALGraph G, MFSet &S){
    for(int num=1;num<G.vexnum;num++){
        int node[3];
        minweight(G, node);     //找出代价最小的一条边,返回给node: [边的起点,边的终点,边的代价]
        int i = fix_mfset(S,node[0]);
        int j = fix_mfset(S,node[1]);
        while(i==j){    //如果这条边的两个端点属于同一个连通分量,把代价重置为最大,重新寻找最小代价边
            ArcNode *p = G.vertices[node[0]].firstarc;
            while (p){
                if(p->adjvex==node[1]){
                    p->info = INFINITY;
                    break;
                }
                p = p->nextarc;
            }
            p = G.vertices[node[1]].firstarc;
            while (p){
                if(p->adjvex==node[0]){
                    p->info = INFINITY;
                    break;
                }
                p = p->nextarc;
            }
            minweight(G, node);
            i = fix_mfset(S,node[0]);
            j = fix_mfset(S,node[1]);
        }
        printf("[%d %d |%d]",node[0],node[1],node[2]);
        merge_mfset(S,i,j);
    }
}

//----------------------------------------
int main(){
    ALGraph G;
    CreateUDN(G);
    Print(G);

    MFSet S;
    Initial(S,G.vexnum);
    MiniSpanTree_Kruskal(G,S);
    return 1;
}
/*
6 10

1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
4 6 2
5 6 6
 */
有向无环图 及其应用:拓扑排序
#include<stdio.h>
#include<stdlib.h>
/*
 * 拓扑排序:(有向无环图)
 * 1在有向图中选择一个没有前驱的顶点并且输出之
 * 2从图中删除该顶点和所有以它为尾的弧
 * 3重复12直到输出所有顶点;或者当前图中不存在无前驱的顶点为止(表示有向图中有环)
 *
 * 实现:
 * 采用邻接表存储有向图;在头结点中增加一个存放顶点入度的数组
 * 删除顶点以及以它为尾的弧,可以当作 弧头顶点的入度减一
 * 为了避免重复检测入度为0的顶点,设置一栈暂存所有入度为0的顶点
 *
 * 复杂度:
 *      对n个顶点e条边的有向图:
 *      构造有向图、建立各顶点的入度数组 O(e)
 *      建立零入度顶点栈 O(n)
 *      拓扑排序过程中:每个顶点进栈一次出栈一次,入度减一的操作共执行e次
 *  总复杂度:O(n+e)
 *
 *
 * 拓扑排序的另一方法:
 * 使用DFS,最先退出DFS的就是第一个出度为0的点,也就是拓扑排序序列的最后一个顶点。
 * 因此,按退出DFS的先后记录下 逆向的拓扑有序序列
 *
 */

//----------------------------构造有向图的邻接表------------------------------
typedef int Status;
#define INFINITY 9999    //最大值
#define MAX_VERTEX_NUM 20   //最大顶点个数
typedef struct ArcNode{
    int adjvex;     //该弧所指向的顶点
    struct ArcNode * nextarc;   //指向下一条弧
}ArcNode;
typedef int VertexType; //顶点信息
typedef struct VNode{   //头节点,存储该点的信息,指向该点的邻边
    VertexType data;    //顶点信息
    ArcNode * firstarc; //该点的第一条邻边
}VNode, AdjList[MAX_VERTEX_NUM+1];
typedef struct {
    AdjList vertices;   //顺序表,存储每一个顶点的头结点
    int vexnum,arcnum;  //顶点数,边数
    int kind;
    int indegree[MAX_VERTEX_NUM];   //存放顶点入度
}ALGraph;
Status CreateDG(ALGraph &G){    //构造有向图  G.kind=UDG(0)
    int i, k;
    printf("Init the num:\n");
    scanf("%d %d",&G.vexnum, &G.arcnum);
    for (i = 1; i <= G.vexnum; ++i){
        G.vertices[i].firstarc=NULL;
        G.vertices[i].data=i;
        G.indegree[i]=0;
    }        //----下标从1开始

    VertexType v1,v2;
    printf("Init the double vertex(%d):\n",G.arcnum);
    for(k=1;k<=G.arcnum;++k){
        scanf("%d %d", &v1, &v2);
        G.vertices[v1].data=v1;
        ArcNode *p = (ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex = v2;
        p->nextarc = G.vertices[v1].firstarc;
        G.vertices[v1].firstarc=p;
        G.indegree[v2]++;
    }
    return 1;
}
void Print(ALGraph G){
    printf("Output Adjacency List Graph:\n");
    ArcNode *p;
    for(int i=1;i<=G.vexnum;i++){
        printf("%d ",G.vertices[i].data);
        p = G.vertices[i].firstarc;
        while (p){
            printf("->%d ",p->adjvex);
            p = p->nextarc;
        }
        printf("\n");
    }
    printf("\n");
}
//------------------------------Stack存放入度为0的顶点-------------------------------------------
#define SElemType VertexType
#define STACK_INIT_SIZE MAX_VERTEX_NUM
typedef struct{
    SElemType * base;
    SElemType * top;
    int stacksize;  //当前已分配的空间
}SqStack;
Status InitStack(SqStack &S){
    S.base = (SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType));
    S.top = S.base;
    S.stacksize = STACK_INIT_SIZE;
    return 1;
}
Status Push(SqStack &S, SElemType e){
    if(S.top-S.base>=STACK_INIT_SIZE)
        S.base = (SElemType *)realloc( S.base, (STACK_INIT_SIZE+S.stacksize)*sizeof(SElemType));
    *S.top++ = e;
    return 1;
}
Status Pop(SqStack &S, SElemType &e){
    if(S.top==S.base) return 0;
    e = *--S.top;
    return 1;
}
Status StackEmpty(SqStack S){
    if(S.top==S.base) return 1;
    return 0;
}
//--------------------------TopologicalSort----------------------------------
Status TopologicalSort(ALGraph G){
    SqStack S;
    InitStack(S);   //初始化栈,把当前入度为0的顶点入栈
    for(int i=1;i<=G.vexnum;++i) if(!G.indegree[i]) Push(S,i);

    int count=0; // 统计输出的顶点数

    VertexType e, k; ArcNode *p;
    while(!StackEmpty(S)){
        Pop(S,e); printf("%d ->",e); count++;
        for(p = G.vertices[e].firstarc; p; p = p->nextarc){
            k = p->adjvex;
            if(!(--G.indegree[k])) Push(S,k);   //如果入度减一之后成0,入栈
        }
    }
    if(count<G.vexnum) return 0;    //图中所有顶点没有全部输出,存在环
    else return 1;
}

//----------------------------------------------------------------------------
int main(){
    ALGraph G;
    CreateDG(G);
    Print(G);

    TopologicalSort(G);
    return 1;
}
/*
6 8

1 2
1 3
1 4
3 2
3 5
4 5
6 4
6 5
 */
有向无环图 及其应用:关键路径
#include<stdio.h>
#include<stdlib.h>

/*
 * --------------------------------------------关键路径-----------------------------------------------------
 * 分析:
 * 由于在AOE中有些活动可以并行进行,所以完成工程的最短时间是从开始点到完成点的最长路径的长度(值路径上各活动的持续时间之和)
 * 路径最长的叫做:关键路径
 * 假设开始点是v1,从v1到vi的最长路径长度叫做事件vi的最早发生时间。这个时间决定了所有以vi为尾的弧表示的活动的最早开始时间。
 *
 * 用e(i)表示活动ai的最早开始时间
 * 用l(i)表示活动ai的最迟开始时间,这是在不推迟整个工程完成的前提下,活动ai最迟必须开始进行的时间
 * 二者之差l(i)-e(i)意味着完成活动ai的时间余量
 * 称l(i)=e(i)的活动叫做关键活动。显然,关键路径上的活动都是关键活动,因此提前完成非关键活动并不能加快工程的进度
 *
 * 因此,分析关键路径的目的就是找出关键活动,辨别关键活动就是要找出l(i)=e(i)的活动。
 * 首先应该求出事件点的最早发生时间ve(j)和最迟发生时间vl(j)
 *      假设活动ai由弧<j,k>表示,其持续时间积为dut(<j,k>),则有如下关系:
 *      e(i) = ve(j)
 *      l(i) = vl(k)-dut(<j,k>)
 *
 *      接下来分两步进行求出ve(j)和vl(j)
 *      1从ve(0)开始向前递推:
 *          ve(j) = MAX{ ve(i)+dut(<i,j>) }, <i,j>属于T,j=1,2,...,n-1
 *              其中,T是所有以第j个顶点为头的弧的集合
 *      2从vl(n-1) = ve(n-1)起往回递推
 *          vl(i) = MIN{ vl(j)-dut(<i,j>) }, <i,j>属于S,i=n-2,...,1,0
 *              其中,S是所有以第i个顶点为尾的弧的集合
 *
 *      这两个公式必须分别在拓扑有序和逆拓扑有序的前提下进行。也就是说,ve(j-1)必须在vj的所有前驱的最早发生时间求出之后才能确定
 *      vl(j-1)必须在vj的所有后继的最迟发生时间求出之后才能确定
 *      因此必须在拓扑排序的基础上计算ve(j-1)和vl(j-1)
 *
 * 求关键路径的算法:
 *      1.输入e条弧<j,k>,建立AOE-网的存储结构
 *      2.从源点v1出发,令ve(1)=0,按拓扑有序求得其余各顶点的最早发生时间ve(i)(2<=i<=n)
 *          如果得到的拓扑序列中顶点个数小于网中顶点个数,说明网中存在环,不能求关键路径,算法终止;
 *      3.初始化vl(i)=ve(n)(1<=i<=n).从汇点vn出发,令vl(n)=ve(n),按逆拓扑有序求其余各顶点的最迟发生时间vl(i)(2<=i<=n-1)
 *      4.根据各顶点的ve和vl值,根据e(s)和l(s)公式,求每条弧s的最早开始时间e(s)和最迟开始时间l(s)。
 *          若某条弧满足e(s)=l(s)则为关键活动
 * 复杂度:O(n+e)
 * 关键活动的速度提高是有限度的,只有在不改变网的关键路径的情况下,提高关键活动的速度才是有效的
 * 另一方面,若网中有几条关键路径,必须同时提高几条关键路径上的活动才可以使整个工程工期缩短
 *
 * 对拓扑排序的修改:
 *      1在拓扑排序之前设初值,ve(i)=0(1<=i<=n)
 *      2在算法中增加一个计算vj的直接后继vk的最早发生时间的操作:
 *          若ve(j)+dut(<j,k>)>ve(k) 则ve(k)=ve(j)+dut(<j,k>)
 *      3为了能按逆拓扑有序序列的顺序计算各顶点的vl值,需要记下在拓扑排序的过程中求得的拓扑有序序列,所以通过设置一个栈来记录
 *          这样在计算求得各顶点的ve值之后,从栈顶至栈底便为逆拓扑有序序列
 *
 */

//----------------------------构造有向图的邻接表------------------------------
typedef int Status;
#define INFINITY 9999    //最大值
#define MAX_VERTEX_NUM 20   //最大顶点个数
typedef struct ArcNode{
    int adjvex;     //该弧所指向的顶点
    struct ArcNode * nextarc;   //指向下一条弧
    int info;       //边权重
}ArcNode;
typedef int VertexType; //顶点信息
typedef struct VNode{   //头节点,存储该点的信息,指向该点的邻边
    VertexType data;    //顶点信息
    ArcNode * firstarc; //该点的第一条邻边
}VNode, AdjList[MAX_VERTEX_NUM+1];
typedef struct {
    AdjList vertices;   //顺序表,存储每一个顶点的头结点
    int vexnum,arcnum;  //顶点数,边数
    int kind;
    int indegree[MAX_VERTEX_NUM];   //存放顶点入度
}ALGraph;
Status CreateDG(ALGraph &G){    //构造有向图  G.kind=UDG(0)
    int i, k;
    printf("Init the num:\n");
    scanf("%d %d",&G.vexnum, &G.arcnum);
    for (i = 1; i <= G.vexnum; ++i){
        G.vertices[i].firstarc=NULL;
        G.vertices[i].data=i;
        G.indegree[i]=0;
    }        //----下标从1开始

    VertexType v1,v2;
    int weight;
    printf("Init the double vertex(%d):\n",G.arcnum);
    for(k=1;k<=G.arcnum;++k){
        scanf("%d %d %d", &v1, &v2, &weight);
        G.vertices[v1].data=v1;
        ArcNode *p = (ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex = v2;
        p->info = weight;
        p->nextarc = G.vertices[v1].firstarc;
        G.vertices[v1].firstarc=p;
        G.indegree[v2]++;

    }
    return 1;
}
void Print(ALGraph G){
    printf("Output Adjacency List Graph:\n");
    ArcNode *p;
    for(int i=1;i<=G.vexnum;i++){
        printf("%d ",G.vertices[i].data);
        p = G.vertices[i].firstarc;
        while (p){
            printf("->[%d | %d] ",p->adjvex, p->info);
            p = p->nextarc;
        }
        printf("\n");
    }
    printf("\n");
}
//------------------------------Stack存放0入度顶点、拓扑有序序列-------------------------------------------
#define SElemType VertexType
#define STACK_INIT_SIZE MAX_VERTEX_NUM
typedef struct{
    SElemType * base;
    SElemType * top;
    int stacksize;  //当前已分配的空间
}SqStack;
Status InitStack(SqStack &S){
    S.base = (SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType));
    S.top = S.base;
    S.stacksize = STACK_INIT_SIZE;
    return 1;
}
Status Push(SqStack &S, SElemType e){
    if(S.top-S.base>=STACK_INIT_SIZE)
        S.base = (SElemType *)realloc( S.base, (STACK_INIT_SIZE+S.stacksize)*sizeof(SElemType));
    *S.top++ = e;
    return 1;
}
Status Pop(SqStack &S, SElemType &e){
    if(S.top==S.base) return 0;
    e = *--S.top;
    return 1;
}
Status StackEmpty(SqStack S){
    if(S.top==S.base) return 1;
    return 0;
}
//----------------------------------------TopologicalOrder----------------------------------------------
int ve[MAX_VERTEX_NUM];
int vl[MAX_VERTEX_NUM];
Status TopologicalOrder(ALGraph G, SqStack &T){
    //用邻接表存储有向网G,求各顶点时间的最早发生时间ve(i)
    //T为拓扑序列顶点栈
    //S为0入度顶点栈
    //各顶点的入度已经在构造G的过程中写入数组G.indegree[]
    //初始化ve数组全为0
    SqStack S;
    InitStack(S); InitStack(T);
    for(int i=1;i<=G.vexnum;++i){
        if(!G.indegree[i]) Push(S,i);  //当前0入度顶点入栈
        ve[i]=0;    //初始化ve
    }

    int count = 0, j, k; ArcNode *p;

    while(!StackEmpty(S)){
        Pop(S, j); Push(T, j); count++;
        for(p=G.vertices[j].firstarc; p; p = p->nextarc){
            k = p->adjvex;
            if(!(--G.indegree[k])) Push(S,k);   //如果入度减一之后成0,入栈S
            if(ve[j] + p->info > ve[k]) ve[k] = ve[j]+p->info;  //修正弧头顶点的最早发生时间
        }
    }
    if(count<G.vexnum) return 0;
    else return 1;
}
Status CriticalPath(ALGraph G){
    //G为有向网,T是逆拓扑序列
    //输出G的各项关键活动
    SqStack T;
    if(!TopologicalOrder(G,T)) return 0;
    for(int i=1;i<=G.vexnum;i++) vl[i] = ve[G.vexnum]; //初始化最迟发生时间

    int j, k, dut;
    ArcNode *p;
    while(!StackEmpty(T))  //按逆拓扑序列求出各顶点的vl
        for(Pop(T,j), p = G.vertices[j].firstarc; p; p=p->nextarc){
            k = p->adjvex; dut = p->info;
            if (vl[k]-dut<vl[j]) vl[j] = vl[k]-dut;
        }

    printf("Vertex ve vl\n");
    for(j=1;j<=G.vexnum;j++) printf("%-6d %-2d %-2d\n",j,ve[j],vl[j]);

    //输出活动以及最早最晚开始时间
    printf("\nActivity [e , l ] l-e\n");
    for(j=1;j<=G.vexnum;j++)
        for(p=G.vertices[j].firstarc; p; p=p->nextarc){
            k=p->adjvex; dut = p->info;
            char tag = (ve[j]==vl[k]-dut)?'*':' ';
            printf("[%d -> %d] [%-2d, %-2d] %c\n",j,k,ve[j],vl[k]-dut,tag);//按照活动发生时间公式:最早、最晚
        }
}

int main(){
    ALGraph G;
    CreateDG(G);
    Print(G);
    CriticalPath(G);
    return 1;
}
/*
图7.29
9 11

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

图7.30
6 8

1 2 3
1 3 2
2 4 2
2 5 3
3 4 4
3 6 3
4 6 2
5 6 1
 */
最短路径:从某个源点到其余各点的最短路径Dijkstra算法
最短路径:每一对顶点之间的最短路径Floyd算法
#include<stdio.h>

//------------------------邻接矩阵存储有向网-------------------------------------------
typedef int Status;
#define INFINITY 9999    //最大值
#define MAX_VERTEX_NUM 15   //最大顶点个数
typedef enum{DG, DN, UDG, UDN}GraphKind;    //{有向图,有向网,无向图,无向网}
typedef int VRType;
typedef int InfoType;
typedef struct ArcCell{
    VRType adj;         //VRType顶点关系类型。对无权图,用1或0表示相邻否
                    //对带权图,则为权值类型
    InfoType * info;    //该弧相关信息的指针
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct {
    AdjMatrix arcs;                     //邻接矩阵  二维数组,存储点和点之间的关系:有无边,有无权
    int vexnum, arcnum;                 //当前顶点数,弧数
    GraphKind kind;                     //图的种类
}MGraph;
Status CreateDN(MGraph &G){
    //采用二维数组(邻接矩阵),构造有向网G
    int i, j, k, w;
    printf("Init the num:\n");
    scanf("%d %d",&G.vexnum, &G.arcnum);
    for(i=1;i<=G.vexnum;i++)
        for(j=1;j<=G.vexnum;j++) G.arcs[i][j]={INFINITY, NULL};  //初始化顶点和顶点的关系,弧的信息
    printf("Init the vertex and the weight(%d):\n",G.arcnum);
    for(k=1; k<=G.arcnum; k++){
        scanf("%d %d %d",&i, &j, &w);
        G.arcs[i][j].adj=w;     //构造二维数组以及权重
    }
    return 1;
}
void Print(MGraph G){
    printf("Output:\n");
    for(int i=1;i<=G.vexnum;i++){
        for(int j=1;j<=G.vexnum;j++)
            if(G.arcs[i][j].adj!=INFINITY) printf("%-3d",G.arcs[i][j].adj);
            else printf("%s ","OO");
        printf("\n");
    }
    printf("\n");
}

//-----------------------------------------最短路径---------------------------------------------------------
void ShortestPath_DIJ(MGraph G, int v0, int (*P)[MAX_VERTEX_NUM], int D[]){
    //P[v][w]为1表示v0到v当前求得的最短路径包括顶点w
    //用Dijkstra算法求有向网G的v0顶点到其余顶点v的最短路径P[v][]以及带权长度D[v]
    //复杂度O(n*n)
    int final[G.vexnum];    //final[v]为1表示已经找到v0到v的最短路径
    int v, w, i, min, j;

    for(v=1;v<=G.vexnum;++v){
        final[v] = 0;
        D[v] = G.arcs[v0][v].adj;
        for(w=1;w<=G.vexnum;++w) P[v][w]=0;
        if(D[v]<INFINITY){ P[v][v0]=1; P[v][v]=1; }
    }
    D[v0]=0; final[v0]=1;

    for(i=2;i<=G.vexnum;++i){
        min = INFINITY;
        for(w=1;w<=G.vexnum;++w)
            if(!final[w])
                if(D[w]<min) { min=D[w]; v=w;}    //在当前还没确定最短路径的顶点中,找到距离v0最近的顶点v
        final[v]=1;

        for(w=1;w<=G.vexnum;++w)    //更新D:判断能否通过v来使得v0到其他点的距离更近
            if(!final[w] && (min+G.arcs[v][w].adj<D[w])){
                D[w] = min+G.arcs[v][w].adj;
                for(j=1;j<=G.vexnum;j++) P[w][j]=P[v][j];
                P[w][w]=1;
            }
    }
}

void ShortestPath_FLOYD(MGraph G, int (*P)[MAX_VERTEX_NUM][MAX_VERTEX_NUM], int (*D)[MAX_VERTEX_NUM]){
    //用Floyd算法求有向网G中各对顶点v和w之间的最短路径P[v][w]以及带权长度D[v][w]
    //若P[v][w][u]为1表示u使v到w当前求得最短路径上的顶点
    //复杂度O(n*n*n)
    int  v,w,u,i;
    for(v=1;v<=G.vexnum;v++)
        for(w=1;w<=G.vexnum;w++){
            D[v][w] = G.arcs[v][w].adj; //初始化距离
            for(u=1;u<=G.vexnum;u++) P[v][w][u]=0;
            if(D[v][w]<INFINITY) P[v][w][v] = P[v][w][w] = 1;
        }

    for(u=1;u<=G.vexnum;u++)
        for(v=1;v<=G.vexnum;v++)
            for(w=1;w<=G.vexnum;w++){
                if(D[v][u]+D[u][w]<D[v][w]){
                    D[v][w] = D[v][u]+D[u][w];
                    for(i=1;i<=G.vexnum;++i) P[v][w][i] = P[v][u][i] || P[u][w][i];
                }
            }
}

void Print(int (*P)[MAX_VERTEX_NUM], int n){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            if(P[i][j]!=INFINITY) printf("%-3d",P[i][j]);
            else printf("%s ","OO");
        printf("\n");
    }
    printf("\n");
}
int main(){
    MGraph G;
    CreateDN(G);
    Print(G);

    int P[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
    int D[G.vexnum];
    ShortestPath_DIJ(G, 1, P, D);
    printf("Dijkstra:\n");
    for(int i=1;i<=G.vexnum;++i) printf("%d ",D[i]);
    printf("\n");
    Print(P, G.vexnum);

    int PP[MAX_VERTEX_NUM][MAX_VERTEX_NUM][MAX_VERTEX_NUM];
    int DD[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
    ShortestPath_FLOYD(G,PP,DD);
    printf("Floyd:\n");
    Print(DD,G.vexnum);
    return 1;
}
/*
带权有向图G6
6 8

1 3 10
1 5 30
1 6 100
2 3 5
3 4 50
4 6 10
5 4 20
5 6 60

图7.36
3 5

1 2 4
1 3 11
2 1 6
2 3 2
3 1 3
 */
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在学C++

2角不嫌多,1角不嫌少

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值