连通图的遍历(深度遍历/广度遍历)

概念:图中的所有节点都要遍历到,并且只能遍历一次。

  • 深度遍历
  • 广度遍历

深度遍历

概念:从一个给定的顶点开始,找到一条边,沿着这条边一直遍历。

广度遍历

概念:从一个给定的顶点开始,找到这个顶点下的所有子顶点后,再找下一层的子顶点。

深度遍历的实现思路

1,创建一个bool数组,用来识别哪个顶点已经被遍历过了。

2,递归

3,递归找给定顶点是否有下一个顶点(方法:get_first_neighbor),都找完后,

4,再递归找给定顶点之后的在3处找到的顶点后的下一个顶点(方法:get_next_neighbor)

光度遍历的实现思路

1,用队列实现,先入队给定顶点

2,出队

3,入队:与在2处出队的顶点有相连的顶点

代码

graph_link.h

#ifndef __graph_link__
#define __graph_link__ #include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include <memory.h>
#include <stdbool.h> #define default_vertex_size 10
#define T char //边的结构
typedef struct Edge{
//顶点的下标
int idx;
//指向下一个边的指针
struct Edge* link;
}Edge; //顶点的结构
typedef struct Vertex{
//顶点的值
T data;
//边
Edge* adj;
}Vertex; //图的结构
typedef struct GraphLink{
int MaxVertices;
int NumVertices;
int NumEdges; Vertex* nodeTable;
}GraphLink; //初始化图
void init_graph_link(GraphLink* g);
//显示图
void show_graph_link(GraphLink* g);
//插入顶点
void insert_vertex(GraphLink* g, T v);
//插入边尾插
void insert_edge_tail(GraphLink* g, T v1, T v2);
//插入边头插
void insert_edge_head(GraphLink* g, T v1, T v2);
//删除边
void remove_edge(GraphLink* g, T v1, T v2);
//删除顶点
void remove_vertex(GraphLink* g, T v);
//销毁图
void destroy_graph_link(GraphLink* g);
//取得指定顶点的第一个后序顶点
int get_first_neighbor(GraphLink* g, T v);
//取得指定顶点v1的临街顶点v2的第一个后序顶点
int get_next_neighbor(GraphLink* g, T v1, T v2); //深度遍历
void dfs_graph(GraphLink* g, T v);
//取得顶点的data值
T getVertexValue(GraphLink* g, int i);
//广度遍历
void cfs_graph(GraphLink* g, T v); #endif

graph_link.c

