图的基础相关知识

至于图是啥,啥是有向图,啥是子图,没啥好介绍的了吧。图论本身是一门比较大的学科,我考试也就刚及格。

图的表示:

邻接矩阵:顶点用一维数组表示,顶点之间的连接关系用二维数组表示。无向图就是对称矩阵,有向图就能通过这个表示方向。

typedef char VertexType;  //顶点类型
typedef int EdgeType;  //边的权值类型
#define MAXVEX 100
#define INFINITY 65535
typedef struct {
    VertexType verxs[MAXVEX];
    EdgeType arc[MAXVEX][MAXVEX];
    int numVertexs, numEdges; //图中当前的顶点树和边数
}MGraph;

邻接表:我们可以发现,当边数相对于顶点数来说较少时,邻接矩阵的表示对存储空间存在极大的浪费,邻接表的处理方式是:

1.顶点用一个一维数组存储,当然,是可以用链表存储的,但是数组比较容易进行读取

2.将每个结点的邻接点构成一个线性表,由于结点的不确定性,使用链表,无向图则是边表,有向图则是出边表。

typedef char VertexType;  //顶点类型
typedef int EdgeType;
typedef struct EdgeNode  //边表结点
{
    int adjvex; //邻接点域,存储该顶点对应的下标
    EdgeType wight; //权值
    struct EdgeNode *next;
}EdgeNode;

typedef struct VertexNode //顶点表结点
{
    VertexType data;
    EdgeNode *firstedge; //边表头指针
}VertexNode, AdjList[MAXVEX];

typedef struct 
{
    AdjList adjList;
    int numVertexs, numEdges;
}GraphAdjList;

十字链表:对于有向图来说,邻接表只能解决出度问题,如果需要解决入度,则需要逆邻接表,这样出度又没了。但是如果将邻接表和逆邻接表结合起来,可能就能解决这个问题。

图的遍历:从图的某一顶点触发访问图中其余顶点,且使每一个顶点仅被访问一次

深度优先遍历:也称为深度优先搜索。比如说从顶点x出发,选择一条x未被检测的边(x,y),如果y已经被标记,就再找边到达下一个点z,如果z没被标记,则标记z,并对z进行深度优先搜索。将y深度优先搜索完毕过后,就再来寻找x的下一条边。

代码实现:


//使用邻接矩阵表示的方式
bool visited[MAX];
//邻接矩阵的深度优先递归算法
void DFS(MGraph G, int i){
    int j;
    visited[i] = true;
    cout << G.verxs[i]; //打印顶点,也可以进行其他的操作,也就是我们这里访问到这个顶点了
    for(j = 0; j < G.numVertexs; j++)
    {
        if(G.arc[i][j] == 1 && !visited[j]) //(i,j)边存在且j顶点未被访问,则进行递归
            DFS(G, j); //对j顶点进行深度优先遍历
    }
}
//邻接矩阵的深度遍历
void DFSTraverse(MGraph G)
{
    int i;
    for (i = 0; i < G.numVertexs; i++) {
        visited[i] = false;  //将所有顶点的访问状态初始化为为访问状态
    }
    for (i = 0; i < G.numVertexs; i++) {
        if(!visited[i])
            DFS(G, i);
    }
}

//邻接表的深度优先递归
void DFS(GraphAdjList GL, int i)
{
    EdgeNode *p;
    visited[i] = true;
    cout << GL.adjList[i].data;
    p = GL.adjList[i].firstedge;
    while (p)
    {
        if(!visited[p->adjvex])
            DFS(GL, p->adjvex);
        p = p->next;
    }
}
//邻接表的深度遍历
void DFSTraverse(GraphAdjList GL)
{
    int i;
    for (i = 0; i < GL.numVertexs; i++) {
        visited[i] = false;
    }
    for (i = 0; i < GL.numVertexs; i++) {
        if(!visited[i])
            DFS(GL, i);
    }
}

广度优先遍历:如果说深度优先遍历类似于树的前序遍历,那么广度优先遍历就类似于树的层序遍历了。

如上图所示的一个图,将其变形,顶点A放置在最上一层,让与它有连接的顶点B、F为第二层,再让与B和F连接的顶点放在第三层CIGE,再将与这四个顶点有连接的顶点放在第四层DH。

//邻接矩阵的广度优先遍历算法
void BFSTraverse(MGraph G)
{
    int i, j;
    Queue Q;
    for (i = 0; i < G.numVertexs; i++) 
        visited[i] = false;
    InitQueue(&Q);
    for (i = 0; i < G.numVertexs; i++) {
        if(!visited[i])
        {
            visited[i] = true;
            cout << G.verxs[i];
            EnQueue(&Q, i); //将此顶点入队
            while (!QueueEmpty(Q)) //若当前队列不为空
            {
                DeQueue(&Q, &i); //将队列中的元素出队,赋值给i
                for (j = 0; j < G.numVertexs; j++) {
                    if(G.arc[i][j] == 1 && !visited[j]) //当前顶点与其他顶点存在边且其他顶点为被访问
                    {
                        visited[j] = true;
                        cout << G.verxs[j];
                        EnQueue(&Q, j); //将找到的此顶点入队
                    }
                }
            }
        }
    }
}

对于邻接表的广度优先遍历其实原理都是一样的。

最小生成树

最短路径

猜你喜欢

转载自blog.csdn.net/qq_23905237/article/details/88029731