1、下面来通过这一道题 回顾回顾图这一张的相关内容

7-1 列出连通集 (30 分)
给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N-1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。

输入格式:
输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。

输出格式:
按照"{ v
​1
​​ v
​2
​​ ... v
​k
​​ }"的格式,每行输出一个连通集。先输出DFS的结果,再输出BFS的结果。

输入样例:
8 6
0 7
0 1
2 0
4 1
2 4
3 5
输出样例:
{ 0 1 4 2 7 }
{ 3 5 }
{ 6 }
{ 0 1 2 7 4 }
{ 3 5 }
{ 6 }
作者: 陈越
单位: 浙江大学
时间限制: 400 ms
内存限制: 64 MB

 

一开始,我想的是用邻接矩阵来写这一道题。

#include<iostream>
using namespace std;
#define MVNum 11
bool visited[MVNum] = {false};
#include<queue>
    
typedef struct ArcNode{
//
    int adjvex; //该边指向的顶点的位置 
    struct ArcNode *nextarc;  //指向下一条边的指针 ???
}ArcNode;

typedef struct VNode{
//    顶点 
    int data;  //顶点的序号 
    ArcNode *firstarc;  //指向第一条依附于该顶点的边的指针 
}VNode,AdjList[MVNum];

typedef struct{
//
    AdjList vertices;  //存储顶点的数组 
    int vexnum,arcnum;  //该图所包含的顶点数和边数 
}ALGraph;


void CreateUDG(ALGraph &G)
{ //采用邻接表表示法,创建无向图G 
cin>>G.vexnum>>G.arcnum; //输入顶点数边数 
    for(int i=0; i<G.vexnum; ++i)
    { //输入各点,构造表头结点表 
       G.vertices[i].data = i; //各顶点值就是对应的i值 
       G.vertices[i].firstarc = NULL;             
       //初始化表头结点的指针域为NULL 
    }
    
    for(int k=0; k<G.arcnum; ++k)
    {    //输入各边两端的顶点,构造邻接表 
    int v1,v2;
       cin >> v1 >> v2 ; //输入边的两个顶点 
       ArcNode *p1 = new ArcNode;  //生成一个新的边结点*p 
       p1->adjvex=v2; //邻接点序号为v2
       //头插法插入到G.vertices[v1].firstarc指向的结点之前
       p1->nextarc=G.vertices[v1].firstarc;  
       G.vertices[v1].firstarc = p1;
       
       ArcNode *p2 = new ArcNode;  //生成一个新的边结点*p 
       p2->adjvex=v1; //邻接点序号为v1
       //头插法插入到G.vertices[v2].firstarc指向的结点之前
       p2->nextarc=G.vertices[v2].firstarc;  
       G.vertices[v2].firstarc = p2;
    }
}


int FirstAdjVex(ALGraph G, int v)//返回顶点v的第一个邻接点序号
{
    
    ArcNode *p=G.vertices[v].firstarc;
    if(p)
        return p->adjvex;
    else
        return -1;
}


int NextAdjVex(ALGraph G, int v, int w)//返回顶点v的相对于w的下一个邻接点序号
{
 
    ArcNode* p=G.vertices[v].firstarc;
    while(p) 
    {
        if(p->adjvex==w)
            break;
        p=p->nextarc;
    }
 
    if(p->adjvex!=w || !p->nextarc)//如果没找到w或者w是最后一个邻接点
        return -1;
    return p->nextarc->adjvex;
 
}

void DFS(ALGraph G, int v){
    cout<<' '<<v<<' ';
    visited[v] = true;
    for(int w = FirstAdjVex(G , v);w>=0;w = NextAdjVex(G,v,w)){
        if(!visited[w]){
            DFS(G , w);
        }
    }
}

void DFSTraverse(ALGraph G){
    
    for(int v = 0;v<G.vexnum ; ++v){
        visited[v] = false; 
    }
//    访问标志数组的初始化 
    for(int v = 0;v<G.vexnum ; ++v){
        
        if(!visited[v]){
            cout<<"{";
            DFS(G , v);
            cout<<"}"<<endl;
        }
    }
}
void BFS(ALGraph G , int v){
    
    queue<int> q;
    visited[v] = true;
    cout<<' '<<v<<' ';
    q.push(v);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for(int w = FirstAdjVex(G,u);w>=0;w = NextAdjVex(G, u , w)){
            if(!visited[w]){
                cout<<' '<<w<<' ';
                visited[w] = true;
                q.push(w);
            }
        }
            }
        }
    

void BFSTraverse(ALGraph G){
    
    for(int v = 0;v<G.vexnum ; ++v){
        visited[v] = false; 
    }
//    访问标志数组的初始化 
    for(int v = 0;v<G.vexnum ; ++v){
        
        if(!visited[v]){
            cout<<"{";
            BFS(G , v);
            cout<<"}"<<endl;
        }
    }
}

int main(){
    ALGraph G;
    CreateUDG(G);
    DFSTraverse(G);
    BFSTraverse(G);
    
    return 0;
}
邻接矩阵

可是结果却与答案有略微差别。

仔细比对后发现是1、2的顺序颠倒了。为什么会这样呢?实际上无论是进行DFS还是BFS时都可能不止有一种结果。以深搜为例,到“依次检查v的所有邻接点”这一步时,如果没有特殊规定的话,可允许有多种遍历邻接点的顺序,广搜亦如是。
但是,仔细读题可发现题目有这样的具体要求:“进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。”意思是题目已经默认像这样的情况我们优先遍历编号小的顶点。
而对于邻接表,这个操作就很难实现。要从小到大排序,数组肯定要比链表来得容易啊!纵观命题者意图,貌似暗示我们用邻接矩阵。于是我决定不再纠结,另起炉灶使用邻接矩阵!