#include "graph_link.h"
#include "nodequeue.h" //初始化图
void init_graph_link(GraphLink* g){
g->MaxVertices = default_vertex_size;
g->NumVertices = g->NumEdges = 0; g->nodeTable = (Vertex*)malloc(sizeof(Vertex) * g->MaxVertices);
assert(NULL != g->nodeTable);
for(int i = 0; i < g->MaxVertices; ++i){
g->nodeTable[i].adj = NULL;
}
} //显示图
void show_graph_link(GraphLink* g){
if(NULL == g)return;
for(int i = 0; i < g->NumVertices; ++i){
printf("%d %c->", i, g->nodeTable[i].data);
Edge* p = g->nodeTable[i].adj;
while(NULL != p){
printf("%d->", p->idx);
p = p->link;
}
printf(" NULL\n");
}
} //插入顶点
void insert_vertex(GraphLink* g, T v){
if(g->NumVertices >= g->MaxVertices)return;
g->nodeTable[g->NumVertices++].data = v;
} //查找顶点的index
int getVertexIndex(GraphLink* g, T v){
for(int i = 0; i < g->NumVertices; ++i){
if(v == g->nodeTable[i].data)return i;
}
return -1;
}
//插入边(头插)
void insert_edge_head(GraphLink* g, T v1, T v2){
int p1 = getVertexIndex(g, v1);
int p2 = getVertexIndex(g, v2); if(p1 == -1 || p2 == -1)return; Edge* p = (Edge*)malloc(sizeof(Edge));
p->idx = p2;
p->link = g->nodeTable[p1].adj;
g->nodeTable[p1].adj = p; p = (Edge*)malloc(sizeof(Edge));
p->idx = p1;
p->link = g->nodeTable[p2].adj;
g->nodeTable[p2].adj = p;
} //取得指定顶点的第一个后序顶点
int get_first_neighbor(GraphLink* g, T v){
int i = getVertexIndex(g, v);
if (-1 == i)return -1;
Edge* p = g->nodeTable[i].adj;
if(NULL != p)
return p->idx;
else
return -1;
} //取得指定顶点v1的临街顶点v2的第一个后序顶点
int get_next_neighbor(GraphLink* g, T ve1, T ve2){
int v1 = getVertexIndex(g, ve1);
int v2 = getVertexIndex(g, ve2);
if(v1 == -1 || v2 == -1)return -1; Edge* t = g->nodeTable[v1].adj;
while(t != NULL && t->idx != v2){
t = t->link;
}
if(NULL != t && t->link != NULL){
return t->link->idx;
}
return -1;
}
//取得顶点的data值
T getVertexValue(GraphLink* g, int i){
if(i == -1)return 0;
return g->nodeTable[i].data;
} //深度遍历
void dfs_graph_v(GraphLink* g, int v, bool* visited){
printf("%c->", getVertexValue(g,v));
visited[v] = true;
//取得相邻顶点的下标
int w = get_first_neighbor(g, getVertexValue(g, v));
while(w != -1){
if(!visited[w]){
dfs_graph_v(g, w, visited);
}
w = get_next_neighbor(g, getVertexValue(g, v), getVertexValue(g,w));
}
}
void dfs_graph(GraphLink* g, T v){
int cnt = g->NumVertices;
bool* visited = (bool*)malloc(sizeof(bool) * cnt);
assert(NULL != visited);
for(int i = 0; i < cnt; ++i){
visited[i] = false;
} int index = getVertexIndex(g, v);
dfs_graph_v(g, index, visited);
free(visited);
} //广度遍历
void cfs_graph(GraphLink* g, T v){ //创建一个辅助的bool数组,用来识别哪个顶点已经被遍历过了
int cnt = g->NumVertices;
bool* visited = (bool*)malloc(sizeof(bool) * cnt);
assert(NULL != visited);
for(int i = 0; i < cnt; ++i){
visited[i] = false;
} //创建队列
NodeQueue q;
init(&q);
//入队
int tar = getVertexIndex(g, v);
enQueue(&q, tar); //队列不为空就执行
while(length(&q) != 0){
//取得队列的第一个元素
int ve = getHead(&q)->data; printf("%c->", getVertexValue(g, ve));
visited[ve] = true; //出队
deQueue(&q); Edge* e = g->nodeTable[ve].adj;
while(NULL != e){
//如果这个顶点没有被遍历过,入队
if(!visited[e->idx]){
visited[e->idx] = true;
enQueue(&q, e->idx);
}
e = e->link;
}
}
}

graph_linkmain.c

#include "graph_link.h"

int main(){
GraphLink gl;
//初始化图
init_graph_link(&gl);
//插入节点
insert_vertex(&gl, 'A');
insert_vertex(&gl, 'B');
insert_vertex(&gl, 'C');
insert_vertex(&gl, 'D');
insert_vertex(&gl, 'E');
insert_vertex(&gl, 'F');
insert_vertex(&gl, 'G');
insert_vertex(&gl, 'H'); insert_edge_head(&gl, 'A', 'B');
insert_edge_head(&gl, 'A', 'C');
insert_edge_head(&gl, 'B', 'D');
insert_edge_head(&gl, 'B', 'E');
insert_edge_head(&gl, 'C', 'F');
insert_edge_head(&gl, 'C', 'G');
insert_edge_head(&gl, 'D', 'H');
insert_edge_head(&gl, 'E', 'H');
insert_edge_head(&gl, 'F', 'G');
//显示图
show_graph_link(&gl); //深度遍历
dfs_graph(&gl, 'E');
printf("null\n"); //广度遍历
cfs_graph(&gl, 'F');
printf("null\n");
}

完整代码

编译方法:g++ -g nodequeue.c graph_link.c graph_linkmain.c

