基于邻接矩阵存储的图的深度优先遍历和广度优先遍历

图的存储结构相比较线性表与树来说就复杂很多,对于线性表来说,是一对一的关系,所以用数组或者链表均可简单存放。树结构是一对多的关系,所以我们要将数组和链表的特性结合在一起才能更好的存放。

那么我们的图,是多对多的情况,另外图上的任何一个顶点都可以被看作是第一个顶点,任一顶点的邻接点之间也不存在次序关系。

仔细观察以下几张图,然后深刻领悟一下:

因为任意两个顶点之间都可能存在联系,因此无法以数据元素在内存中的物理位置来表示元素之间的关系(内存物理位置是线性的,图的元素关系是平面的)。

如果用多重链表来描述倒是可以做到,但在几节课前的树章节我们已经讨论过,纯粹用多重链表导致的浪费是无法想像的(如果各个顶点的度数相差太大,就会造成巨大的浪费)。

邻接矩阵(无向图)

考虑到图是由顶点和边或弧两部分组成,合在一起比较困难,那就很自然地考虑到分为两个结构来分别存储。

顶点因为不区分大小、主次,所以用一个一维数组来存储是狠不错的选择。

而边或弧由于是顶点与顶点之间的关系,一维数组肯定就搞不定了,那我们不妨考虑用一个二维数组来存储。

图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。

我们可以设置两个数组,顶点数组为vertex[4]={V0,V1,V2,V3},边数组arc[4][4]为对称矩阵(0表示不存在顶点间的边,1表示顶点间存在边)。

对称矩阵:所谓对称矩阵就是n阶矩阵的元满足a[i][j]=a[j][i](0<=i,j<=n)。即从矩阵的左上角到右下角的主对角线为轴,右上角的元与左下角相对应的元全都是相等的。

有了这个二维数组组成的对称矩阵,我们就可以很容易地知道图中的信息:

  1. 要判定任意两顶点是否有边无边就非常容易了;
  2. 要知道某个顶点的度,其实就是这个顶点Vi在邻接矩阵中第i行(或第i列)的元素之和;
  3. 求顶点Vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接点咯。

邻接矩阵(有向图)

无向图的边构成了一个对称矩阵,貌似浪费了一半的空间,那如果是有向图来存放,会不会把资源都利用得很好呢?

可见顶点数组vertex[4]={V0,V1,V2,V3},弧数组arc[4][4]也是一个矩阵,但因为是有向图,所以这个矩阵并不对称,例如由V1到V0有弧,得到arc[1][0]=1,而V0到V1没有弧,因此arc[0][1]=0。

另外有向图是有讲究的,要考虑入度和出度,顶点V1的入度为1,正好是第V1列的各数之和,顶点V1的出度为2,正好是第V1行的各数之和。

邻接矩阵(网)

在图的术语中,我们提到了网这个概念,事实上也就是每条边上带有权的图就叫网。

下面以此无向图为例,使用邻接矩阵存储,并实现深度优先遍历和广度优先遍历:

代码如下:

#include <stdio.h>

#define MaxVex        100            //最大顶点数
#define INFINITY    65535        //表示∞
#define TRUE        1
#define    FALSE        0
typedef char        VertexType;    //顶点类型
typedef    int            EdgeType;    //权值类型
typedef int            Bool;
Bool    visited[MaxVex];

typedef struct {
    VertexType    vexs[MaxVex];            //顶点数组
    EdgeType    arc[MaxVex][MaxVex];    //邻接矩阵
    int    numVertexes, numEdges;            //当前图中的结点数以及边数
}MGraph;

//广度优先遍历需要的循环队列
typedef struct {
    int    data[MaxVex];
    int    front, rear;
}Queue;

/****************************************/
//队列的相关操作

//初始化
void InitQueue(Queue *Q)
{
    Q->front = Q->rear = 0;
}

//入队
void EnQueue(Queue *Q, int e)
{
    if ((Q->rear+1)%MaxVex == Q->front)
        return ;

    Q->data[Q->rear] = e;
    Q->rear = (Q->rear+1)%MaxVex;
}

//判空
Bool QueueEmpty(Queue *Q)
{
    if (Q->front == Q->rear)
        return TRUE;
    else
        return FALSE;
}