写邻接矩阵很顺利,因为相对而言比邻接表更简单。等我写完之后跑起来,却发现几乎读不进数据,总是出现一些稀奇古怪的结果。经过多次仔细检查发现原来是我没有使用&把AMGraph传到函数内部。

#include<iostream>
using namespace std;
#define MVNum 10
#include<queue>
bool visited[MVNum];

typedef struct{
    int vexs[MVNum];
//    一位数组 存放顶点
    int arcs[MVNum][MVNum];
//    二维数组 邻接矩阵
    int vexnum,arcnum;
//    当前图的顶点数、边数 
}AMGraph;

void CreateGraph(AMGraph &G){
//  如何将输入的信息->无向图
    cin>>G.vexnum>>G.arcnum;
//    依次输入顶点数目和边的数目 
    for(int i = 0 ; i < G.vexnum ; i++){
        G.vexs[i] = i;
    }    
//    在本题中点的编号就是其序号 
    for(int m = 0 ; m < G.vexnum ; m++)
        for(int n = 0 ; n< G.vexnum ; n++)
            G.arcs[m][n] = 0;
        
//    邻接矩阵初始化 
    for(int k = 0 ; k <G.arcnum ; k++){
        int v1,v2;
        cin>>v1>>v2;
//        v1、v2分别是一条边两个顶点的端点 
        G.arcs[v1][v2] =1;
        G.arcs[v2][v1] =1;
    }
//    两点之间有边:将值改为1进行标记 
}

void DFS(AMGraph &G , int v){
    cout<<v<<' ';
    visited[v] = true;
    for(int w = 0 ; w < G.vexnum ; w++)
        if((G.arcs[v][w] == 1)&&(!visited[w]))  DFS(G,w);
}

void DFSTraverse(AMGraph &G){
    for(int v = 0 ; v <G.vexnum ; v++)  visited[v] = false;
//    初始化visited数组 一旦访问将其置为true 
    for(int v = 0 ; v <G.vexnum ; v++){ 
        if(!visited[v]){
            cout<<"{"<<' ';
            DFS(G,v);
            cout<<"}"<<endl;} 
    } 
}

int FirstAdjVex(AMGraph &G , int u){
    for(int i = 0 ;i<G.vexnum ; i++){
        if(G.arcs[u][i] == 1){
            return i;
        }
    }
    return -1;
}

int NextAdjVex(AMGraph &G ,int u , int w){
    for(int i = w+1; i<G.vexnum; i++){
        if(G.arcs[u][i] == 1){
            return i;
        }
    }
    return -1;
}

void BFS(AMGraph &G , int v){
    queue<int> q;
    visited[v] = true;
    cout<<v<<' ';
    q.push(v);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for(int w = FirstAdjVex(G,u) ; w >= 0 ; w = NextAdjVex(G, u , w)){
            if(!visited[w]){
                cout<<w<<' ';
                visited[w] = true;
                q.push(w);
            }
        }
            }
        }

void BFSTraverse(AMGraph &G){
    for(int v = 0 ; v <G.vexnum ; v++){
        visited[v] = false;
    }
    for(int v = 0 ; v<G.vexnum ; ++v){
        if(!visited[v]){
            cout<<"{"<<' ';
            BFS(G,v);
            cout<<"}"<<endl;        }
    }
}

int main(){
    AMGraph G;
    CreateGraph(G);
    DFSTraverse(G);
    BFSTraverse(G);
    return 0;
}
邻接矩阵1

这一下终于顺利通过了!但是和同学交流之后发现还可以更优化,代码更清晰简洁:

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int n,m,v[11],Map[11][11];
void DFS(int x)
{
    cout<<' '<<x;
    for(int i=0; i<n; i++)
    {
        if(!v[i]&&Map[x][i]==1)
        {
            v[i]=1;
            DFS(i);
        }
    }
}
void BFS(int x)
{
    int y;
    queue<int>Q;
    Q.push(x);
    while(!Q.empty())
    {
        y=Q.front();
        cout<<' '<<y;
        Q.pop();
        for(int i=0; i<n; i++)
        {
            if(!v[i]&&Map[y][i]==1)
            {
                v[i]=1;
                Q.push(i);
            }
        }
    }
}
int main()
{
    int x,y;
    cin>>n>>m;
    memset(Map,0,sizeof(Map));
    memset(v,0,sizeof(v));
    while(m--)
    {
        cin>>x>>y;
        Map[x][y]=Map[y][x]=1;
    }
    for(int i=0; i<n; i++)
    {
        if(v[i]==0)
        {
            v[i]=1;
            cout<<"{";
            DFS(i);
            cout<<" }"<<endl;
        }
    }
    memset(v,0,sizeof(v));
    for(int i=0; i<n; i++)
    {
        if(v[i]==0)
        {
            v[i]=1;
            cout<<"{";
            BFS(i);
            cout<<" }"<<endl;
        }
    }
    return 0;
}
邻接矩阵2

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

5.19更新

深入理解迪杰特斯拉算法

 

老师布置的作业题里面有一道要求用迪杰特斯拉算法求最短路径。我看了一遍课本,但还是有一点云里雾里,最后在网上看到了一篇文章,感觉写的很清晰,让我理解起来也很容易。下面来简单总结一下:

1、相关概念:

迪杰特斯拉算法是干什么的:用于对有权图进行搜索,找出图中两点的最短距离。

两个集合:

S:已求出最短路径的顶点集合。初始条件为一个源点。

U:未确定最短路径的顶点集合。

操作步骤:按最短路径长度的递增次序依次把U组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。

 

2、用例子理解

3、填表: