图(一)
小编也是最近再学 图。
图是一种较线性表和树更为复杂的数据结构。在线性表中,每个中间节点只有一个前驱或者后驱节点。树结构,数据家具元素之间有着明显的层次。而图中,数据元素的关系可是是任意的。
较容易记错的地方。在有向图中,<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; } } } } } }
未完,待续...........