HNU图结构实验详解

本文介绍了图的深度优先搜索(DFS)和广度优先搜索(BFS)的概念,以及这两种搜索方法在邻接链表和邻接矩阵实现中的应用。同时,详细讲解了Dijkstra和Prim算法,用于寻找图中的最短路径。作者通过分享代码实现和调试过程中遇到的问题,帮助读者更好地理解和掌握这些算法。
摘要由CSDN通过智能技术生成

明天就要实验啦,今晚写这个不知道学不学的完啊,太头秃了。
不过咱死马当活马医,能复习怎样就怎样咯。
声明一下哈,这篇博客里用到的代码不是我原创的。参考了CSDN上一位大佬的答案。在此,仅对大佬的代码进行复现,以及加上我自己的理解。希望能对我这个菜狗有所帮助。

DFS与BFS

时间有限本文写不了多长咯,长话短说,直接上DFS的示例,栈结构,和shaffer教材的实现伪代码

DFS示例与该过程递归栈的变化DFS示例与该过程递归栈的变化
请添加图片描述
这里是伪代码实现示例。
这里有什么不懂的呢,最显而易见的是PreVisit和PostVisit了,这两个函数是干嘛的呢?
回想我们在树结构处学过先序遍历、中序遍历、后序遍历和层次遍历。那时候用到了一个visit函数,visit被作为参数传入,根据访问顺序的不同,而存在于递归访问的不同位置。(这么说如果不记得树的实验就不容易看懂,不过没关系,后续复习的时候会把树结构的实验也补上来的。)
下面是广度优先搜索的示例和伪代码。
请添加图片描述知识背景复习就到这里~

Dijkstra与Prim

这里我都记得,不过一时找不到课本伪代码了,所以这一部分的背景补充先略过

实验文件与模块介绍

解压实验包并新建项目,我们就得到这样的目录

在这里插入图片描述graph里是图结构的ADT
Graph_test里是test内容,我们要完成的Dijkstra和Prim,BFS DFS都在里面
grlist是图的邻接链表实现
grmat是图的邻接矩阵实现
link和llist list都是链表的实现

需要完成的代码部分

先上开胃菜

完成顶点出度入度的计算

究其思想就是借用ADT里给出的isEdge(int v1,int v2)来判断(v1,v2)是不是图的一条边。
martix和list实现唯一的区别在于出度的计算,list实现可以直接返回vertex[v]->length()就可
martex实现需要遍历一下所有点到v的边是否存在于图就可以

int getInDegree(int v)   // 求顶点v的入度
    {
        int result = 0;
        //............... 在此行以下插入补充代码
		for(int i=0;i<numVertex;i++) {
			if(i!=v) {
				if(isEdge(i,v)) result++;
			}
		}
        //............... 在此行以上插入补充代码
        return result;
    }

int getOutDegree(int v)    // 求顶点v的出度
    {
        int result=0;
         //............... 在此行以下插入补充代码
		 result=vertex[v]->length();
        //............... 在此行以上插入补充代码
        return result;
    }
};

so easy啦是不是,其实我自己做的时候遇到了两个坑。
1.图的链表实现和图的邻接矩阵实现,对应的出度入度计算方法是不同的(毕竟他们给出的ADT就不同)
2.一定仔细看ADT哇,善于借用ADT来帮助完成函数功能。我第一次没有注意看,手撸链表,然后理所当然地出现了segment error
上面是grlist的实现方式,下面展示grmat的实现方式

int getInDegree(int v)   // 求顶点v的入度
    {
        int result = 0;
        //............... 在此行以下插入补充代码
		for(int i=0;i<numVertex;i++) {
			if(isEdge(i,v)) result++;
		}
        //............... 在此行以上插入补充代码
        return result;
    }

    int getOutDegree(int v)    // 求顶点v的出度
    {
        int result = 0;
        //............... 在此行以下插入补充代码
		 for(int i=0;i<numVertex;i++) 
		 	if(isEdge(v,i)) result++;
        //............... 在此行以上插入补充代码
        return result;
    }

DFS与BFS

这两种图的搜索方法上面已经介绍过。因为两种图的实现都已经封装好了,所以这一步两种实现的处理是一样的。

void DFS(int v, void (*PreVisit)(int v), void (*PostVisit)(int v), void (*Visiting)(int v))   // Depth first search
    {
		PreVisit(v);//如果要先序遍历/访问,可以从previsit这里传入需要做的处理
		visiting(v);//中序遍历
		G->setMark(v,VISITED);//访问当前结点
		for(int i=G->first(v);i<G->n();i=G->next(v,i)) {//这里有点像回溯算法是不是
			if(G->getMark(i)==UNVISITED)
			DFS(i, PreVisit, PostVisit, Visiting);
			}
		PostVisit(v);//后序遍历。真的是太像BinTree啦
    }