//出队
void DeQueue(Queue *Q, int *e)
{
    if (Q->front == Q->rear)
        return ;

    *e = Q->data[Q->front];
    Q->front = (Q->front+1)%MaxVex;
}
/****************************************/

//建立图的邻接矩阵
void CreateMGraph(MGraph *G)
{
    int i, j, k, w;

    printf("输入顶点数和边数: ");
    scanf("%d%d", &G->numVertexes,&G->numEdges);
    fflush(stdin);

    printf("==============================\n");
    printf("输入各个顶点:\n");
    for (i=0; i<G->numVertexes; ++i)
    {
        printf("顶点%d: ",i+1);
        scanf("%c", &G->vexs[i]);
        fflush(stdin);
    }

    for (i=0; i<G->numVertexes; ++i)
    {
        for (j=0; j<G->numVertexes; ++j)
            G->arc[i][j] = INFINITY;
    }

    printf("==============================\n");
    for (k=0; k<G->numEdges; ++k)
    {
        printf("输入边(vi, vj)中的下标i和j和权W: ");
        scanf("%d%d%d", &i,&j,&w);
        G->arc[i][j] = w;
        G->arc[j][i] = G->arc[i][j];
    }
}

//输出
void DisMGraph(MGraph *G)
{
    int i, j, k;
    k = G->numVertexes;
    for (i=0; i<k; ++i)
    {
        for (j=0; j<k; ++j)
        {
            printf("%5d ", G->arc[i][j]);
        }
        putchar(‘\n‘);
    }
}

/****************************************/
//图的深度优先遍历
void DFS(MGraph G, int i)
{
    int j;
    visited[i] = TRUE;
    printf("%c ",    G.vexs[i]);

    for (j=0; j<G.numVertexes; ++j)
    {
        if (G.arc[i][j]!=INFINITY  &&  !visited[j])
            DFS(G, j);
    }
}

void DFSTraverse(MGraph G)
{
    int i;
    for (i=0; i<G.numVertexes; ++i)
        visited[i] = FALSE;

    for (i=0; i<G.numVertexes; ++i)
    {
        if (!visited[i])
            DFS(G, i);
    }

}

//图的广度优先遍历
void BFSTraverse(MGraph *G)
{
    int i, j;
    Queue Q;

    for (i=0; i<G->numVertexes; ++i)
        visited[i] = FALSE;

    InitQueue(&Q);

    for (i=0; i<G->numVertexes; ++i)
    {
        if (!visited[i])
        {
            visited[i] = TRUE;
            printf("%c ", G->vexs[i]);
            EnQueue(&Q, i);

            while (!QueueEmpty(&Q))
            {
                DeQueue(&Q, &i);
                for (j=0; j<G->numVertexes; ++j)
                {
                    if (!visited[j] && G->arc[i][j]!=INFINITY)
                    {
                        visited[j] = TRUE;
                        printf("%c ", G->vexs[j]);
                        EnQueue(&Q, j);
                    }
                }
            }
        }
    }
}
/****************************************/

//程序入口
int main(){
    MGraph G;

    CreateMGraph(&G);

    printf("\n图的深度优先遍历为: ");
    DFSTraverse(G);    

    printf("\n图的广度优先遍历为: ");
    BFSTraverse(&G);

    printf("\n");

    return 0;
}

运行结果截图:

时间: 2024-10-11 17:22:04

基于邻接矩阵存储的图的深度优先遍历和广度优先遍历的相关文章

深度优先遍历和广度优先遍历

邻接表存储的图的深度优先遍历: 设X是当前被访问顶点,在对X做过访问标记后,选择一条从x出发的未检测过的边(x,y).若发现顶点y已访问过,则重新选择另一条从x出发的未检测过的边,否则沿边(x,y)到达未曾访问过的y,对y访问并将其标记为已访问过:然后从y开始搜索,直到搜索完从y出发的所有路径,即访问完所有从y出发可达的顶点之后,才回溯到顶点x,并且再选择一条从x出发的未检测过的边. 使用深度优先遍历这个图的具体过程是: 1.首先从一个从未走过的顶点作为起始顶点,如1号2.沿1号顶点的边尝试访问

基于邻接表存储的图的深度优先遍历和广度优先遍历

