数据结构与算法学习 (12)图的遍历

图的遍历是指从图中的任意顶点出发,对图中的所有顶点访问一次且只访问一次。图的遍历操作和树的遍历操作功能相似。图的遍历是图的一种基本操作,图的许多其他操作都是建立在遍历操作的基础之上的
1.深度优先遍历
深度优先思想:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

深度优先是一个递归的过程。首先,选定一个出发点后进行遍历,如果有邻接的未被访问过的节点则继续前进。若不能继续前进,则回退一步再前进,若回退一步仍然不能前进,则连续回退至可以前进的位置为止。重复此过程,直到所有与选定点相通的所有顶点都被遍历。
深度优先是递归过程,带有回退操作,因此需要使用栈存储访问的路径信息。当访问到的当前顶点没有可以前进的邻接顶点时,需要进行出栈操作,将当前位置回退至出栈元素位置。

当图采用邻接矩阵存储时,由于矩阵元素个数为n2,因此时间复杂度就是O(n2)。
当图采用邻接表存储时,邻接表中只是存储了边结点(e条边,无向图也只是2e个结点),加上表头结点为n(也就是顶点个数),因此时间复杂度为O(n+e)。

邻接矩阵的深度优先遍历:

typedef int Status;    /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */

typedef char VertexType; /* 顶点类型应由用户定义 */
typedef int EdgeType; /* 边上的权值类型应由用户定义 */

#define MAXSIZE 9 /* 存储空间初始分配量 */
#define MAXEDGE 15
#define MAXVEX 9
#define INFINITYC 65535

typedef struct
{
   VertexType vexs[MAXVEX]; /* 顶点表 */
   EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */
   int numVertexes, numEdges; /* 图中当前的顶点数和边数 */
}MGraph;

/*4.1 构建一个邻接矩阵*/
void CreateMGraph(MGraph *G)
{
   int i, j;
   
   //1. 确定图的顶点数以及边数
   G->numEdges=15;
   G->numVertexes=9;
   
   /*2.读入顶点信息,建立顶点表 */
   G->vexs[0]='A';
   G->vexs[1]='B';
   G->vexs[2]='C';
   G->vexs[3]='D';
   G->vexs[4]='E';
   G->vexs[5]='F';
   G->vexs[6]='G';
   G->vexs[7]='H';
   G->vexs[8]='I';
   
   /*3. 初始化图中的边表*/
   for (i = 0; i < G->numVertexes; I++)
   {
       for ( j = 0; j < G->numVertexes; j++)
       {
           G->arc[i][j]=0;
       }
   }
   
   /*4.将图中的连接信息输入到边表中*/
   G->arc[0][1]=1;
   G->arc[0][5]=1;
   
   G->arc[1][2]=1;
   G->arc[1][8]=1;
   G->arc[1][6]=1;
   
   G->arc[2][3]=1;
   G->arc[2][8]=1;
   
   G->arc[3][4]=1;
   G->arc[3][7]=1;
   G->arc[3][6]=1;
   G->arc[3][8]=1;
   
   G->arc[4][5]=1;
   G->arc[4][7]=1;
   
   G->arc[5][6]=1;
   
   G->arc[6][7]=1;
   
   /*5.无向图是对称矩阵.构成对称*/
   for(i = 0; i < G->numVertexes; I++)
   {
       for(j = i; j < G->numVertexes; j++)
       {
           G->arc[j][i] =G->arc[i][j];
       }
   }
}

/*4.2 DFS遍历*/
Boolean visited[MAXVEX]; /* 访问标志的数组 */
//1. 标识顶点是否被标记过;
//2. 选择从某一个顶点开始(注意:非连通图的情况)
//3. 进入递归,打印i点信息,标识; 边表
//4. [i][j] 是否等于1,没有变遍历过visted
void DFS(MGraph G,int i){
   //1.
   visited[i] = TRUE;
   printf("%c",G.vexs[I]);
   
   //2.0~numVertexes
   for(int j = 0; j < G.numVertexes;j++){
       if(G.arc[i][j] == 1 && !visited[j])
           DFS(G, j);
   }
}

