图(一)

  小编也是最近再学 图。

图是一种较线性表和树更为复杂的数据结构。在线性表中,每个中间节点只有一个前驱或者后驱节点。树结构,数据家具元素之间有着明显的层次。而图中,数据元素的关系可是是任意的。

  较容易记错的地方。在有向图中,<v,w>表示从v到w的一条弧,v称为弧尾,w称为弧头。

1 数组表示法。采用二维数组存储距离(权值)。当0元素较多的时候,就会造成内存空间的较多浪费。

#include<iostream>
using namespace std;
//串的定长存储表示
#define MAX_VEX 30
#define ERROR 0
#define OK 1
#define INFINITY MAX_VAL
typedef int InfoType;
//图  多对多的关系任意元素之间都可能相关
//G=(V,E)   V顶点  E图的弧
//生成树 极小连通子图 n个顶点 n-1条边

//临界矩阵的标识(数组) 1 为邻接 0不为
typedef enum{DG,AG,WDG,WAG}GraphKind;
typedef struct ArcType   //定义E
{
    VexType vex1, vex2;  //弧或边所衣服的两个顶点
    ArcValType ArcVal;  //弧或边的权值
    ArcInfoType ArcInfo;   // 弧或边的其他信息
}Arctype; // 弧或边的结构定义
typedef struct  //定义V
{
    GraphKind kind; //图的种类标值
    int vexnum, arcnum;  //图的当前顶点数和弧数
    VexType vexs[MAX_VEX];  //顶点向量
    ArcType adj[MAX_VEX][MAX_VEX];
}MGraph;  //图的结构定义
//图的创建
MGraph* Creat_Graph(MGraph* G)
{
    cout << "请输入图的种类标值" << endl;
    cin >> &G->kind;
    G->vexnum = 0;   //初始化顶点的个数
    return G;
}
//图的顶点定位
int LocateVex(MGraph* G, VexType* vp)
{
    int k;
    for (k = 0; k < G->vexnum; k++)
    {
        if (G->vexs[k] == *vp) return k;
        return -1;  //图中无此节点
    }
}
//向中增加节点  类似与线性表中末尾增加一个数据元素
int AddVertex(MGraph* G, VexType* vp)
{
    int k, j;
    if (G->vexnum >= MAX_VEX)
        cout << "Vextex Overflow!" << endl; return -1;
    if (LocateVex(G, vp) != -1)
        cout << "Vextex has existed!" << endl; return -1;
    k = G->vexnum; G->vexs[G->vexnum++] = *vp;
    if (G->kind == DG || G->kind == AG)   //不带权的有向图或无向图
        for (j = 0; j < G->vexnum; j++)
            G->adj[j][k].ArcVal = G->adj[k][j].ArcVal=0;
    else    //带权的有向图或无向图
    {
        for (j = 0; j < G->vexnum; j++)
        {
            G->adj[j][k].ArcVal = INFINITY;
            G->adj[k][j].ArcVal = INFINITY;
        }
    }
}
//向图中增加一条弧
int AddArc(MGraph* G, ArcType* arc)
{
    int k, j;
    k = LoacteVex(G, &arc->vex1);
    j = LocateVex(G, &arc->vex1);
    if (k == -1 || j == -1)
    {
        cout << "Arc's Vertex do not existed!" << endl;
        return -1;
    }
    if (G->kind == DG || G->kind == WDG)   //有向图或带权的有向图
    {
        G->adj[k][j].ArcVal = arc->ArcVal;
        G->adj[k][j].ArcInfo = arc->ArcInfo;
    }
    else
    {
        G->adj[k][j].ArcVal = arc->ArcVal;
        G->adj[j][k].ArcVal = arc->ArcVal;
        G->adj[k][j].ArcInfo = arc->ArcInfo;
        G->adj[j][k].ArcInfo = arc->ArcInfo;
    }
    return 1;
}

2 图的存储结构,我们采用邻接矩阵:

  包括 表节点(类似链表,存储单元不连续) 和 头节点(连续的存储单元),相比直接用二位矩阵存储来说,使用的内存空间更小。