ok下面是BFS

void BFS(int start, void (*PreVisit)(int v), void (*PostVisit)(int v), void (*Visiting)(int v))
    {
		int v,w;
		queue<int> q;
		G->setMark(start,VISITED);
		q.push(start);//上面可称为准备过程
		while(!q.empty()) {
			v=q.front();
			q,pop();
			if(v==start) PreVisit(v);//由于start已经设为访问过了,所以单独处理
			visiting(v);//中序遍历,和DFS过程一样(虽然我现在并不是很理解)
			for(w=G->first(v),w<G->n();w=G->next(v,w)) //开始处理结点的邻居结点
				if(G->getMark(w)==UNVISITED) {
				PreVisit(w);
				G->setMark(w,VISITED);
				q.push(w);
			}
			PostVisit(v);
		}
    }

Dijkstra与Prim算法

void Dijkstra1(int* D, int s)
    {//来吧,我最爱的dijkstra算法,“一把手就拉住了,我亲爱的小冤家啊~~”
    	int v,w;
    	//注意,通常情况下D数据是需要对source进行初始化的,但这里不需要,D的初始化在main函数里做过了,要是再做的话,0会覆盖掉本该出现的INF
    	for(int i=0;i<G->n();i++) {//这个意思是说要遍历n个结点
			v=minVertex(D);//找到D数组中为访问的最小数的下标,别的不用管,minVertex帮你做过了
			if(v==INFINITY) return;
			G->setMark(v,VISITED);
			for(w=G->first(v);w<G->n();w=G->next(v,w)) //我这里还有疑问,为什么不需要判断w结点是否已访问过呢?我还试了下,如果验证是否访问,反而会出现错误
				if(D[w]>D[v]+G->weight(v,w)) D[w]=D[v]+G->weight(v,w);
		}
    }

接下来,Prim算法

int V[G->n()];
int v,w,i;
for(int i=0;i<G->n();i++) {
	v=minVertex(D);
	if(v==INFINITY) return;
	G->setMark(v,VISITED);
	if(v!=s) ADDEdgetoMST(V[v],v);
	for(w=G->first(v);w<G->n();v=G->next(v,w)) 
		if(D[w]>G->weight(v,w)) {
		D[w]=G->weight(v,w);
		V[w]=v;
		}
}
}

好了,今晚就写到这里了。明天还有早八。
中午实验加油啊QwQ

实验做完了,我靠,你糊的机房该换新了吧。编译器能给我气抽抽,卡到裂开好吗,每次还要手动clean。而且我觉得题目的代码有隐性bug,我的BFS代码在本地机上可以跑,但是机房就不行了,报错在graph(int int) 不能reload。离谱啊,就即便不调用BFS也会出编译问题。这我能怎么办咯。不过还行,20道测试,AC10道就算满分了。
哎~希望下次能抽到一个灵敏的电脑。

作为菜狗,我理所当然地参加了周一中午的补测。体验和上次形成鲜明对比,(按理来说我上次已经满分了),编译器非常利索,但这次也不是一次AC的,记录一下debug过程出现的问题,以及今天完美过掉的BFS。
1.DFS里的递归部分
本该是DFS(w,pervisit,visiting,postvisit)把写成v了,导致无穷递归
2.Dijkstra和Prim里忘记写setMark(w,VISITED)导致每次结果都只返回源点到各边的距离。这也是我觉得用minMartex的不好处,选择标记不是自己写的,就老记不住访问过后加标记。
标记加在确定每轮访问的v之后就可以,顺序无所谓。

今天的BFS:

void BFS(int start,void (PreVisit*)(int v),void (Visiting*)(int v),void (PostVisit*)(int v)) {
	int v=start,w=0;
	queue<int> q;
	Previsit(v);
	q.push(v);
	G->setMark(v,VISITED);
	while(!q.empty()) {
		v=q.front();
		q.pop();
		Visiting(v);
		for(w=G->first(v);w<G->n();w=G->next(v,w)) {
			if(G->getMark(w)==UNVISITED) 
			{	PreVisit(w);
				G->setMark(w,VISITED);
				q.push(w);
			}
		}
		PostVisit(v);
	}
}

相信我,这段代码在HNU绝对“吸烟刻肺”级别的了,好吗。
这次实验就先这样吧。以及,如果你有个在CS的朋友,送ta一把漂亮耐用的雨伞和一大盒感冒灵颗粒,ta会感动到哭的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值