一.深度优先遍历是连通图的一种遍历策略.其基本思想如下: 设x是当前被访问顶点,在对x做过访问标记后,选择一条从x出发的未检测过的边(x,y).若发现顶点y已访问过,则重新选择另一条从x出发的未检测过的边,否则沿边(x,y)到达未曾访问过的y,对y访问并将其标记为已访问过:然后从y开始搜索,直到搜索完从y出发的所有路径,即访问完所有从y出发可达的顶点之后,才回溯到顶点x,并且再选择一条从x出发的未检测过的边.上述过程直至从x出发的所有边都已检测过为止. 例如下图中: 1.从0开始,首先找到0的关

邻接矩阵存储的图

<span style="font-size:24px;">java实现用邻接矩阵(相邻矩阵)实现图,缺点是矩阵中大量的0元素会耗费大量的存储空间</span> public class Graph { final int MAX_VERTEX = 10;// 最多10个顶点 Vertex[] vertex;// 顶点数组 int[][] adjacency;// 邻接矩阵 int numOfVertex;// 当前图中顶点的数量 public Graph()

多级树的深度优先遍历与广度优先遍历(Java实现)

目录 多级树的深度优先遍历与广度优先遍历(Java实现) 节点模型 深度优先遍历 广度优先遍历 多级树的深度优先遍历与广度优先遍历(Java实现) 深度优先遍历与广度优先遍历其实是属于图算法的一种,多级树可以看做是一种特殊的图,所以多级数的深/广遍历直接套用图结构的遍历方法即可. 工程中后端通常会用多级树来存储页面表单的各级联动类目,本文提供了深度遍历与广度遍历的示例,在使用时只要根据你的业务需求稍加改动即可. 我们知道,遍历有递归,非递归两种方式.在工程项目上,一般是禁用递归方式的,因为递归非

二分搜索树的深度优先遍历和广度优先遍历

二分搜索树的特点 二分搜索树首先是一个二叉树,其次其必须满足的条件是:每个节点的键值必须大于其左子节点,每个节点的键值必须小于其右子节点,这样以左右孩子为根的子树仍为二分搜索树,需要注意的是,二分搜索树不一定是一颗完全二叉树. 深度优先遍历 深度优先遍历的基本思想:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次.深度优先遍历的非递归的通用做法是采用栈.要特别注意的是,二分搜索树的深度优先遍历比较特殊,可以细分为前序遍历.中序遍历.后序遍历. 前序遍历:先访问当前节点,再依次

以邻接表作为存储结构的图的深度优先遍历和广度优先遍历(c++版)

一.图的存储 用邻接表法存储图,存储结构分为两部分,一部分为存储图的所有顶点的数组,另一部分为挂载在数组的每个元素后面的用来表示顶点的邻接点的链表. 1.存储顶点的结构单元为: class vnode { public: string nodename; bool visted;//进行图的遍历时用于标记图是否被访问过 node *next; vnode() { visted = false; next = NULL; } }; 链表的结构单元为: class node { public: st

java实现图的遍历(深度优先遍历和广度优先遍历)

package arithmetic.graphTraveral;import java.util.LinkedList;import java.util.Queue; /** * 这个例子是图的遍历的两种方式 * 通过它,使我来理解图的遍历 * Created on 2013-11-18 * @version 0.1 */public class GraphTraveral{ // 邻接矩阵存储图 // --A B C D E F G H I // A 0 1 0 0 0 1 1 0 0 //

图的遍历---深度优先遍历与广度优先遍历

对下图进行遍历,分别采用深度优先和广度优先 1.深度优先遍历的主要思想:首先从一个未被访问的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点: 当没有未访问过的顶点时,则回到上一个顶点,继续试探访问别的顶点,直到所有顶点都被访问. 显然,深度优先遍历是沿着图的某一条分支遍历直到末端,然后回溯,再沿着另一条进行同样的遍历,直到所有顶点被访问. /*深度优先搜索算法遍历图的各个顶点*/ #include<stdio.h> int n, sum, book[101]; int e[101][101

邻接矩阵c源码(构造邻接矩阵,深度优先遍历,广度优先遍历,最小生成树prim,kruskal算法)

matrix.c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <limits.h> #include "aqueue.h" #define MAX_VALUE INT_MAX #define MAX_NUM 100 typedef char node_type; typedef struct matrix { node_type vertex[M