//邻接链表法
//表节点 顶点节点
typedef struct LinkNode
{
    int adjvex;   //邻接点在头节点数组的位置(下标)
    InfoType info;  //与边或弧的相关信息。如权值
    LinkNode* nextarc; //指向下一个表节点
}LinkNode;   //表节点类型定义
typedef struct VexNode
{
    VexType data;  //顶点的信息
    int indegree; //顶点的度 有向图是入度或出度或没有
    LinkNode* firstarc;  //指向第一个表节点
}VexNode;   //顶点节点类型定义
typedef ArcType
{
    VexType vex1,vex2;  //弧或边所依附的两个顶点
    InfoType info;   //与边或弧的相关的信息,如权值
}ArcType;   //弧或边的结构定义
typedef struct
{
    GraphKind kind;   //图的种类标值
    int vexnum;
    VexNode AdjList[MAX_VEX];
}ALGraph;   //图的结构定义
//图的创建
ALGraph* Create_Graph(ALGraph* G)
{
    cout << "请输入种类标值" << endl;
    cin >> &G->kind;
    G->vexnum = 0;
    return G;
}
//图的顶点定位  确定一个顶点在AdjList数组某个元素的data域内容
int LocateVex(ALGraph* G, VexType* vp)
{
    int k;
    for (k = 0; k < G->vexnum; k++)
        if (G->AdjList[k].data == *vp)return k;
    return -1;  //图中无此节点
}
//向图中增加节点  在AdjList数组的末尾增加一个数据元素
int AddVertex(ALGraph* G, VexType* vp)
{
    int k, j;
    if (G->vexnum >= MAX_VEX)
        cout << "Vertex Overflow!" << endl; return -1;
    if (LocateVex(G, vp) != -1)
        cout << "Vertex has existed!" << endl; return -1;
    G->AdjList[G->vexnum].data = *vp;
    G->AdjList[G->vexnum].indegree = 0;
    G->AdjList[G->vexnum].firstarc = NULL;
    k = ++G->vexnum;
    return k;
}
//向图中增加一条弧
int AddArc(ALGraph* G, ArcType* arc)
{
    int k, j;
    LinkNode* p, * q;
    k = LocateVex(G, &arc->vex1);
    j = LocateVex(G, &arc->vex2);
    if (k == -1 || j == -1)
    {
        cout << "Arc Vertex do not existed!" << endl;
        return -1;
    }
    p = (LinkNode*)malloc(sizeof(LinkNode));
    p->adjvex = arc->vex1; p->info = arc->info;
    p->nextarc = NULL;     //边的起始表节点赋值
    q = (LinkNode*)malloc(sizeof(LinkNode));
    q->adjvex = arc->vex2; q->info = arc->info;
    q->nextarc = NULL;  //边的末尾表节点的节点赋值
    if (G->kind == AG || G->kind == WAG)
    {
        q->nextarc = G->AdjList[k].firstarc;
        G->AdjList[k].firstarc = q;
        p->nextarc = G->AdjList[j].firstarc;
        G->AdjList[j].firstarc = p;
    }  //是无向图 用头插入法插入到两个单链表
    else
    {
        q->nextarc = G->AdjList[k].firstarc; //建立正邻接列表用
        G->AdjList[k].firstarc = q;
        //q->nextarc = G->AdjList[j].firstarc;   建立逆邻接列表用
        //G->AdjList[j].firstarc = q;
    }
}

3 十字链表。

  是有向图的另一种存储方式。可以看成是将有向图的邻接表和逆邻接表结合起来得到的一种链表。

  包括 弧节点 和 顶点节点。

  弧节点中包括五部分:尾域,头域,链域hlink,链域tlink,info(相关信息)。

  顶点节点:data数据,firstin(指向该节点的弧头),firstout(指向该节点的弧尾)。

//十字链表  将有向图的正邻接表和逆邻接表结合起来的得到的一种表
typedef struct ArcNode
{
    int tailvex, headvex;   //尾节点和头节点在图中的位置
    InfoType ArcNode* hlink, * tlink;  //与弧相关的信息
    struct ArcNode* hlink, * tlink; 
}ArcNode;  //弧节点类型定义
typedef struct VexNode
{
    VexType data;    //顶点的信息
    ArcNode* firstin, * firstout;  //分别指向该顶点的第一条入弧和出弧
}VexType;  //顶点类型的定义
typedef struct
{
    int vexnum,arcnum;     //定义当前的定点数和弧数
    VexNode xlist[MAX_VEX];   //表头向量
}OLGraph;  //图的定义
//建立有向图的十字链表  G.kind = DG
Status CreateDG(OLGraph& G)
{
    cin >> G.vexnum >> G.arcnum >> IncInfo;   //IncInfo为0标识各弧不含其他信息
    for (i = 0; i < G.vexnum; ++i)
    {
        cin >> G.xlist[i].data;
        G.xlist[i].firstin = NULL; G.xlist[i].firstout = NULL;  //初始化表头
    }
    for (k = 0; k < G.arcnum; ++k)
    {
        cin >> v1 >> v2;
        i = LocateVex(G, v1); j = LocateVex(G, v2);   //确定v1和v2的位置
        p = (ArcNode*)malloc(sizeof(ArcNode));
        p->tailvex = i; p->headvex = j;
        p->hlink = G.xlist[j].firstin; p->tlink = G.xlist[i].firstout;  //对弧节点赋值
        G.xlist[j].firstin = G.xlist[i].firstout = p;  //完成在入弧和出弧链头的插入
        if (IncInfo) cin >> p->info;
    }
}

