概述
Q129.(10分)图的操作:
1.图的存储结构使用邻接矩阵。
2.创建图操作类,支持BFS遍历、DFS遍历、求单源最短路径、求最小生成树、判断是否存在回路等五个功能,这些功能封装成图操作类的成员函数
3. 采用“起始节点,终止节点,权值”输入图的m条边,创建图。
4. 输出从节点1开始的BFS遍历、DFS遍历节点遍历顺序。
5. 输出从节点1到其余节点的最短路径及最短路径长度,如果没有路径,输出0。
6. 输出图的最小生成树包含的边,边用“起始节点,终止节点,权值”表示。
输入输出说明:
输入:
第1行输入2个整数,图的节点数n(不超过10个)、边数m。
接下来m行,每行3个整数。采用“起始节点,终止节点,权值”输入图的m条边。
输出:
第1行为图的BFS遍历
第2行为图的DFS遍历
接下来n-1行,为图中第1个节点到其他节点的最短路径,如果没有路径,路径长度输出“0”。格式为:最短路径 路径长度。
接下来输出图的最小生成树包含的边,边用“起始节点,终止节点,权值”表示,按权值从小到大输出最小生成树的边。
最后一行输出大写字母 YES 或 NO:YES代表图中有环,NO代表图中无环。
输入:
6 9
1 2 10
1 4 20
1 6 2
2 3 3
2 4 5
3 5 15
4 5 11
4 6 10
5 6 3
输出:
1 2 4 6 3 5
1 2 3 5 4 6
1 2 10
1 2 3 13
1 6 4 12
1 6 5 5
1 6 2
1 6 2
2 3 3
5 6 3
2 4 5
1 2 10
YES
首先,兵荒马乱后的代码,为了提交oj,不知道自己写了个什么玩意。
1、另外,一定要读题:我最先一直认为这是有向图,虽然他有起点终点,但是带入测试样例,居然是无向图。
就这一个地方,加上matrix[v1][v2]=matrix[v2][v1]就可以了,调了好久。
2、Dijstra和MST的代码不知道怎么搞得,糟糕。
3、并且之里面还有个很烦的地方:就是结点从1开始,后面的for循环全部都有n+1;或者最开始存的时候要减-1。
#include <iostream>
#include <assert.h>
#include <queue>
#include <memory.h>
#include <algorithm>
using namespace std;
int INFINITY =9999999;
class Graphm{
private:
int INFINITY = 9999999;
int numVertex, numEdge;
int count=0;
int *mark;
//int mat[100][100]={{0}};
//int **p = mat;
//书上的代码是从0开始,而符合题目从1开始——特别注意!!!!! 所以涉及的话都要加1,特别注意
void DFS(int v){
cout<<v<<" "; //??????
this->setMark(v,1);
for (int w=this->first(v); w<this->n()+1;w=this->next(v,w)){ //注意,承接上面,这里要加1
if (this->getMark(w) == 0)
DFS(w);
}
}
void BFS(int start, queue<int>* Q ){
int v,w;
Q->push(start);
this->setMark(start,1);
while (Q->size()!=0){
v = Q->front();
Q->pop();
if(count!=n()-1) {cout<<v<<" ";count+=1;}//?????实现Previsit式的输出
else cout<<v;
for (w=this->first(v); w<this->n()+1; w=this->next(v,w)) //+1;
if (this->getMark(w)==0){
this->setMark(w,1);
Q->push(w);
}
}
}
int helpfindcircle(int v, int h){
setMark(v,1);
for (int w = first(v); w<n()+1; w = next(v,w)){
if(getMark(w)==1 && w!=h)
return 1;
else if (getMark(w)==0)
if(helpfindcircle(w,v))
return 1;
}
return 0;
}
public:
int **matrix;
void Init(int n){
numVertex = n;
numEdge = 0;
mark = new int[n+1];
for (int i=1;i<=numVertex;i++)
mark[i] = 0; //UNVISITED
matrix = (int**) new int*[numVertex+1];
for(int i=1;i<=numVertex;i++) //这里可以不从0开始
matrix[i]=new int[numVertex+1];
for (int i=1; i<numVertex+1;i++)
for (int j=1;j<numVertex+1;j++)
matrix[i][j]=0;
}
Graphm(int numVert){
Init(numVert);
}
~Graphm(){
delete []mark;
for (int i=0;i<numVertex+1;i++) //+1
delete []matrix[i];
delete []matrix;
}
int n(){return numVertex;}
int e(){return numEdge;}
int first(int v){//第一个邻居
for (int i=1;i<numVertex+1;i++) //+1
if (matrix[v][i]!=0) return i;
return numVertex+1;//如果没有直接返回n+1;------非常巧妙,在下面的遍历中终止条件就是<numVertex+1,大于说明一定没有邻居
}
int next(int v, int w){//w后v的下一个邻居
for (int i=w+1;i<numVertex+1;i++)
if (matrix[v][i]!=0)
return i;
return numVertex+1;//没有,返回n+1 -------非常巧妙,在下面的遍历中终止条件就是<numVertex+1,大于说明一定没有邻居
}
void setEdge(int v1, int v2, int wt){
//Assert(wt>0, "illegal weight value.");
if (matrix[v1][v2] == 0) numEdge++;
matrix[v1][v2] = matrix[v2][v1]=wt;
}
void delEdge(int v1, int v2){
if (matrix[v1][v2]!=0) numEdge--;
matrix[v1][v2]=0;
}
bool isEdge(int i, int j){ //判断i,j之间是否有边
return matrix[i][j]!=0;
}
int weight(int v1, int v2){return matrix[v1][v2];}
int getMark(int v){return mark[v];}
void setMark(int v, int val){mark[v]=val;}
void graphTraverse1(){//DFS
for (int v=1;v<this->n()+1;v++) //+1
this->setMark(v,0); //首先全部都标记未访问
for (int v=1;v<this->n()+1;v++) //+1
//从1开始,一个一个遍历
if (this->getMark(v)==0)
DFS(v);
cout<<endl;
}
void graphTraverse2(int start){ //宽搜的开始
count=0;
for (int v=1;v<this->n()+1;v++) //+1
this->setMark(v,0); //首先全部都标记未访问
queue<int>* Q = new queue<int>;
for(int i=1;i<numVertex+1;i++)
if(getMark(i)==0) BFS(start,Q);
cout<<endl;
}
/*int[] get(int s){
int D[this->n()+1];
for (int i=1;i<this->n()+1;i++){
D[i]=INFINITY;
}
for (int i=1;i<n();i++)
setMark(i,0); //根本就可以不用this指了
return D;
}*/
/* void Dijkstra(int* D, int s){
int i,v,w;
D[s] = 0;
for(i = 1; i<n()+1; i++)
setMark(i,0);
for (i=1; i<numVertex+1; i++){
v = minVertex(D);
if (D[v]==INFINITY) return;
setMark(v,1);
for (w = first(v); w<n()+1; w=next(v,w))
if(D[w] > (D[v]+weight(v,w)))
D[w] = D[v]+weight(v,w);
}
}*/
/* void dijkstra( int* D) {
int* vis = new int[numVertex+1];
int pre[numVertex+1];
int k;
for (int i=1;i<n()+1;i++)
vis[i]=0;
for (int i = 1; i <= n(); i++) {
D[i] = 1000000;
}
D[1] = 0;
pre[1] = 0;
vis[1] = 1;
for (int i = 1; i <= n(); i++) {
if (matrix[1][i] != 0) {
D[i] = matrix[1][i]; pre[i] = 1;
}
}
for (int i = 1; i <= n()-1; i++) {
int minn = 1000000;
for (int j = 1; j <= n(); j++) {
if (minn > D[j] && vis[j] == 0) {
minn = D[j];
k = j;
}
}//找出目前最小的一个点
vis[k] = 1;
for (int j = 1; j <= n(); j++) {
if (vis[j] == 0 && d[j] > d[k] + graph[k][j]&&graph[k][j]!=0) {
d[j] = d[k] + graph[k][j];
pre[j] = k;
}
}
}
for (int i = 2; i <= N; i++) {
string s = "";
s = getstring(d[i]);
int x = i;
while (x != 0) {
s = getstring(x) + " " + s;
x = pre[x];
}
cout <<s<< endl;
}
}
string getstring(int x) {
string s = "";
int z = x;
char c;
while (z > 0) {
c = z % 10 + '0';
z /= 10;
s = c + s;
}
return s;
}*/
int minVertex(int* D){
int i, v=-1;
for (i=1; i<this->n()+1;i++)
if (this->getMark(i) == 0) {v=i;break;}
for (i++;i<this->n()+1;i++)
if(this->getMark(i)==0 && (D[i]<D[v]))
v=i;
return v;
}
/*void isLoop(){
for (int v=1;v<this->n()+1;v++) //+1
this->setMark(v,0); //首先全部都标记未访问
for (int v=1;v<this->n()+1;v++) //+1
//从1开始,一个一个遍历
if (this->getMark(v)==0)
DFS(v);
}*/
bool isLoop(){
int i;
for(i=1;i<numVertex+1;i++)
setMark(i,0);
for(i=1;i<n()+1;i++)
if(getMark(i)==0)
{
if (helpfindcircle(i,-1)==1)
return true;
}
return false;
}
void PreVisit(int v){
cout<<v<<' ';
}
/* void helpDFS(int v) {
PreVisit(v);
setMark(v, 1);
for (int w=first(v); w<numVertex+1; w = next(v,w))
if (getMark(w) == 0)
helpDFS(w);
}
void Dfs(){
int i;
for(i=1;i<numVertex+1;i++)
setMark(i,0);
for(int i=1;i<numVertex+1;i++)
if(getMark(i)==0)
helpDFS(i);
cout<<endl;
}*/
void Dfs() {
count=0;
for(int i=1;i<numVertex+1;i++)
setMark(i,0);
for (int i = 1; i <= n(); i++) {
if (getMark(i)==0) {
dfs(i);
}
}
cout << endl;
}
void dfs(int u) {
setMark(u,1);
if(count!=n()-1) {cout<<u<<" ";count+=1;}//?????实现Previsit式的输出
else cout<<u;
for (int i = 1; i <= n(); i++) {
//if (getMark(i)==1) flag = 1;
if (getMark(i) == 0 && matrix[u][i] != 0) {
dfs(i);
}
}
}
};
int N;
int d[10000];
int vis[10000]={0};
int pre[10000]={0};
int k;
string getstring(int x) {
string s = "";
int z = x;
char c;
while (z > 0) {
c = z % 10 + '0';
z /= 10;
s = c + s;
}
return s;
}
void dijkstra(int **graph) {
memset(vis, 0, sizeof(vis));
//cout << "输出1到剩余各节点的最短路径" << endl;
for (int i = 1; i <= N; i++) {
d[i] = 1000000;
}
d[1] = 0;
pre[1] = 0;
vis[1] = 1;
for (int i = 1; i <= N; i++) {
if (graph[1][i] != 0) {
d[i] = graph[1][i]; pre[i] = 1;
}
}
for (int i = 1; i <= N-1; i++) {
int minn = 1000000;
for (int j = 1; j <= N; j++) {
if (minn > d[j] && vis[j] == 0) {
minn = d[j];
k = j;
}
}//找出目前最小的一个点
vis[k] = 1;
for (int j = 1; j <= N; j++) {
if (vis[j] == 0 && d[j] > d[k] + graph[k][j]&&graph[k][j]!=0) {
d[j] = d[k] + graph[k][j];
pre[j] = k;
}
}
}
for (int i = 2; i <= N; i++) {
string s = "";
s = getstring(d[i]);
int x = i;
while (x != 0) {
s = getstring(x) + " " + s;
x = pre[x];
}
cout <<s<< endl;
}
}
struct e{
int x,y;
int w;
}E[3002];
int pres[1002]={-1};
bool cmp(e e1, e e2){
return e1.w< e2.w;
}
int find(int i){
if (pres[i]==-1) return i;
else return pres[i]=find(pres[i]); //这一步很巧妙,如果直接return find(pre[i]),则只是找到了根节点罢了,
//但是这样写,找到的同时,又全部在这一过程中压缩了路径,使其全部都等于根节点了。
}
void Add(e e1){
int y = find(e1.x);
int y2 = find(e1.y);
//pre[e1.x] = y2; 这是错的!!!! 要从根节点上改变,否则是错误的只是将当前这个结点又换了一个根罢了
pres[y] =y2;
}
int main()
{
cin>>N;
int m;
cin>>m;
Graphm g(N);
//int s,e,w;
for(int i=0;i<m;i++){
cin>>E[i].x>>E[i].y>>E[i].w;
g.setEdge(E[i].x,E[i].y,E[i].w);
}
//cout<<"-----------"<<endl;
g.graphTraverse2(1); //从1开始
g.Dfs();
dijkstra(g.matrix);
memset(pres,-1,sizeof(pres));
sort(E,E+m,cmp);
//cout<<"-----------------"<<endl;
//int weight=0;
int count=0;
for (int i=0;i<m;i++){
int a = find(E[i].x);
int b = find(E[i].y);
if (a!=b){
count+=1;
//cout<<count<<":"<<a<<"-"<<b<<" ";
Add(E[i]);
//weight+=E[i].w;
cout<<E[i].x<<" "<<E[i].y<<" "<<E[i].w<<endl;
if (count==N-1) break; //边足够了
}
}
bool ans = g.isLoop();
if (ans) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
return 0;
}
第二天改证重写,注释在其中,很详细了
//写完后,感觉有点不足的是,Prim算法里面存边没有用矩阵,还是用了strcut结构体来存,好像跟这个图使用矩阵实现有点别扭。
//并且,我增加了许多私有变量,为了方便我的实现。
#include <iostream>
#include <queue>
#include <algorithm>
#include <memory.h>
using namespace std;
//由题意:1、无向图 --矩阵对称 2、只有为正的路径(否则哪来的输出0)---0为表示是否有边存在
class Graphm{
//第一次写的时候:对类里面的操作不够熟悉1、在所有的操作上在this,并且没有直接用变量---多此一举,本来就是默认this
// 2、在函数内部应该直接使用私有变量,封装的public函数应该是对外面的,私有公有都是面向外面的。
//使用下标开始的问题:题目是从1开始,①下标从1,不过所有for循环都要加一,---很容易忘(第一次)
// ②还是从0,在输入的时候-1,输出的时候加1(这次)
private:
int numVertex,numEdge;
int (**matrix);
int *mark; //我不知道为啥要用指针,这两项
int INFINITY = 99999999;
int cnt=0;//辅助
//记录被选上的边
struct e{
int s;
int e;
int w;
};
int *father;
public:
Graphm(int n){
numVertex = n;
numEdge=0;
matrix =(int **) new int*[n];//这句话一定要在下一句话的前面
for(int i=0;i<n;i++)
matrix[i]=new int[n]; //注意二维矩阵的初始
for (int i=0;i<n;i++)
for(int j=0;j<n;j++)
matrix[i][j]=0;
mark= new int[n];
}
~Graphm(){
for (int i=0;i<numVertex;i++)
delete []matrix[i];
delete []matrix;
delete []mark;
}
public:
//我觉得这都是外面操作的,实际在类里面不用这么操作
void setMark(int v, int val){mark[v]=val;}
int getMark(int v) {return mark[v];}
void setEdge(int v1, int v2, int weight){
if (matrix[v1][v2]==0) numEdge++;//非常严谨的思路
matrix[v1][v2] = matrix[v2][v1] = weight; //顺便还可以改变已有的 //无向图与有向图区别
}
void delEdge(int v1, int v2){
matrix[v1][v2] = matrix[v2][v1]=0; //无向图与有向图区别
}
int weight(int v1, int v2){
return matrix[v1][v2];
}
bool isEdge(int v1, int v2){
if (matrix[v1][v2]!=0) return true;
return false;
}
int n(){return numVertex;}
int e(){return numEdge;}
//下面才是题目要求实现的函数,并且会用到的封装函数
//为了方便操作而封装:
int first(int v){ //找到v的第一个邻居:是从坐标来看的
for (int i=0;i<numVertex;i++){
if (matrix[v][i]!=0) //无向图有向图都适用
return i;
}
return numVertex; //没有则返回numVertex(与后面的地方对应,十分巧妙)
}
int next(int v, int w){ //找到v的w邻居的下一个邻居
for (int i=w+1;i<numVertex;i++){
if (matrix[v][i]!=0)
return i;
}
return numVertex;
}
//真正地实现函数:
//①:深搜,不得不使用一个帮助函数
void DFS(){
for(int i=0;i<numVertex;i++)
mark[i]=0;
for(int i=0;i<numVertex;i++)
if (mark[i]==0) //这一句话!!!
helpDfs(i,0);
}
//②宽搜,可以不需要一个辅助函数:用queue写,就不用递归---不过和书上遍历那里讲得一样,还是辅助一下,所以将其划为私有。
void BFS(){
cnt=0;
queue<int>* q= new queue<int>;
for(int i=0;i<numVertex;i++)
mark[i]=0;
for (int i=0;i<numVertex;i++) //注意:还是得有这个循环,第一次的时候去掉了:因为只宽搜一个顶点,不一定能全部遍历完所有顶点。---并且这里还是从0开始的
if(mark[i]==0) //这一句话!!!
helpBfs(i,q);
delete q;
}
//单源最小路径
//需要一个辅助函数:找D中每次未标记的最小值--路径最短的顶点
void Dijkstra(int s){
//初始准备
for (int i=0;i<numVertex;i++)
mark[i]=0; //这个不是记录是否访问过,而是记录其的值是否固定,是否已经找到最小值了。
int D[numVertex];
int pre[numVertex]; //记录路径,初始化不能达到为-1;
memset(pre,-1,sizeof (pre));
//pre[s]=-1;可要可不要,初始结点是知道的
for (int i=0;i<numVertex;i++)
D[i]=INFINITY;
D[s]=0;
//开始操作
for (int i=0;i<numVertex;i++){ //最先是从s开始,D[s]=0;一共确定numVertex次,每次确定一个最小
int min = minVertex(D);
if (min==-1) return; //说明剩下的点都无法到达,直接结束
//找到了,那么剩下的值随着更新
mark[min]=1; //固定这个值
for (int i=first(min);i<numVertex;i=next(min,i)){
if (mark[i]==0 && D[i]>D[min]+matrix[min][i]){ //对于标记了的这一条一定不成立,所以可以不用交未标记那句话
D[i]=D[min]+matrix[min][i];
pre[i]=min;
}
}
}
//D,pre记录了最小路径的值,和路径
//输出--只写s=1输出的情况:所以就从i=1遍历即可,若是其他,则还要看输出的顺序
for (int i=1;i<numVertex;i++){
if(D[i]==INFINITY) cout<<0+1<<" "<<i+1<<0<<endl;
else{
cout << 0+1<<" ";
string s="";
int x = i;
while (pre[x]!=-1){
s=std::to_string(x+1)+" "+s; //输出+1
x = pre[x];
}
cout<<s<<D[i]<<endl;
}
}
}
//最小生成树---这种算法出来边不是从大到小的顺序,是随机的
//另外一个最小生成树算法Kruskal:还要在弄一个辅助函数add和pre来添加边,可能最先的setEdge需要用struct node存一下来选,但是这个算法找到就可以输出,不用像上一个一样排序。
void Prim(){
for(int i=0;i<numVertex;i++)
mark[i]=0;
int D[numVertex];
for (int i=0;i<numVertex;i++)
D[i]=INFINITY;
D[0]=0; //随便选一个起始点
//记录被选的边 更新后不一定是最小的,可能有多次更新,所以就不能用cnt直接存,应该对应每一个点。
struct e E[numVertex];
E[0].s=0;E[0].e=0;E[0].w=INFINITY;//不输出,使其在最后
//cnt = 0;
//开始操作
for (int i=0;i<numVertex;i++){ //执行numVertex,每个顶点都看一遍,选出MST
int min = minVertex(D);
if (min==-1) return; //不存在最小生成树
mark[min]=1; //mark用于是否被选上,就是将原来的点分为了两个集合
for (int i=first(min);i<numVertex;i=next(min,i))
if (mark[i]==0 && D[i]>matrix[min][i]){
D[i]=matrix[min][i]; //更新后不一定是最小的,可能有多次更新,所以就不能直接存。
//E[cnt].s = min;E[cnt].e=i;E[cnt].w=matrix[min][i];
E[i].s=min;E[i].e=i;E[i].w=matrix[min][i];
}
}
//输出
sort(E,E+numVertex,cmp);
for (int i=0;i<numVertex-1;i++) //不输出最后一条,起始点的
if (E[i].s<E[i].e) //输出顺序
cout<<E[i].s+1<<" "<<E[i].e+1<<" "<<E[i].w<<endl; //输出+1
else
cout<<E[i].e+1<<" "<<E[i].s+1<<" "<<E[i].w<<endl; //输出+1
}
//判断有无环存在,无向图 ---用并查集来判断的话,MST最好就写Kruskal了
bool hasCircle(){
father = new int[numVertex];
for (int i=0;i<numVertex;i++)
father[i]=-1;
for (int i=0;i<numVertex;i++)
for (int j=i;j<numVertex;j++){ //无向图遍历可以只遍历一半
if (matrix[i][j]!=0){
int y1 = find(i);
int y2 = find(j);
if (y1==y2) return true; //同根了,但是现在又要添加一边,一定有一个回路
else father[y1]=y2; //不同根,则连接起来
}
}
return false;
delete[] father;
}
private:
int find(int v){
if (father[v]==-1) return v;
return father[v]=find(father[v]); //顺带压缩
}
//对应struct e的排序--cmp必须static
bool static cmp(struct e e1,struct e e2){
return e1.w<e2.w;
}
int minVertex(int *D){
int minCost=INFINITY;
int flag=-1;
for (int i=0;i<numVertex;i++){
if (minCost>D[i] && mark[i]==0){
minCost=D[i];
flag=i;
}
}
return flag;
}
void helpDfs(int v, int cnt){
//Previsit(v)操作------因为本文实现的是从1开始,输出都加1, 再加上控制最后不输出空格:①可以像写的那样做 ②还可以额外增加一个私有变量cnt,每次令其=0--看BFS
if(cnt!=numVertex-1) {cout<<v+1<<" ";cnt+=1;}
else cout<<v+1<<endl;
mark[v]=1;
for (int i = first(v); i<numVertex;i=next(v,i)){
if (mark[i]==0)
helpDfs(i,cnt);
}
}
void helpBfs(int v, queue<int>* q){
q->push(v);
mark[v]=1;
while (!q->empty()) {
int a = q->front();
q->pop();
//Previsit(a)
if (cnt!=numVertex-1) {cout<<a+1<<" ";cnt+=1;}
else cout<<a+1<<endl;
//mark[v]=1; //就在这里设置标记就可以,这里会访问到所有结点,或是像书上那样在两处---------不可以,只能在两处!!!因为这里只会pop出来的时候标记,而后面的没有pop出来的没有标记,在遍历邻居时,又会把它加一遍
for(int i=first(a);i<numVertex;i=next(a,i))
if (mark[i]==0){
q->push(i);
mark[i]=1;
}
}
}
};
int main(){
int n,m;
cin>>n>>m;
Graphm g(n);
int s,e,w;
for (int i=0;i<m;i++){
cin>>s>>e>>w;
g.setEdge(s-1,e-1,w); //因为里面全是从0开始
}
//cout<<"------";
g.BFS();
g.DFS();
g.Dijkstra(1-1);
g.Prim();
bool a = g.hasCircle();
if (a) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
最后
以上就是积极鼠标为你收集整理的图的操作的全部内容,希望文章能够帮你解决图的操作所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复