void DFSTravese(MGraph G){
   //1.初始化
   for(int i=0;i

邻接表的深度优先遍历:

#define MAXSIZE 9 /* 存储空间初始分配量 */
#define MAXEDGE 15
#define MAXVEX 9
#define INFINITYC 65535

typedef int Status;    /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */

typedef char VertexType; /* 顶点类型应由用户定义 */
typedef int EdgeType; /* 边上的权值类型应由用户定义 */

/* 邻接矩阵结构 */
typedef struct
{
   VertexType vexs[MAXVEX]; /* 顶点表 */
   EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */
   int numVertexes, numEdges; /* 图中当前的顶点数和边数 */
}MGraph;

/* 邻接表结构****************** */
typedef struct EdgeNode /* 边表结点 */
{
   int adjvex;    /* 邻接点域,存储该顶点对应的下标 */
   int weight;        /* 用于存储权值,对于非网图可以不需要 */
   struct EdgeNode *next; /* 链域,指向下一个邻接点 */
}EdgeNode;

typedef struct VertexNode /* 顶点表结点 */
{
   int in;    /* 顶点入度 */
   char data; /* 顶点域,存储顶点信息 */
   EdgeNode *firstedge;/* 边表头指针 */
}VertexNode, AdjList[MAXVEX];

typedef struct
{
   AdjList adjList;
   int numVertexes,numEdges; /* 图中当前顶点数和边数 */
}graphAdjList,*GraphAdjList;

/*4.1 构建一个邻接矩阵*/
void CreateMGraph(MGraph *G)
{
   int i, j;
   
   //1. 确定图的顶点数以及边数
   G->numEdges=15;
   G->numVertexes=9;
   
   /*2.读入顶点信息,建立顶点表 */
   G->vexs[0]='A';
   G->vexs[1]='B';
   G->vexs[2]='C';
   G->vexs[3]='D';
   G->vexs[4]='E';
   G->vexs[5]='F';
   G->vexs[6]='G';
   G->vexs[7]='H';
   G->vexs[8]='I';
   
   /*3. 初始化图中的边表*/
   for (i = 0; i < G->numVertexes; I++)
   {
       for ( j = 0; j < G->numVertexes; j++)
       {
           G->arc[i][j]=0;
       }
   }
   
   /*4.将图中的连接信息输入到边表中*/
   G->arc[0][1]=1;
   G->arc[0][5]=1;
   
   G->arc[1][2]=1;
   G->arc[1][8]=1;
   G->arc[1][6]=1;
   
   G->arc[2][3]=1;
   G->arc[2][8]=1;
   
   G->arc[3][4]=1;
   G->arc[3][7]=1;
   G->arc[3][6]=1;
   G->arc[3][8]=1;
   
   G->arc[4][5]=1;
   G->arc[4][7]=1;
   
   G->arc[5][6]=1;
   
   G->arc[6][7]=1;
   
   /*5.无向图是对称矩阵.构成对称*/
   for(i = 0; i < G->numVertexes; I++)
   {
       for(j = i; j < G->numVertexes; j++)
       {
           G->arc[j][i] =G->arc[i][j];
       }
   }
}

/*4.2 利用邻接矩阵构建邻接表*/
void CreateALGraph(MGraph G,GraphAdjList *GL){
   
   //1.创建邻接表,并且设计邻接表的顶点数以及弧数
   *GL = (GraphAdjList)malloc(sizeof(graphAdjList));
   (*GL)->numVertexes = G.numVertexes;
   (*GL)->numEdges = G.numEdges;
   
   //2. 从邻接矩阵中将顶点信息输入
   for (int i = 0; i < G.numVertexes; i++) {
       //顶点入度为0
       (*GL)->adjList[i].in = 0;
       //顶点信息
       (*GL)->adjList[i].data = G.vexs[i];
       //顶点边表置空
       (*GL)->adjList[i].firstedge = NULL;
   }
   
   //3. 建立边表
   EdgeNode *e;
   for (int i = 0; i < G.numVertexes; i++) {
       for (int j = 0; j < G.numVertexes; j++) {
           if (G.arc[i][j] == 1) {
            
               //创建边表中的邻近结点 i->j
               e = (EdgeNode *)malloc(sizeof(EdgeNode));
               //邻接序号为j
               e->adjvex = j;
               //将当前结点的指向adjList[i]的顶点边表上
               e->next = (*GL)->adjList[i].firstedge;
               (*GL)->adjList[i].firstedge = e;
               //顶点j 上的入度++;
               (*GL)->adjList[j].in++;
               
//                //创建边表中的邻近结点 j->i
//                e = (EdgeNode *)malloc(sizeof(EdgeNode));
//                //邻接序号为j
//                e->adjvex = I;
//                //将当前结点的指向adjList[i]的顶点边表上
//                e->next = (*GL)->adjList[j].firstedge;
//                (*GL)->adjList[j].firstedge = e;
//                //顶点j 上的入度++;
//                (*GL)->adjList[i].in++;
           }
       }
   }
}


Boolean visited[MAXSIZE]; /* 访问标志的数组 */
/* 邻接表的深度优先递归算法 */
void DFS(GraphAdjList GL, int i)
{
   EdgeNode *p;
   visited[i] = TRUE;
   
   //2.打印顶点 A
   printf("%c ",GL->adjList[i].data);
   
   p = GL->adjList[i].firstedge;
   
   //3.
   while (p) {
       if(!visited[p->adjvex])
           DFS(GL,p->adjvex);
       
       p = p->next;
   }
   
}

/* 邻接表的深度遍历操作 */
void DFSTraverse(GraphAdjList GL)
{
   //1. 将访问记录数组默认置为FALSE
   for (int i = 0; i < GL->numVertexes; i++) {
       /*初始化所有顶点状态都是未访问过的状态*/
       visited[i] = FALSE;
   }

   //2. 选择一个顶点开始DFS遍历. 例如A
   for(int i = 0; i < GL->numVertexes; I++)
       //对未访问过的顶点调用DFS, 若是连通图则只会执行一次.
       if(!visited[I])
           DFS(GL, i);
}

2.广度遍历
类似于树的按层次遍历的过程。假设从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为起始点,重复上述过程,直至所有顶点都被访问到为止。换句话说,广度优先搜索遍历图的过程是以v为起始点,由近至远,依次访问和v有路径相通且路径长度为1,2,···的顶点。

如上图中的c,首先访问v1和v1的邻接点v2和v3,然后依次访问v2的邻接点v4和v5及v3的邻接点v6和v7,最后访问v4的邻接点v8。得到访问序列为:v1 →v2→v3→v4→v5→v6→v7→v8。和深度优先搜索类似,在遍历的过程中也需要一个访问标志数组。并且为了顺次访问路径长度为1,2,3···的顶点,需要附设队列以存储已被访问的路径长度为1,2,···的顶点。

邻接矩阵的广度优先遍历代码:

/* 循环队列的顺序存储结构 */
typedef struct
{
    int data[MAXSIZE];
    int front;        /* 头指针 */
    int rear;        /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
}Queue;

/* 初始化一个空队列Q */
Status InitQueue(Queue *Q)
{
    Q->front=0;
    Q->rear=0;
    return  OK;
}

/* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
Status QueueEmpty(Queue Q)
{
    if(Q.front==Q.rear) /* 队列空的标志 */
    return TRUE;
    else
    return FALSE;
}

/* 若队列未满,则插入元素e为Q新的队尾元素 */
Status EnQueue(Queue *Q,int e)
{
    if ((Q->rear+1)%MAXSIZE == Q->front)    /* 队列满的判断 */
    return ERROR;
    Q->data[Q->rear]=e;            /* 将元素e赋值给队尾 */
    Q->rear=(Q->rear+1)%MAXSIZE;/* rear指针向后移一位置, */
    /* 若到最后则转到数组头部 */
    return  OK;
}

/* 若队列不空,则删除Q中队头元素,用e返回其值 */
Status DeQueue(Queue *Q,int *e)
{
    if (Q->front == Q->rear)            /* 队列空的判断 */
    return ERROR;
    *e=Q->data[Q->front];                /* 将队头元素赋值给e */
    Q->front=(Q->front+1)%MAXSIZE;    /* front指针向后移一位置, */
    /* 若到最后则转到数组头部 */
    return  OK;
}
/*1.2.1 邻接矩阵广度优先遍历-代码实现*/
Boolean visited[MAXVEX]; /* 访问标志的数组 */
void BFSTraverse(MGraph G){
    
    int temp = 0;
    
    //1.
    Queue Q;
    InitQueue(&Q);
    
    //2.将访问标志数组全部置为"未访问状态FALSE"
    for (int i = 0 ; i < G.numVertexes; i++) {
        visited[i] = FALSE;
    }
    
    //3.对遍历邻接表中的每一个顶点(对于连通图只会执行1次,这个循环是针对非连通图)
    for (int i = 0 ; i < G.numVertexes; i++) {
        
        if(!visited[i]){
            visited[i] = TRUE;
            printf("%c  ",G.vexs[i]);
            
            //4. 入队
            EnQueue(&Q, i);
            while (!QueueEmpty(Q)) {
                //出队
                DeQueue(&Q, &i);
                for (int j = 0; j < G.numVertexes; j++) {
                    if(G.arc[i][j] == 1 && !visited[j])
                    {    visited[j] = TRUE;
                        printf("%c   ",G.vexs[j]);
                        EnQueue(&Q, j);
                    }
                }
            }
        }
        
    }
    
    
}

邻接表的广度优先遍历代码:

/*1.2.2 邻接表广度优先遍历*/
Boolean visited[MAXSIZE]; /* 访问标志的数组 */
void BFSTraverse(GraphAdjList GL){
    
    //1.创建结点
    EdgeNode *p;
    
    Queue Q;
    InitQueue(&Q);
    

    //2.将访问标志数组全部置为"未访问状态FALSE"
    for(int i = 0; i < GL->numVertexes; i++)
        visited[i] = FALSE;
    
    //3.对遍历邻接表中的每一个顶点(对于连通图只会执行1次,这个循环是针对非连通图)
    for(int i = 0 ;i < GL->numVertexes;i++){
        //4.判断当前结点是否被访问过.
        if(!visited[i]){
            visited[i] = TRUE;
            //打印顶点
            printf("%c ",GL->adjList[i].data);
            
            EnQueue(&Q, i);
            while (!QueueEmpty(Q)) {
                DeQueue(&Q, &i);
                p = GL->adjList[i].firstedge;
                while (p) {
                    //判断
                    if(!visited[p->adjvex]){
                        visited[p->adjvex] = TRUE;
                         printf("%c ",GL->adjList[p->adjvex].data);
                        EnQueue(&Q, p->adjvex);
                    }
                    p = p->next;
                }
            }
            
        }
    }
    
}

你可能感兴趣的:(数据结构与算法学习 (12)图的遍历)