1.分别实现有向图的邻接矩阵和邻接表存储结构的建立算法,分析和比较各建立算法的时间复杂度以及存储结构的空间占用情况。
逆邻接矩阵:
//邻接矩阵
typedef struct {
char vertex[V_Number];//顶点表
int edge[V_Number][V_Number];//边表
int n;//顶点数
int e;//边数
}MTGraph;
void CreatMTGragh(MTGraph* G)//构建有向图的逆邻接矩阵表
{
cout << "输入顶点数和边数" << endl;
cin >> G->n>> G->e;
cout << "输入顶点信息" << endl;
for (int i = 0; i < G->n; i++)
cin >> G->vertex[i];
for (int i = 0; i < G->n; ++i)
for (int j = 0; j < G->n; ++j)
G->edge[i][j] = 0;//初始化
cout << "读入e条边建立邻接矩阵,分别输入tail、head、weight" << endl;
for (int k = 0; k < G->e; k++) {
int i, j, w;
cin >> i >> j >> w;//输入边(i,j)上的权值
G->edge[i][j] = w;
}
}
邻接矩阵建立算法的时间复杂度为O(n2+n+e) 空间复杂度为O(n+n2)
逆邻接表:
//逆邻接表
typedef struct node {//边表
int adjvex;//邻接点域
int cost;//边上的权值
struct node* next;//下一边链接指针
}EdgeNode;
typedef struct {//顶点表节点
char vertex;//顶点数据域
EdgeNode* firstedge;//边链表头指针
}VertexNode;
typedef struct {//图的邻接表
VertexNode vexlist[V_Number];
int n;
int e;
}AdjGraph;
void CreateGraph(AdjGraph* G) {//建立有向图逆邻接表
cout << "输入顶点数和边数" << endl;
cin >> G->n >> G->e;
cout << "输入顶点信息" << endl;
for (int i = 0; i < G->n; ++i) {
cin >> G->vexlist[i].vertex;
G->vexlist[i].firstedge = NULL;//边表置为空表
}
int tail, head, weight;
EdgeNode* temp;
cout << "读入e条边建立邻接矩阵,分别输入tail、head、weight" << endl;
for (int i = 0; i < G->e; ++i) {//逆邻接矩阵表
cin >> tail >> head >> weight;
EdgeNode *p= new EdgeNode;//头插法
p->adjvex = head;
p->cost = weight;
p->next = G->vexlist[tail].firstedge;
G->vexlist[tail].firstedge = p;
}
}
邻接表建立算法的时间复杂度为O(n+e) 空间复杂度为O(n+e)
2.实现有向图的邻接矩阵和邻接表两种存储结构的相互转换算法。
示例有向图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t9czIL9I-1677332191110)(未命名.assets/图片2.png)]
邻接矩阵转换为邻接表:
测试代码
int main() {
MTGraph* G = new MTGraph;
AdjGraph* A = new AdjGraph;
int in[V_Number] = { 0 };
int out[V_Number] = { 0 };
CreatMTGragh(G);
cout << "邻接矩阵:" << endl;
printm(G);
cout << "转换后" << endl;
A = TransToAdj(G);
printa(A);
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u2W9oUPs-1677332191110)(未命名.assets/图片6.png)]
3.在上述两种存储结构上,分别实现有向图的深度优先搜索(递归 和非递归)和广度优先搜索算法。并以适当的方式存储和展示相应的搜索结果, 包括:深度优先或广度优先生成森林(或生成树)、深度优先或广度优先序列 和深度优先或广度优先编号。并分析搜索算法的时间复杂度和空间复杂度。
使用的栈类:
#define maxlength 10000
class MyStack {
public:
MyStack();
void MakeNull();//将栈置空
void Push(int x);//进栈
int Pop();//出栈
bool Empty();//判断栈是否为空
int Top();
public:
int stk[maxlength];//用于存放横坐标、纵坐标
int top;//用于记录栈顶
};
使用的队列类:
#define Max 1000
class Queue {//尾进头出
public:
Queue();//无参构造初始化队列
bool Empty();
void EnQueue(int x);
int DeQueue();
public:
int front;
int rear;
int d[Max];
};
int dfn[V_Number];//顶点的先深编号
bool visited[V_Number];//访问标记数组
void visitm(MTGraph* G, int i) {//按下标访问逆邻接矩阵的顶点表
cout << G->vertex[i] << " ";
}
void visita(AdjGraph* G, int i) {//按下标访问邻接表的顶点表
cout << G->vexlist[i].vertex << " ";
}
//邻接矩阵的递归深度优先搜索
void DFSMR1(MTGraph* G, int i, int& count) {//从一个顶点出发的一次深度优先遍历算法
visitm(G, i);
visited[i] = true;//标记为访问过
dfn[i] = count++;//对i顶点进行编号
for (int j = 0; j < G->n; ++j) {
if (G->edge[i][j] && (!visited[j])) {//找到下一个未被访问的邻接点
DFSMR1(G, j, count);
}
}
}
void DFSMre(MTGraph* G) {//主算法
int count = 1;
for (int i = 0; i < G->n; ++i) {
visited[i] = false;//标记数组初始化
}
for (int i = 0; i < G->n; ++i) {
if (!visited[i])
DFSMR1(G, i, count);//从顶点i出发的一次搜索
}
}
//邻接表的递归深度优先搜索
void DFSAR1(AdjGraph* G, int i, int& count) {//从一个顶点出发的一次深度优先遍历算法
EdgeNode* p;
visita(G, i);
visited[i] = true;
dfn[i] = count++;//对i顶点进行编号
p = G->vexlist[i].firstedge;
while (p) {//有相邻边
if (!visited[p->adjvex])//未访问过
DFSAR1(G, p->adjvex, count);//访问该相邻点
p = p->next;
}
}
void DFSAre(AdjGraph* G) {//主算法
int count = 1;
for (int i = 0; i < G->n; ++i) {
visited[i] = false;//标记数组初始化
}
for (int i = 0; i < G->n; ++i) {
if (!visited[i])
DFSAR1(G, i, count);//从顶点i出发的一次搜索
}
}
//邻接矩阵的非递归深度优先搜索
void DFSM1(MTGraph* G, int i, int& count) {//从一个顶点出发的一次深度优先遍历算法
MyStack S;
S.Push(i);//进栈
visitm(G, i);
visited[i] = true;//标记为访问过
dfn[i] = count++;//对i顶点进行编号
for (int j = 0;;) {
if (j == G->n) {
if (S.Empty())
break;//栈空退出
i=S.Pop();
j = 0;
}
if (G->edge[i][j] && !visited[j]) {
visited[j] = true;
visitm(G, j);
dfn[j] = count++;
S.Push(j);
i = j;//从j开始
j = -1;
}
++j;
}
}
void DFSM(MTGraph* G) {//主算法
int count = 1;
for (int i = 0; i < G->n; ++i) {
visited[i] = false;//标记数组初始化
}
for (int i = 0; i < G->n; ++i) {
if (!visited[i])
DFSM1(G, i, count);//依次访问顶点
}
}
//邻接表的非递归深度优先搜索
void DFSA1(AdjGraph* G, int i, int& count) {//从一个顶点出发的一次深度优先遍历算法
MyStack S;
S.Push(i);
visita(G, i);
visited[i] = true;
dfn[i] = count++;//对i顶点进行编号
while (1) {
EdgeNode* p = G->vexlist[i].firstedge;
while (p) {
if (!visited[p->adjvex]) {
S.Push(p->adjvex);
visita(G, p->adjvex);
visited[p->adjvex] = true;
dfn[p->adjvex] = count++;
if (G->vexlist[p->adjvex].firstedge)//下一个顶点
p = G->vexlist[p->adjvex].firstedge;
else
p = p->next;
}
else
p = p->next;
}
if (S.Empty())
break;
i=S.Pop();
}
}
void DFSA(AdjGraph* G) {//主算法
int count = 1;
for (int i = 0; i < G->n; ++i) {
visited[i] = false;
}
for (int i = 0; i < G->n; ++i) {
if (!visited[i])
DFSA1(G, i, count);
}
}
int bfn[V_Number];//顶点的先深编号
//邻接矩阵的广度优先遍历
void BFSM1(MTGraph* G, int i, int& count) {//从一个顶点出发的一次广度优先遍历算法
Queue Q;//初始化队列
visited[i] = true;
visitm(G, i);
bfn[i] = count++;
Q.EnQueue(i);//顶点入队
while (!Q.Empty()) {//队空搜索结束
int i;
i = Q.DeQueue();
for (int j = 0; j < G->n; j++) {
if (G->edge[i][j] && !visited[j]) {
visitm(G, j);//访问j
visited[j] = true;
bfn[j] = count++;
Q.EnQueue(j);
}
}
}
}
void BFSM(MTGraph* G) {//主算法
int count = 1;
for (int i = 0; i < G->n; ++i) {
visited[i] = false;//标记数组初始化
}
for (int i = 0; i < G->n; ++i) {
if (!visited[i]) {
BFSM1(G, i, count);
}
}
}
//邻接表的广度优先遍历
void BFSA1(AdjGraph* G, int i, int& count) {//从一个顶点出发的一次广度优先遍历算法
EdgeNode* p;
Queue Q;
visita(G, i);
visited[i] = true;
bfn[i] = count++;
Q.EnQueue(i);
while (!Q.Empty()) {
int i;
i = Q.DeQueue();
p = G->vexlist[i].firstedge;
while (p) {
if (!visited[p->adjvex]) {
visita(G, p->adjvex);
visited[p->adjvex] = true;
bfn[p->adjvex] = count++;
Q.EnQueue(p->adjvex);
}
p = p->next;
}
}
}
void BFSA(AdjGraph* G) {//主算法
int count = 1;
for (int i = 0; i < G->n; ++i) {
visited[i] = false;//初始化标记数组
}
for (int i = 0; i < G->n; ++i) {
if (!visited[i]) {
BFSA1(G, i, count);
}
}
}
邻接矩阵的深度优先搜索时间复杂度为O(n2)
表的深度优先搜索时间复杂度为O(n+e)
4.对于有向图,采用“邻接表”存储结构,设计和实现计算每个顶点入度、 出度和度的算法,并分析其时间复杂度。
void DU(AdjGraph* A, int in[], int out[]) {
EdgeNode* p;
for (int i = 0; i < A->n; ++i) {
p = A->vexlist[i].firstedge;
while (p) {
in[i]++;
out[p->adjvex]++;
p = p->next;
}
}
}
时间复杂度为O(n+e)
5.以适当的方式输入图的顶点和边,并显示相应的结果。要求顶点不少于 10 个,边不少于 13 条。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-As94WU2W-1677332191111)(未命名.assets/图片2.png)]
完成实验目标345
测试代码:
int main() {
MTGraph* G = new MTGraph;
AdjGraph* A = new AdjGraph;
int in[V_Number] = { 0 };
int out[V_Number] = { 0 };
CreatMTGragh(G);
cout << "邻接矩阵:" << endl;
printm(G);
cout << "转换后" << endl;
A = TransToAdj(G);
printa(A);
cout << "广度优先遍历序列:" << endl;
BFSM(G);
cout << endl;
cout << "广度优先编号:" << endl;
for (int i = 0; i < G->n; ++i) {
cout << bfn[i] << " ";
}
cout << endl;
cout << "深度优先搜索序列为:" << endl;
DFSMre(G);
cout << endl;
cout << "深度优先编号为:" << endl;
for (int i = 0; i < G->n; ++i) {
cout << dfn[i] << " ";
}
cout << endl;
DU(A, in, out);
cout << "各顶点入度为:" << endl;
for (int i = 0; i < A->n; ++i) {
cout << in[i] << " ";
}
cout << endl;
cout << "各顶点出度为:" << endl;
for (int i = 0; i < A->n; ++i) {
cout << out[i] << " ";
}
cout << endl;
cout << "各顶点度为:" << endl;
for (int i = 0; i < A->n; ++i) {
cout << in[i] + out[i] << " ";
}
cout << endl;
}
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R92Od3Z1-1677332191111)(未命名.assets/图片7.png)]
<< " ";
}
cout << endl;
DU(A, in, out);
cout << “各顶点入度为:” << endl;
for (int i = 0; i < A->n; ++i) {
cout << in[i] << " ";
}
cout << endl;
cout << “各顶点出度为:” << endl;
for (int i = 0; i < A->n; ++i) {
cout << out[i] << " ";
}
cout << endl;
cout << “各顶点度为:” << endl;
for (int i = 0; i < A->n; ++i) {
cout << in[i] + out[i] << " ";
}
cout << endl;
}
结果:
[外链图片转存中...(img-R92Od3Z1-1677332191111)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KizxPlAR-1677332191111)(未命名.assets/图片8.png)]