c/c++连通图的遍历(深度遍历/广度遍历)的更多相关文章

  1. Java多线程遍历文件夹,广度遍历加多线程加深度遍历结合

    复习IO操作,突然想写一个小工具,统计一下电脑里面的Java代码量还有注释率,最开始随手写了一个递归算法,遍历文件夹,比较简单,而且代码层次清晰,相对易于理解,代码如下:(完整代码贴在最后面,前面是功 ...

  2. 图的存储及遍历 深度遍历和广度遍历 C++代码实现

    /*图的存储及遍历*/ #include<iostream> using namespace std; //----------------------------------- //邻接 ...

  3. 记录JS如何使用广度遍历找到节点的所有父节点

    我们在实际的工作业务场景中经常遇到这样的场景,求取树数据中某个节点的父亲节点以及所有的父亲节点,这样的场景下不建议使用深度遍历,使用广度遍历可以更快找到. 1.案例解说 比如树的长相是这样的: 树的数 ...

  4. java遍历树(深度遍历和广度遍历

    java遍历树如现有以下一颗树:A     B          B1               B11          B2               B22     C          C ...

  5. 多级树的深度遍历与广度遍历(Java实现)

    目录 多级树的深度遍历与广度遍历 节点模型 深度优先遍历 广度优先遍历 多级树的深度遍历与广度遍历 深度优先遍历与广度优先遍历其实是属于图算法的一种,多级树可以看做是一种特殊的图,所以多级数的深/广遍 ...

  6. 重新整理数据结构与算法(c#)—— 图的深度遍历和广度遍历[十一]

    参考网址:https://www.cnblogs.com/aoximin/p/13162635.html 前言 简介图: 在数据的逻辑结构D=(KR)中,如果K中结点对于关系R的前趋和后继的个数不加限 ...

  7. 采用邻接矩阵表示图的深度优先搜索遍历(与深度优先搜索遍历连通图的递归算法仅仅是DFS的遍历方式变了)

    //采用邻接矩阵表示图的深度优先搜索遍历(与深度优先搜索遍历连通图的递归算法仅仅是DFS的遍历方式变了) #include <iostream> using namespace std; ...

  8. 【算法】【python实现】二叉树深度、广度优先遍历

    二叉树的遍历,分为深度优先遍历,以及广度优先遍历. 在深度优先遍历中,具体分为如下三种: 先序遍历:先访问根节点,再遍历左子树,再遍历右子树: 中序遍历:先遍历左子树,再访问根节点,再遍历右子树: 后 ...

  9. C++编程练习(8)----“二叉树的建立以及二叉树的三种遍历方式“(前序遍历、中序遍历、后续遍历)

    树 利用顺序存储和链式存储的特点,可以实现树的存储结构的表示,具体表示法有很多种. 1)双亲表示法:在每个结点中,附设一个指示器指示其双亲结点在数组中的位置. 2)孩子表示法:把每个结点的孩子排列起来 ...

随机推荐

  1. solr查询特殊字符的处理

    1. 使用solr的页面功能时,遇到语法错误 2. 确认原因 从lexical error分析时特殊字符的问题. 首先 solr查询有哪些特殊字符? 查找官方文档:http://lucene.apac ...

  2. 飞跃式发展的后现代 Python 世界

    飞跃式发展的后现代Python世界 如果现代Python有一个标志性特性,那么简单说来便是Python对自身定义的越来越模糊.在过去的几年的许多项目都极大拓展了Python,并重建了“Python”本 ...

  3. vue_drf之实现极验滑动验证码

    一.需求 1,场景 我们在很多登录和注册场景里,为了避免某些恶意攻击程序,我们会添加一些验证码,也就是行为验证,让我们相信现在是一个人在交互,而不是一段爬虫程序.现在市面上用的比较多的,比较流行的是极 ...

  4. 利用Python测量滴水湖的水面面积

    美丽的滴水湖   美丽的滴水湖坐落在上海的东南角,濒临东海,风景秀丽,安静舒适,是旅游.恋爱的绝佳去处.笔者有幸去过一回,对那儿的风土人情留下了深刻的印象,如果有机会,笔者还会多去几次!   滴水湖是 ...

  5. C#基础知识总结(二)

    摘要 第二篇主要讲:变量.连接符占位符等.转义字符.数据的计算.数据的转换.try-catch的简单熟悉.复合运算符和自加自减 一.变量 1.数据存储在内存中:内存叫做RAM,内存被分隔为一小格一小格 ...

  6. sql查询语句时怎么把几个字段拼接成一个字段

    sql查询语句时怎么把几个字段拼接成一个字段SELECT CAST(COLUMN1 AS VARCHAR(10)) + '-' + CAST(COLUMN2 AS VARCHAR(10) ...) a ...

  7. Hibernate入门(六)---------HQL语句

    Query: 代表面向对象的一个Hibernate查询操作.在Hibernate中,通常使用session.createQuery()方法接收一个HQL语句,然后调用Query的 list()或uni ...

  8. spring-framework-中文文档二:Bean概述

    Spring IoC容器管理一个或多个bean.这些bean是使用您提供给容器的配置元数据创建的,例如,以XML <bean/>定义的形式 . 在容器本身中,这些bean定义被表示为 Be ...

  9. nodemailer + express + h5 拖拽文件上传 实现发送邮件

    一.部署 1.部署Express 2.准备一个邮箱并开始SMTP服务 二.服务器端 三.客户端 四.效果:

  10. linux学习笔记-wget相关知识

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! wget是非交互式的网络文件下载工具,这里参考帮助文档,记录下实用参数和使用方法. 一.wget的实用参数: wget: 用法: ...