数据结构(C语言)图的基本操作及应用
图的基本操作及应用
- 用邻接表法创建无向图
- 以邻接表形式打印无向图
- 深度遍历无向图并打印
- 利用队列知识实现广度遍历无向图并打印
- 取各顶点在表中序号
头文件及数据
#include <iostream>
using namespace std;
#define MAX 100 //最大顶点数
图存储结构
- 边结点的存储结构:这个边所指向的顶点的位置、指向连接这个顶点的下一条边的指针(比如这个顶点为1,则nextB第一个指向边12,nextB下一个则会指向边13,再下一个则指边14)、这条边的相关信息(如权值)
typedef struct BNode
{
int pointPosite; //该边所指向顶点的位置
struct BNode *nextB; //指向下一条边的指针
int info; //和边相关的信息
}BNode;
- 顶点的存储结构:存放顶点的数据域、指向连接这个顶点的第一条边(这个有些不好理解,如下图:比如顶点为1,则顶点1的firstB为边12,若在循环里下一个firstB则指向边25)
typedef struct DNode
{
string data; //存放顶点信息
BNode *firstB; //指针指向第一条依附该顶点的边
}DNode,AdjList[MAX];//AdjList表领接表类型``
- [ ] ***邻接表的存储结构***
```cpp
typedef struct
{
AdjList vertices; //顶点向量
int DNum, BNum; //图的当前顶点数和边数
}TGraph;
队列的存储表示
typedef struct
{
string *base; //存储空间的基地址
int front; //头指针
int rear; //尾指针
}SqQueue;
队列的初始化、入队、出队、判空
void InitQueue(SqQueue &Q)
{//构造一个空队列
Q.base = new string[MAX];
if(!Q.base)
{
cout<<"内存分配失败"<<endl;
}
Q.front = Q.rear = 0;
}
void EnQueue(SqQueue &Q, string e)
{
if((Q.rear+1)%MAX == Q.front)
{
cout<<"队列已满"<<endl;
}
Q.base[Q.rear] = e;
Q.rear = (Q.rear+1)%MAX;
}
string DeQueue(SqQueue &Q, string &e)
{
if(Q.front == Q.rear)
{
cout<<"队列为空"<<endl;
}
e = Q.base[Q.front];
Q.front = (Q.front+1)%MAX;
return e;
}
bool Empty(SqQueue Q)
{
if(Q.front == Q.rear)
{
return true;
}
return false;
}
邻接表法创建无向图
- 首先要求输入所要创建无向图的总顶点数和总边数,第二步在循环里输入顶点建立表头结点表,最后利用循环,要求输入各条边依附的两个顶点的信息,取两顶点在表头结点表中的序号,利用两个指针p1,p2分别使用头插法构建邻接表
void CreateUDG(TGraph &G) //无向图UDG
{
string v1,v2;
cout<<"请输入所要创建图的总顶点数和总边数:";
cin>>G.DNum>>G.BNum; //输入总顶点数和总边数
for(int i = 0; i < G.DNum; ++i) //输入各点,构造表头结点表
{
cout<<"请输入第"<<i+1<<"个顶点的值:";
cin>>G.vertices[i].data; //输入顶点值
G.vertices[i].firstB = NULL;//初始化表头结点指针域为空
}
for(int k = 0; k < G.BNum; ++k) //输入各边,构造领接表
{
BNode *p1,*p2;
cout<<"请输入第"<<k+1<<"条边的信息:";
cin>>v1>>v2; //输入一条边依附的两个顶点
int m = LocateVex(G, v1); int n = LocateVex(G, v2); //得到v1 v2在G.vertices中的序号
p1 = new BNode; //生成一个新的边结点*p1
p1->pointPosite = n; //邻接点序号为n
p1->nextB = G.vertices[m].firstB; //头插法
G.vertices[m].firstB = p1;
p2 = new BNode;
p2->pointPosite = m;
p2->nextB = G.vertices[n].firstB;
G.vertices[n].firstB = p2;
}
}
打印该图邻接表
- 定义指针p,外层for循环遍历输出每个结点信息,for循环中p指针依次指向每个顶点的邻接点,当指针p不为空时,进入内层while循环,首先输出指针p的data信息,再移动指针p指向下一个邻接点信息继续while循环
void UDGprint(TGraph G)
{
BNode *p;
for(int i = 0; i < G.DNum; ++i)
{
cout<<G.vertices[i].data;
p = G.vertices[i].firstB;
while(p)
{
cout<<"->";
cout<<p->pointPosite;
p = p->nextB;
}
cout<<endl;
}
}
取所给顶点在表中的序号
- 对表中所有顶点进行遍历,若表中的第i个data的值与所给顶点相等,则返回i,否则返回-1
int LocateVex(TGraph G, string v)
{
for(int i = 0; i < G.DNum; ++i)
{
if(v == G.vertices[i].data)
{
return i;
}
}
return -1;
}
定义标志数组
- 定义标志数组,其初值全为false,以便标记顶点是否遍历,其初值为false;
深度优先遍历图
- 首先输出第一个开始顶点v,且设置为已访问。定义指针p,访问指定开始顶点的第一个邻接点,p不为空时进入while循环,将邻接点的序号赋给w,判断是否被访问过,若没有则以w为参数对函数递归;若被访问过,p指向下一个边结点进入循环
void deepTravel(TGraph G, string v)
{
int b,w;
BNode *p;
cout<<v<<" ";
b = LocateVex(G, v);
visited[b] = true; //访问第b个顶点,标志为true
p = G.vertices[b].firstB; //p指向v的边链表的第一个边结点
while(p != NULL) //如果p不为空
{
w = p->pointPosite; //w为v的第一个边结点
if(!visited[w]) //若w未被访问 则继续递归
{
string a;
for(int i = 0; i < G.DNum; ++i)
{
if(w == i)
{
a = G.vertices[i].data;
}
}
deepTravel(G, a);
}
p = p->nextB; //否则p指向下一个v的边结点
}
}
广度优先遍历图
- 首先将所有顶点的标记数组设置为未访问false;第二步输出第一个开始顶点v,且设置为已访问,并将开始顶点v入队;第三步若队列不为空则进入外层while循环,将队列出队,并将指针p指向出队顶点v的第一个邻接点;第四步若指针p不为空则进入内层while循环,if判断指针p所指向的邻接点是否被访问过,若没有被访问过则输入所指向的邻接点data,同时将此邻接点设置为已被访问且将此邻接点入队;若指针p所指向的邻接点被访问过,则移动指针p指向v的下一个邻接点继续进入循环
void breadthTravel(TGraph G, string v)
{
BNode *p;
int l;
string e;
int b = LocateVex(G, v);
SqQueue Q;
InitQueue(Q);
for(int i = 0; i < G.DNum; i++)
visited[i] = false;
if(!visited[b])
{
visited[b] = true;
cout<<v<<" ";
EnQueue(Q, v);
while(!Empty(Q))
{
string s = DeQueue(Q, e);
int u = LocateVex(G, s);
p = G.vertices[u].firstB;
while(p)
{
l = p->pointPosite;
if(!visited[l])
{
visited[l] = true;
cout<<G.vertices[l].data<<" ";
EnQueue(Q, G.vertices[l].data);
}
p = p -> nextB;
}
}
}
}
main函数的设计
int main()
{
string ch;
TGraph G;
CreateUDG(G);
cout<<"建立的邻接表如下: "<<endl;
UDGprint(G);
cout<<"请输入遍历开始的结点:";
cin >> ch;
cout<<"深度遍历序列为: ";
deepTravel(G, ch);
cout<<"\n广度遍历序列为: ";
breadthTravel(G, ch);
return 0;
}
程序调试及运行结果分析
- 运行结果(1)
- 输入的图如下所示:
- 运行结果(2)
- 输入的图如下所示: