当前位置: 首页 > news >正文

浅谈【数据结构】图-图的遍历

目录

1、图的遍历

2、深度优先搜索算法

3、广度优先搜索算法


谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注

没错,说的就是你,不用再怀疑!!!

希望我的文章内容能对你有帮助,一起努力吧!!!


1、图的遍历

图的遍历是对树的遍历的推广,是按照一种规划(或者说是一种次序)访问图中各顶点一次,并且只能 访问一次,亦或者是将网状结构按照某种规划进行线性化。

对于图的遍历通常有两种方式:

  • 广度优先搜索算法(Breath First search)BFS
  • 深度优先搜索算法(Depth First search)DFS

两者是现代人工智能AI的基础。

2、深度优先搜索算法

深度优先搜索算法(Depth First search)DFS

设初始化的时候,图中各个顶点均为被访问过。从图中某个顶点(V0)出发,访问V0,然后搜索V0的 邻接顶点Vi,再以Vi为基础访问Vi的邻接顶点(依次下去...)(深度优先)。若某个顶点没有邻接顶点 (邻接顶点均被访问完毕),则回溯到它上一个顶点,然后再从它的上一个顶点开始继续进行邻接顶点 的访问,直到所有的顶点都被访问完毕。

***DFS示例代码***

#include <iostream>
#include <cstring>

// 关系类型
typedef struct relation
{
    int index; // 下标
    int weight; // 权值
    struct relation *next; // 下一个关系的顶点的下标指针
}Relation_t;

// 顶点类型
typedef struct 
{
    std::string data; // 顶点数据
    Relation_t *first; // 该顶点的关系集
}Vertex_t;


// 当前顶点数
int current_count = 0;

/*
    @brief  创建一个图:邻接表
    @param  count 该图的最大顶点数量 
    @return 成功返回创建好的图指针
*/
Vertex_t *creatGraph(int count)
{
    // 申请空间
    Vertex_t *graph = new Vertex_t[count];

    // 初始化
    memset(graph,0,sizeof(Vertex_t)*count);

    std::cout << "请输入顶点数据空格分开(“结束”输入):";
    // 接受顶点

    while(1)
    {
        std::string data = "结束";
        std::cin >> data;
        if(data == "结束")
            break;

        if(current_count == count)
            break;

        // 新增顶点位置
        graph[current_count].data  = data;
        graph[current_count].first = nullptr;

        current_count++;
    }

    // 增加关系
    while(1)
    {
        std::cout << "请输入顶点关系(结束 结束 -1):";

        // 出发顶点和终止顶点 权值
        std::string start;
        std::string end;
        int data;
        std::cin >> start >> end >> data;

        if(start=="结束"||end=="结束"||data == -1)
            break;

        // 存储关系
        // 获取start和end在图数组的什么位置
        int index_s = 0,index_e = 0;
        for(;index_s < current_count;index_s++)
            if(graph[index_s].data == start)
                break;
        for(;index_e < current_count;index_e++)
            if(graph[index_e].data == end)
                break;

        // 两个顶点的下标找到了
        if(index_s == current_count||index_e == current_count)
            continue;
        
        // 添加关系
        Relation_t *rt = new Relation_t;
        rt->index = index_e;
        rt->weight = data;
        rt->next = nullptr;

        // 当至少存在出度顶点的时候
        if(graph[index_s].first)
        {
            Relation_t *rt_ptr = graph[index_s].first;
            while(rt_ptr->next)
                rt_ptr = rt_ptr->next;
            
            // 存进关系链表
            rt_ptr->next = rt;
        }
        else{ // 一个出度结点都没有的时候
            graph[index_s].first = rt;
        }
    }

    return graph;
}

void printrelation(Vertex_t *graph)
{
    if(!graph)
        std::cout << "空图" << std::endl;
    
    for(int count_v = 0;count_v < current_count;count_v++)
    {
        std::cout << "顶点<"<< graph[count_v].data <<">:";
        
        Relation_t *rt_ptr = graph[count_v].first;
        while(rt_ptr)
        {
            std::cout<< "<"<< graph[count_v].data <<","<< 
            graph[rt_ptr->index].data <<">" 
            << "("<<rt_ptr->weight<<")";
        
            rt_ptr = rt_ptr->next;
        }
        std::cout << std::endl;
    }
}


