数据结构 | C++ 图的链式操作
模板的用法:
在函数或类前
template<class T>
在函数体或类体中将需要的数据类型替换成T即可.在main()调用函数时,定义好实参的数据类型即可合法使用该函数.
模板的意义在于不必写若干函数的重载.
其中,模板声明中的"class"用"typename"更合适,'T'也可用任意字符代替
template<typename T>
ALGraph.h文件
//邻接表
#include<vector>
#include<queue>//STL
enum GraphType {undigraph,digraph};//定义图类型:有向图和无向图
边类和顶点类
//边类
struct EdgeNode
{
int adjvex; //将结点用1,2,3...表示位置
EdgeNode *nextnode; //指向下一个节点
/*可有一个数据成员表示权*/
};
//顶点类
struct VexNode
{
char data; //顶点的值
EdgeNode *firstedge; //指向对应边表的头指针
};
//注意顶点类有EdgeNode定义,所以两个结构体的顺序固定
图类
//图类
class ALGrape
{
public:
ALGrape(GraphType k,vector<char> vex, int v, int e);//图类型,储存各顶点值的数组,顶点数,边数
~ALGrape();
void DFSTraverse(); //一般图 深度优先遍历
void BFSTraverse(); //一般图 广度优先遍历
private:
int vexnum; //顶点数
int edgenum; //边数
vector <VexNode> vexlist ; //顶点表
GraphType kind; //图的类型
void DFS(int v, bool *visited); //连通图 深度优先遍历
};
构造函数算法思想:
每条边连接va,vb首尾顶点,将va的序号①链入vb为头节点的链表,再将vb的序号②链入va为头节点的链表.
顶点表中每一个元素都和一个序号对应,代码运行的也是这些序号,要访问时就vexlist[v].data即可
//构造函数
ALGrape::ALGrape(GraphType k , vector<char> vex, int v, int e)//vector定义形参时,不要加[]
{
EdgeNode *p;
kind = k;
vexnum = v;
edgenum = e;
vexlist.resize(vexnum); //意思是:将vexlist的元素个数调整为vexnum个.
for (int i = 0; i < vexnum; ++i)//初始化顶点表
{
vexlist[i].data = vex[i]; /*当vexlist数据类型 为模板时,显示vexlist无法用点运算符*/
vexlist[i].firstedge = NULL;
}
int va, vb;//一条边邻接的两个顶点的序号
for (int i = 0; i < edgenum; ++i)
{
if (kind == undigraph)
{
cout << "无向图第" << i + 1 << "条边的始顶点" << endl;
cin >> va;
cout << "无向图第" << i + 1 << "条边的尾顶点" << endl;
cin >> vb;
}
else
{
cout << "有向图第" << i + 1 << "条边的始顶点" << endl;
cin >> va;
cout << "有向图第" << i + 1 << "条边的尾顶点" << endl;
cin >> vb;
}
p = new EdgeNode; //头插法,一条边连接两个顶点,为va链上vb,也为vb链上va
p->adjvex = vb;
p->nextnode=vexlist[va].firstedge;
vexlist[va].firstedge = p;
p = new EdgeNode;
p->adjvex = va;
p->nextnode = vexlist[vb].firstedge;
vexlist[vb].firstedge = p;
}
cout << "输入成功!" << endl;
}
析构函数
//析构函数
ALGrape::~ALGrape()
{
EdgeNode *p;
for (int j = 0; j < vexnum; j++)
{
p = vexlist[j].firstedge;//p指向头指针指向的首元结点
while (p)
{
vexlist[j].firstedge = p->nextnode;//头删法
delete p;
p = vexlist[j].firstedge;
}
}
cout << "析构函数运行成功" << endl;
}
深度遍历函数:
DFS(v,visited)实现连通图遍历,DFSTraverse()实现一般图(等价于多个连通图)遍历,所以在main()中只调用,DFSTraverse()
//一般图 深度优先遍历
void ALGrape::DFSTraverse()//一个一般图==多个连通图
{
bool *visited = new bool[vexnum];//不能使用 "bool visited[vexnum]; ",因为this指针无法用于常量表达式
for (int i = 0; i < vexnum; i++)
{
visited[i] = false; //初始化
}
for (int i = 0; i < vexnum; i++)
{
if (!visited[i]) //若哪个结点还未访问,就用DFS();
{
DFS(i,visited);
}
}
delete[] visited;
}
//连通图 深度优先遍历
void ALGrape::DFS(int v,bool *visited)//*表示形参可修改
{
cout << vexlist[v].data<<" ";
visited[v] = true; //为了这一句,才有了bool *visited 这个形参
EdgeNode *p = vexlist[v].firstedge; //工作指针指向第一个边结点
while(p)
{
if (!visited[p->adjvex]) //若结点未被访问
{
DFS(p->adjvex, visited); //递归,不满足条件时返回上一层递归
}
p = p->nextnode;
}
}
广度遍历函数:
DFS()在访问完当前结点所有邻结点后,可以退回上一个结点接着操作.这种回退需要递归.
BFS()没有回退,不能使用递归.
广度遍历算法要保证 先被访问的结点的邻接点 先于 后被访问的结点的邻接点 被访问.
为了实现这种逐层按序访问,需要一个队列(特点:先进先出)存储每次访问的所有邻接点
//一般图 广度遍历
void ALGrape::BFSTraverse()
{
bool *visited = new bool[vexnum];//建立辅助数组,初始化
for (int i = 0; i < vexnum; i++)
{
visited[i] = false;
}
queue<int>q; //建立存储顺序的队列
for (int j = 0; j < vexnum; j++) //该层循环可应对多个联通分量的情况
{
if (!visited[j])
{
cout << vexlist[j].data<<" "; //输出第一个顶点值
visited[j] = true;
q.push(j);
while (!q.empty())//队列非空
{
int w = q.front(); q.pop();//第一个元素赋给w并出队
for (EdgeNode *p = vexlist[w].firstedge; p; p = p->nextnode)//遍历该点的每一条边
{
if (!visited[p->adjvex])
{
cout << vexlist[p->adjvex].data<<" ";
visited[p->adjvex] = true;
q.push(p->adjvex);
}
}
}//end while
}
}
delete[] visited;//有new[] 必有delete[]
}
main()函数
using namespace std;
#include <iostream>
#include"ALGraph.h"
int main()
{
vector<char> a = {'a','b','c','d','e' };
int e1;//边数
g1:cout << "输入边数"<<endl;
cin >> e1;
if (e1 < 0)
{
system("cls");
cout << "输入的值非法!" << endl; goto g1;
}
else
{
ALGrape alg(undigraph, a, 5, e1);
alg.DFSTraverse();
}
vector<char> b = { 'a','b','c','d','e','f' };
int e2;//边数
g2:cout << "输入边数"<<endl;
cin >> e2;
if (e2 < 0)
{
system("cls");
cout << "输入的值非法!" << endl; goto g2;
}
else
{
ALGrape alg(undigraph, b, 6, e2);
alg.BFSTraverse();
}
system("pause");
return 0;
}
问题总结:
bug1:边类和结点类行文顺序写反了,系统编译错误
bug2:void ALGrape::DFSTraverse()中
不能使用 "bool visited[vexnum]; ",因为this指针无法用于常量表达式
解决方案是new一个bool数组:
bool *visited = new bool[vexnum];
然后就初始化
for (int i = 0; i < vexnum; i++)
{
visited[i] = false;
}