算法与数据结构(c语言)——图(Figure,三):遍历操作

图的遍历

从图的某个顶点出发访问遍图中所有顶点,且每个顶点仅被访问一次,这一过程便叫做图的遍历。

深度优先遍历

也有称深度优先搜索(Depth First Search),简称DFS。有点像是树的先序遍历。

无向图的邻接矩阵存储方式深度优先遍历算法的代码实现: 

// 访问标志数组
bool visited[MAXVN];

// 邻接矩阵深度优先递归算法
void DFS(MGraph G, int i) {
    // 访问这个顶点,此处只是简单打印
    printf("%c ", G.vexs[i]);
    // 访问过后将标记设置为true
    visited[i] = true;

    // 开始操作结点i的邻接点
    for(int j = 0; j < G.vertexNum; j++) {
        // 当顶点j未访问过,同时顶点i和顶点j是相邻接的,才进行递归调用。
        if(G.arc[i][j] == 1 && !visited[j]) {
            DFS(G, j);
        }
    }
}

// 邻接矩阵的深度遍历操作
void DFSTraverse(MGraph G) {
    int i;
    // 初始化所有顶点的初始化状态为未访问
    for(i = 0; i < G.vertexNum; i++) {
        visited[i] = false;
    }
    for(i = 0; i < G.vertexNum; i++) {
        // 对所有未访问过的顶点调用DFS,若是连通图,则只会调用一次
        if(!visited[i]) {
            DFS(G,i);
        }
    }
}

 邻接表结构存储的形式,

// 邻接表的深度优先递归算法
void DFS(MLGraph MLG, int i) {
    EdgeNode *p = MLG.adjList[i].firstedge;

    // 访问这个顶点,此处只是简单打印
    printf("%c ", MLG.adjList[i].data);
    // 访问过后将标记设置为true
    visited[i] = true;

    // 顶点i存在边(有邻接点),进行操作
    while(p) {
        // 当顶点i的邻接点未被访问过,才进行递归调用。
        if(!visited[p->adjvex]) {
            DFS(MLG, p->adjvex);
        }
        // 指向结点i的邻接点
        p = p->next;
    }
}

对于有向图而言,它只是对通道存在可行或不可行,算法上没有变化。

广度优先遍历 

又称广度优先搜索算法(Breadth-First-Search),BFS,有点像是图的层序遍历。

无向图的邻接表存储方式广度优先遍历算法实现

// 邻接表的广度遍历操作
void BFSTraverse(MLGraph G) {
    Queue q;
    EdgeNode *p;
    InitQueue(&q);

    for(int i = 0; i < G.vertexNum; i++) {
        visited[i] = false;
    }

    for(int i = 0; i < G.vertexNum; i++) {
        if(!visited[i]) {
            // 访问顶点,这里只是简单打印
            printf("%c ",G.adjList[i].data);
            // 设置访问过后的标记
            visited[i] = true;
            // 将顶点的下标入队列
            EnQueue(&q,i);
            
            // 队列不为空
            while(!QueueEmpty(q)) {
                // 出队列
                DeQueue(&q, &i);
                // 将i结点的第一条边赋值给指针p
                p = G.adjList[i].firstedge;
                
                // 循环判断边结点是否存在
                while(p) {
                    // 判断邻接点是否被访问过
                    if(!visited[p->adjvex]) {
                        printf("%c ",G.adjList[p->adjvex].data);
                        // 修改邻接顶点的访问标志
                        visited[p->adjvex] = true;
                        // 邻接点的下标入队列
                        EnQueue(&q, p->adjvex);
                    }
                    // 指向下一个边表结点
                    p = p->next;
                }
            }
        }
    }
}

邻接矩阵存储方式 :

/* 邻接矩阵的广度遍历算法 */
void BFSTraverse(MGraph G) {
    int i, j;
    Queue Q;
    for(i = 0; i < G.vertexNum; i++) {
        visited[i] = false;
	}
    /* 初始化一辅助用的队列 */
    InitQueue(&Q);
    /* 对每一个顶点做循环 */
    for(i = 0; i < G.vertexNum; i++) {
        /* 若是未访问过就处理 */
        if (!visited[i]) {
            /* 设置当前顶点访问过 */
            visited[i]=TRUE;
            /* 操作顶点,此处只是简单打印 */
            printf("%c ", G.vexs[i]);
            /* 将此顶点下标入队列 */
            EnQueue(&Q,i);
            /* 若当前队列不为空 */
            while(!QueueEmpty(Q)) {
                /* 将队对元素出队列,赋值给i */
                DeQueue(&Q,&i);
                for(j = 0; j < G.vertexNum; j++) {
                    /* 判断其它顶点若与当前顶点存在边且未访问过  */
                    if(G.arc[i][j] == 1 && !visited[j]) {
                        /* 将找到的此顶点标记为已访问 */
                        visited[j] = true;
                        /* 打印顶点 */
                        printf("%c ", G.vexs[j]);
                        /* 将找到的此顶点入队列  */
                        EnQueue(&Q,j);
                    }
                }
            }
        }
    }
}

用到的辅助队列:

Status InitQueue(Queue *Q) {
	Q->front = 0;
	Q->rear = 0;
	return OK;
}

/* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
bool 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;
	}
	/* 将元素e赋值给队尾 */
	Q->data[Q->rear]=e;
	/* rear指针向后移一位置,若到最后则转到数组头部 */
	Q->rear=(Q->rear+1)%MAXSIZE;

	return  OK;
}

/* 若队列不空,则删除Q中队头元素,用e返回其值 */
Status DeQueue(Queue *Q,int *e) {
    /* 队列空的判断 */
	if (Q->front == Q->rear) {
        return ERROR;
	}
    /* 将队头元素赋值给e */
	*e=Q->data[Q->front];
	/* front指针向后移一位置, 若到最后则转到数组头部*/
	Q->front=(Q->front+1)%MAXSIZE;

	return  OK;
}

 

运行结果:

对比两种遍历算法,它们在时间复杂度上是一样的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值