// 计算下标
int getIndex(Vertex_t *graph,std::string vertex_v)
{
    for(int index = 0;index < current_count;index++)
    {
        if(vertex_v == (graph[index]).data)
            return index;
    }
    return -1;
}



/*
    逻辑:
        设初始化的时候,图中各个顶点均为被访问过。
        从图中某个顶点(V0)出发,访问V0,然后搜索V0的邻接顶点Vi,再以Vi为基础访问Vi的邻接顶点(依次下去...)(深度优先)。
        若某个顶点没有邻接顶点(邻接顶点均被访问完毕),
        则回溯到它上一个顶点,然后再从它的上一个顶点开始继续进行邻接顶点的访问,直到所有的顶点都被访问完毕。
    @brief 深度优先搜索算法
    @param graph 需要进行深度优先搜索的图指针
*/
void DFS(Vertex_t *graph,std::string v0)
{
    int v0_pos = getIndex(graph,v0);
    if(v0_pos == -1)
        return;

    // 标志位数组,且初始化所有顶点都为未被访问
    static bool *flags=new bool[current_count];

    // 访问V0
    std::cout << graph[v0_pos].data << " ";
    flags[v0_pos] = true;

    Relation_t *currnet_relation = graph[v0_pos].first;

    while(currnet_relation)
    {
        // 递归下去进行访问
        if(flags[currnet_relation->index] == false)
            DFS(graph,graph[currnet_relation->index].data);

        // 递归结束返回,再次访问下一个邻接点
        currnet_relation = currnet_relation->next;
    }

    std::cout << std::endl;
}



int main()
{
    Vertex_t *graph = creatGraph(10);

    DFS(graph,"A");

    delete []graph;

    return 0;
}

3、广度优先搜索算法

广度优先搜索算法(Breath First search)BFS

类似于树的层次遍历,初始化的时候,图中的各项顶点均未被访问,从图某个顶点开始(V0)依次的访 问V0的各个邻接点(广度优先)

然后分别冲这些被访问过的顶点出发,仍然按照广度优先的策略访问其他的顶点...直到所有能访问的顶 点均被访问完毕。

***BFS示例代码***

#include <iostream>
#include <cstring>
#include <queue>

// 关系类型
typedef struct relation
{
    int index; // 下标
    int weight; // 权值
    struct relation *next; // 下一个关系的顶点的下标指针
}Relation_t;

// 顶点类型
typedef struct 
{
    std::string data; // 顶点数据
    Relation_t *first; // 该顶点的关系集
}Vertex_t;


// 当前顶点数
int current_count = 0;

/*
    @brief  创建一个图:邻接表
    @param  count 该图的最大顶点数量 
    @return 成功返回创建好的图指针
*/
Vertex_t *creatGraph(int count)
{
    // 申请空间
    Vertex_t *graph = new Vertex_t[count];

    // 初始化
    memset(graph,0,sizeof(Vertex_t)*count);

    std::cout << "请输入顶点数据空格分开(“结束”输入):";
    // 接受顶点

    while(1)
    {
        std::string data = "结束";
        std::cin >> data;
        if(data == "结束")
            break;

        if(current_count == count)
            break;

        // 新增顶点位置
        graph[current_count].data  = data;
        graph[current_count].first = nullptr;

        current_count++;
    }

    // 增加关系
    while(1)
    {
        std::cout << "请输入顶点关系(结束 结束 -1):";

        // 出发顶点和终止顶点 权值
        std::string start;
        std::string end;
        int data;
        std::cin >> start >> end >> data;

        if(start=="结束"||end=="结束"||data == -1)
            break;

        // 存储关系
        // 获取start和end在图数组的什么位置
        int index_s = 0,index_e = 0;
        for(;index_s < current_count;index_s++)
            if(graph[index_s].data == start)
                break;
        for(;index_e < current_count;index_e++)
            if(graph[index_e].data == end)
                break;

        // 两个顶点的下标找到了
        if(index_s == current_count||index_e == current_count)
            continue;
        
        // 添加关系
        Relation_t *rt = new Relation_t;
        rt->index = index_e;
        rt->weight = data;
        rt->next = nullptr;

        // 当至少存在出度顶点的时候
        if(graph[index_s].first)
        {
            Relation_t *rt_ptr = graph[index_s].first;
            while(rt_ptr->next)
                rt_ptr = rt_ptr->next;
            
            // 存进关系链表
            rt_ptr->next = rt;
        }
        else{ // 一个出度结点都没有的时候
            graph[index_s].first = rt;
        }
    }

    return graph;
}

