图
1.图的邻接矩阵存储结构
#define MAXVERTEXNUM 100
typedef char VertexType; //顶点的数据类型
typedef int EdgeType; //带权图中边上全权值的数据类型
typedef struct MGraph{
VertexType Vex[MAXVERTEXNUM]; //顶点表
EdgeType Edge[MAXVERTEXNUM][MAXVERTEXNUM]; //邻接矩阵,边表
int vexnum,arcnum; //图的当前顶点数和弧数
}MGraph;
2.图的邻接表存储结构
#define MAXVERTEXNUM 100
typedef struct ArcNode{ //边表节点
int adjvex; //该弧所指向的顶点的位置
struct ArcNode *next; //指向下一条弧的指针
InfoType info; //网的边权值
}ArcNode;
typedef struct VNode{ //顶点表节点
VertexType data; //顶点信息
ArcNode *first; //指向第一条依附该顶点的弧的指针
}VNode,AdjList[MAXVERTEXNUM];
typedef struct ALGraph{
AdjList vertices; //邻接表
int vexnum,arcnum; //图的顶点数和弧数
}ALGraph;
3.图的遍历
3.1广度优先搜索(BFS)
类似于树的层次遍历
bool visited[MAX_VERTEX_NUM]; //访问标记数组
void BFSTraverse(Graph G){ //对图G进行广度优先遍历
queue<int> q; //初始化辅助队列
for(int i=0;i<G->vexnum;i++) //将标记数组初始化
visited[i]=false;
for(int i=0;i<G->vexnum;i++) //从0号顶点开始遍历
BFS(G,i,q);
}
void BFS(Graph G,int v,Queue<int> &queue){
visit(v); //访问初始顶点v
visited[v]=true; //标记v已经访问
queue.push(v); //将v入队列
while(!queue.empty()){
v=queue.front(); //将队列中第一个出队
queue.pop();
for(int w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){ //检测v所有邻接点
if(!visited(w)){ //w为v的尚未访问的邻接顶点
visit(w); //访问w
visited[w]=true; //标记w已经访问
queue.push(w); //将w入队
}
}
}
}
3.1.1BFS算法求单源最短路径问题
void BFS_MIN_Distance(Graph G,int u){
//d表示从u到i节点的最短路径,-1为没有路径
int d[vexnum];
for(int i=0;i<G->vexnum;i++)
d[i]=-1; //初始化路径长度
queue<int> q;
d[u]=0;
visited[u]=true;
q.push(u);
while(!q.empty()){ //BFS算法主过程
u=q.front(); //队头元素出队
q.pop();
for(int w=FirstNeighbor(G,u);w>=0;w=NextNeighbor(G,u,w)){
if(!visited[w]){ //w为u未访问过的邻接节点
d[w]=d[u]+1; //路径长度+1
visited[w]=true; //设置w已经访问
q.push(w); //将其入队
}
}
}
}
3.1.2广度优先生成树
3.2深度优先搜索(DFS)
类似于树的先序遍历
bool visited[MAX_VERTEX_NUM];//标记访问数组
void DFSTraverse(Graph G){ //对图G进行深度优先遍历
for(int i=0;i<G->vexnum;i++) //将标记数组初始化
visited[i]=false;
for(int i=0;i<G->vexnum;i++) //从v=0开始遍历
if(!visited[i])
DFS(G,i);
}
void DFS(Graph G,int v){ //从顶点v出发,深度优先遍历图
visit(v); //访问顶点v
visited[v]=true; //将v标记为访问过
for(int w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){
if(!visited[w]) //w为v尚未访问的邻接节点
DFS(G,w); //递归访问
}
}
4.图的基本操作
取x邻接顶点y的下一个邻接顶点的函数NextNeighbor(G,x,y)
//邻接矩阵作为存储结构
int NextNeighbor(MGraph G,int v,int w){
if(v!=-1&&w!=-1){
for(int col=w+1;col<G->vexnum;col++){
if(G->Edge[v][col]>0&&G->Edge[x][col]<maxWeight)
return col;
}
}
return -1;
}
//邻接表作为存储结构
int NextNeighbor(MGraph G,int v,int w){
if(v!=-1){
ArcNode *a=G->vertices[v]->first; //对应边链表的第一个边节点
while(!p&&p->adjvex!=w){ //寻找邻接顶点w
p=p->next;
}
if(!p&&!p->adjvex)
return p->next->adjvex; //返回下一个邻接顶点
}
}
图对应的算法题
邻接表&&邻接矩阵
1.写出从图的邻接表表示转成邻接矩阵表示的算法
void convert(ALGraph &G,int arcs[M][N]){
for(int i=0;i<n;i++){ //一次遍历各顶点表节点为头的边链表
p=(G->vertices[i]).first; //去除顶点i的第一条出边
while(p!=null){ //遍历边链表
arc[i][p->adjvex]=1;
p=p->next; //取出下一条边
}
}
}
2.2021统考真题
已知无向连通图G由顶点集V和边集E组成,|E|>0,当G中度为奇数的顶点个数不大于2的偶数时,G存在包含所有边且长度为|E|的路径(称为EL路径)。设图G采用邻接矩阵存储,类型如下:
typedef struct{
int numVertices,numEdges;
char VerticesList[MAXV];
int Edge[MAXV][MAXV];
}MGraph;
设计算法int IsExistEL(MGraph G),判断G是否存在EL路径,若存在,则返回1,否则返回0
int IsExistEL(MGraph &G){
int verticesCount=0,edgesCount=0;
for(int i=0;i<G->numVertices;i++){
for(int j=i+1;j<G->numVertices;j++){
if(G->Edge[i][j]==1)
edgesCount++; //计算当前顶点的度
}
if(edgesCount%2!=0) //对度为奇数的顶点计数
verticesCount++;
edgesCount=0;
}
return (verticesCount==0||verticesCount==2)?1:0;
}
DFS&&BFS
1.判断一个无向图是否为一棵树
bool isTree(Graph &G){
for(int i=0;i<G->vexnum;i++)
visited[i]=false;
int vNum=0,eNum=0;
DFS(G,1,vNum,eNum,visited);
//一颗无向图G是一棵树的条件是G必须是无回路的连通图或由n-1条边的连通图
if(vNum==G->vexnum&&eNum=2*(vNum-1)) return true;
else return false;
}
void DFS(Graph &G,int v,int &vNum,int &eNum,int visited[]){
//深度优先遍历,统计访问过的顶点数和边数,通过vNum和eNum返回
visited[v]=true; //访问标记
vNum++;
int w=FirstNeighbor(G,v); //取v的第一个邻接顶点
while(w!=-1){ //当邻边顶点存在
eNum++; //边数+1
if(!visited[w]){
DFS(G,w,vNum,eNum,visited);
w=NextNeighbor(G,v,w);
}
}
}
2.写出图的DFS的非递归算法(采用邻接表形式)
void DFS_Recurrence(ALGraph G,int v){
int visited[G->vexnum];
for(int i=0;i<G->vexnum;i++) //初始化visited,其用于判断顶点是否如果栈
visited[i]=false;
stack<int> s;
s.push(v);
visited[v]=true; //将第一个节点标记为已入栈
while(!s.empty()){
v=s.top(); //取得栈顶节点,并弹出
s.pop();
visit(v); //访问该节点
for(ArcNode *p=G->vertices[v]->first;p!=null;p=p->next){ //遍历该点的邻接弧
int w=p->adjvex; //获取该弧指向的下一顶点号
if(!visited(w)){ //如果该顶点没有入过栈
s.push(w); //将其入栈并标记
vistited(w)=true;
}
}
}
}
3.分别采用DFS和BFS判别以邻接表方式存储的有向图中是否存在由顶点vi到vj(i!=j)的路径
//DFS
void IsHaveRoute_DFS(ALGraph G,int v,int j,bool &result){
if(v==j) //用result来标识是否有路径
result=true;
int visited[G->vexnum];
for(int i=0;i<G->vexnum;i++) //初始化visited,其用于判断顶点是否访问过
visited[i]=false;
visited[i]=true;
for(ArcNode *p=G->vertices[i]->first;p!=null;p=p->next){
int w=p->adjvex;
if(!visited[w]&&!result){ //当前节点未访问过且到目前位置没有找到路径,则继续遍历
IsHaveRoute_DFS(G,w,j,result);
}
}
}
//BFS
//其实就相当于BFS遍历,只不过在遍历过程中加入判断条件当前遍历节点是否和vj相等
void IsHaveRoute_BFS(ALGraph G,int v,int j){
int visited[G->vexnum];
for(int i=0;i<G->vexnum;i++) //初始化visited,其用于判断顶点是否访问过
visited[i]=false;
queue<int> q;
q.push(v);
visited[v]=true;
while(!q.empty()){
v=q.front();
q.pop();
for(ArcNode *p=G->vertices[v]->first;p!=null;p=p->next){
int w=p->adjvex;
if(w==j) return true;
if(!visited[w]{
visited[w]=true;
q.push(w);
}
}
}
}
4.图采用邻接表表示,设计一个算法,输出从顶点vi到顶点vj的所有简单路径
void FindPath(ALGraph G,int v,int j,string &path){
path.append(v); //用一个字符串存放路径
visited(v)=true; //访问标记
if(v=j){
cout<<path; //输出路径
}
for(ArcNode *p=G->vertices[v]->first;p!=null;p=p->next){ //DFS的主要步骤
int w=p->adjvex;
if(!visited[w])
FindPath(G,w,j,path);
}
//回退
path.erase(path.end()-1); //路径回退到上个节点
visited[v]=false; //使该顶点可用
}