4 邻接多重表。是无向图的另一种存储结构,

//邻接多重表   无向图的另一种链式存储结构
typedef emnu{ unvisited, visited } Visitting;
typedef struct EdgeNode
{
    Visitting mark; //访问标值
    int ivex, jvex;  //该边依附的两个节点在图中的位置 
    InfoType Info;  //与边的相关的信息,入=如权值
    EdgeNode* ilink, * jlink;   //分别指向依附于这两个顶点的吓一条边
}EdgeNode;  //弧节点类型的定义
typedef struct VexNode
{
    VexType data;
    ArcNode* firstedge;   //依附于该顶点的第一条边
}VexBox;  //顶点的类型定义
typedef struct
{
    int vexnum;
    VexBox mullist[MAX_VEX];
}AMGraph;
//图的边表存储结构
typedef struct ENode
{
    int ivex, jvex;   //边所依附的两个顶点
    WeightType weight;  //边的权值
}ENode;  //边元素的类型定义
#define MAX_EDGE 100
typedef struct
{
    int vexnum, edgenum;     //定点数和边数
    VexBox vexlist[MAX_VEX];   //顶点表
    ENode edgelist[MAX_EDGE];   //边表
}ELGraph;

5 图的遍历

  我们从某一定带你出发访便图中其余的顶点,且每个顶点仅仅被访问一次。

  大致有两种方法:深度优先遍历,广度优先搜索。

 

深度优先搜索算法:

  一条路走到黑,递归。

//图的遍历  从一顶点出发,访问其余每个节点
//数的遍历算法有 深度优先搜索算法 和 广度优先搜索算法
//一条路走到头  再步步回撤  递归过程
typedef emnu{ FALSE, TRUE } BOOLEAN;
BOOLEAN Visited[MAX_VEX];
void DFS(ALGraph* G, int v)    //一条路走到黑
{
    LinkNode* p;
    Visited[v] = TRUE;
    Visited[v];
    p = G->AdjList[v].firstarc;
    while (p != NULL)
    {
        if (!Visited[p->adjvex])  DFS(G, p->adjvex);
        p = p->nextarc;
    }
}
void DFS_traverse_Graph(ALGraph* G)
{
    int v;
    for (v = 0; v < G->vexnum; v++)
    {
        Visited[v] = FALSE;     //访问标值数组初始化
    }
    for (v = 0; v < G->vexnum; v++)
    {
        if (!Visited[v]) DFS(G, v);   //对尚未访问的顶点调用DFS
    }
}

广度优先算法,一次访问兄弟节点。

//广度优先搜索算法   树的按层次遍历过程
typedef emnu{ FALSE, TRUE } BOOLEAN;
BOOLEAN Visited[MAX_VEX];
typedef struct Queue
{
    int elem[MAX_VEX];
    int front, rear;
}Queue;  //定义一个队列保存将要访问的顶点
void BFS_traverse_Graph(ALGraph* G)
//按广度优先非递归遍历G   使用辅助队列Q和访问标值数组visited
{
    int k, v, w;
    LinkNode* p; Queue* Q;
    Q = (Queue*)malloc(sizeof(Queue));
    Q->front = Q->rear = 0;      //建立空队列初始化
    for (k = 0; k < G->vexnum; k++)
    {
        Visited[k] = FALSE;   //访问标值初始化
    }
    for (v = 0; v < G->vexnum; k++)
    {
        if (!Visited[v])   //尚未访问
        {
            Q->elem[++Q->rear] = v;     //v入队
            while (Q->front != Q->rear)
            {
                w = Q->elem[++Q->front];
                Visited[w] = TRUE;
                Visit(w);
                p = G->AdjList[w].firstarc;
                while (p != NULL)
                {
                    if (!Visited[p->adjvex])
                    {
                        Q->elem[++Q->rear] = p->adjvex;
                        p = p->nextarc;
                    }
                }
            }
        }
    }
}

 

 

未完,待续...........

posted @ 2020-04-11 22:37  为红颜  阅读(116)  评论(0编辑  收藏