void printrelation(Vertex_t *graph)
{
    if(!graph)
        std::cout << "空图" << std::endl;
    
    for(int count_v = 0;count_v < current_count;count_v++)
    {
        std::cout << "顶点<"<< graph[count_v].data <<">:";
        
        Relation_t *rt_ptr = graph[count_v].first;
        while(rt_ptr)
        {
            std::cout<< "<"<< graph[count_v].data <<","<< 
            graph[rt_ptr->index].data <<">" 
            << "("<<rt_ptr->weight<<")";
        
            rt_ptr = rt_ptr->next;
        }
        std::cout << std::endl;
    }
}


// 计算下标
int getIndex(Vertex_t *graph,std::string vertex_v)
{
    for(int index = 0;index < current_count;index++)
    {
        if(vertex_v == (graph[index]).data)
            return index;
    }
    return -1;
}



/*
    逻辑:
        类似于树的层次遍历,初始化的时候,图中的各项顶点均未被访问,
        从图某个顶点开始(V0)依次的访问V0的各个邻接点(广度优先)
        然后分别冲这些被访问过的顶点出发,仍然按照广度优先的策略访问其他的顶点...直到所有能访问的顶点均被访问完毕。
    @brief 广度优先搜索算法
    @param graph 需要进行广度优先搜索的图指针
*/
void BFS(Vertex_t *graph,std::string v0)
{
    int v0_pos = getIndex(graph,v0);
    if(v0_pos == -1)
        return;

    // 搞个队列
    std::queue<int> q;

    // 入队V0
    q.push(v0_pos);

    // 标志位数组,且初始化所有顶点都为未被访问
    static bool *flags=new bool[current_count];

    // 访问V0
    flags[v0_pos] = true;

    int current_pos = v0_pos;

    while(q.size())
    {
        Relation_t *currnet_relation=graph[current_pos].first;

        // 当前关系是否存在 循环的作用:是将该顶点的所有边都入队
        while(currnet_relation)
        {
            // 是否被访问过
            if(flags[currnet_relation->index] == false)
            {
                q.push(currnet_relation->index);
                flags[currnet_relation->index] = true;
            }

            // 递归结束返回,再次访问下一个邻接点
            currnet_relation = currnet_relation->next;
        }

        // 出队,打印、标记
        current_pos = q.front();
        q.pop();

        std::cout << graph[current_pos].data << " ";
        
    }

    std::cout << std::endl;
}



int main()
{
    Vertex_t *graph = creatGraph(10);

    BFS(graph,"A");

    delete []graph;

    return 0;
}

相关文章:

  • 8.26 T4 日记和编辑器(fhq维护kmp——kmp本身含有的单射与可合并性)
  • JS WebSocket 深度解析
  • forEach和map遍历大数据,到底谁更快?实践出真知
  • 字符串压缩算法
  • 相机坐标系转换世界坐标系,zedimudepth
  • WSL-ubuntu下载安装配置cudnn
  • AutoGPT开源项目解读
  • spyglass-lint关闭(disable/stop/waiver)规则
  • 书生.浦江大模型实战训练营——(十三)茴香豆:企业级知识库问答工具
  • Android高级UI --- canvas
  • Android 12中读写SD卡,提示Operation not permitted问题处理
  • AI嵌入式人工智能开发 --- 【1】初始RKNPU
  • word、pdf、excel及zip加密(含示例效果及工具类)
  • 5 大场景上手通义灵码企业知识库 RAG
  • dma 和 mmap 的区别和联系
  • 【系统架构设计】测试评审方法
  • OSPF路由协议详解
  • 2025秋招大语言模型落地实践面试题
  • 更改了ip地址怎么改回来
  • Git —— 1、Windows下安装配置git
  • 苹果手机为何无法在美制造?全球供应链难迁移
  • 暗蓝评《性别打结》丨拆解性别之结需要几步?
  • 伊朗港口爆炸已致46人死亡
  • 事关稳就业稳经济,10张海报看懂这场发布会的政策信号
  • 伊朗国防部发言人:发生爆炸的港口无进出口军用物资
  • 楼下电瓶车起火老夫妻逃生时被烧伤,消防解析躲火避烟注意事项