数据结构与算法-Day12-图

255 阅读5分钟

基本概念

:一种多对多的结构,是由顶点的有穷⾮空集合和顶点之间边的集合组成。通常表示为: G(V,E). 其中,G表示一个图, V图G中的顶点集合,E图G中边的集合。

子图:指的是由图中一部分顶点和边构成的图,称为原图的子图。

图的存储

图的存储分为两种,顺序存储和链式存储

顺序存储(邻接矩阵)

图的顺序存储需要用到两个数组,一个数组存放图中顶点本身的数据(一维数组),另外一个数组用于存储各顶点之间的关系(二维数组/邻接矩阵)。

假设图Gn个顶点,则邻接矩阵是一个n*n的方阵,定义为:

顶点数组为:

邻接矩阵为:

其中arc[v1][v0]>0代表顶点v1、v0为邻接点。

数据结构如下:

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

顺序存储的深度优先遍历

  • 递归
/*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<G.numVertexes;i++){
        visited[i] = FALSE;
    }
    //2.某一个顶点,避免出现孤岛的情况
    for(int i = 0;i<G.numVertexes;i++){
        if(!visited[i]){
            DFS(G, i);
        }
    }
}
  • 非递归-利用栈思想
void DFSTraverse_1(MGraph G) {
    int* stack = (int*)malloc(sizeof(int)*G.numVertexes);
    int* visited = (int*)calloc(G.numVertexes, sizeof(int));
    //此次循环也是为了避免孤岛的情况出现
    for(int j = 0; j < G.numVertexes; j++) {
        if(visited[j] == 0) {
            visited[j] = 1;
            int top = -1;
            //第一个顶点入栈
            stack[++top] = j;
            printf("%c ", G.vexs[j]);
            while(top>-1) {
                int topPosition = stack[top];
                //找到栈顶顶点对应的邻接点数组
                int *m = G.arc[topPosition];
                int i;
                for(i = 0; i < G.numVertexes; i++) {
                    //遍历数组,找到未访问过的邻接点则入栈,
                    if(m[i]==1 && visited[i]==0) {
                        stack[++top] = i;
                        printf("%c ", G.vexs[i]);
                        visited[i] = 1;
                        break;
                    }
                }
                //如果当前数组的顶点都被访问过,则需要出栈,从上一个顶点开始遍历
                if(i==G.numVertexes) {
                    top--;
                }
            }
        }
    }
    free(stack);
    free(visited);
}

顺序存储的广度优先遍历

void BFSTraverse(MGraph G){
    //创建一个队列
    int *queue = (int*)calloc(G.numVertexes, sizeof(int));
    //队头、队尾
    int rear = 0;
    int front = 0;
    //是否访问顶点的数组
    int *visited = (int*)calloc(G.numVertexes, sizeof(int));
    //防止出现孤岛
    for(int i = 0; i < G.numVertexes; i++) {
        //第一个顶点入队
        if(visited[i] == 0) {
            visited[i] = 1;
            printf("%c ", G.vexs[i]);
            queue[rear++] = i;
            while(rear != front) {
                //顶点出队
                int topPosition = queue[front++];
                //找到顶点对应的邻接数组
                int *m = G.arc[topPosition];
                for(int j = 0; j < G.numVertexes; j++) {
                    //遍历数组,将未被访问过的邻接点加入队列并访问
                    if(m[j] == 1 && visited[j] == 0) {
                        queue[rear++] = j;
                        visited[j] = 1;
                        printf("%c ", G.vexs[j]);
                    }
                }
            }
        }
    }
    free(queue);
}

链式存储(邻接表)

和顺序存储类似,需要有一个数组来存储图的各个顶点,不同的是,该数组里面的元素是类似链表的数据结构。

上图的存储结构如下所示:

/* 邻接表结构****************** */
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;

深度优先遍历

邻接表的遍历和链接矩阵的遍历很类似,唯一的区别在于邻接矩阵是需要遍历每一个顶点的邻接数组,而邻接表需要遍历的是每一个顶点的邻接链表。

  • 递归
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);
}
  • 非递归-栈
void DFSTraverse_1(GraphAdjList GL) {
    int* stack = (int*)calloc(GL->numVertexes, sizeof(int));
    int* visited = (int*)calloc(GL->numVertexes, sizeof(int));
    EdgeNode *edgeNode;
    for(int i = 0; i < GL->numVertexes; i++) {
        if(visited[i]==0) {
            visited[i] = 1;
            int top = -1;
            stack[++top] = i;
            printf("%c ", GL->adjList[i].data);
            while(top>-1) {
                int topPostion = stack[top];
                edgeNode = (GL->adjList[topPostion]).firstedge;
                while(edgeNode) {
                    if(visited[edgeNode->adjvex] == 0) {
                        visited[edgeNode->adjvex] = 1;
                        stack[++top] = edgeNode->adjvex;
                        printf("%c ", GL->adjList[edgeNode->adjvex].data);
                        break;
                    }
                    edgeNode = edgeNode->next;
                }
                if(edgeNode == NULL) {
                    top--;
                }
            }
        }
    }
    free(stack);
    free(visited);
}

广度优先遍历

void BFSTraverse_1(GraphAdjList GL){
    //创建一个队列
    int *queue = (int*)calloc(GL->numVertexes, sizeof(int));
    //队头、队尾
    int rear = 0;
    int front = 0;
    EdgeNode *node;
    //是否访问顶点的数组
    int *visited = (int*)calloc(GL->numVertexes, sizeof(int));
    //防止出现孤岛
    for(int i = 0; i < GL->numVertexes; i++) {
        //第一个顶点入队
        if(visited[i] == 0) {
            visited[i] = 1;
            printf("%c ", GL->adjList[i].data);
            queue[rear++] = i;
            while(rear != front) {
                //顶点出队
                node = GL->adjList[front++].firstedge;
                while(node) {
                    //遍历数组,将未被访问过的邻接点加入队列并访问
                    if(visited[node->adjvex] == 0) {
                        queue[rear++] = node->adjvex;
                        visited[node->adjvex] = 1;
                        printf("%c ", GL->adjList[node->adjvex].data);
                    }
                    node = node->next;
                }
            }
        }
    }
}