数据结构(八):图介绍及面试常考算法

一、图介绍

1、定义

图由结点的有穷集合V和边的集合E组成。其中,结点也称为顶点。一对结点(x, y)称为边(edge),表示顶点x连接到顶点y。边可以包含权重/成本,显示从顶点x到y所需的成本。若两个顶点之前存在一条边,就表示这两个顶点具有相邻关系。

2、类型

(1)无向图

(2)有向图

3、表示方法

(1)邻接矩阵

邻接矩阵存储结构就是用矩阵表示图中各顶点之间的邻接关系,两个顶点有邻接关系,就记录为1,否则为0。

(2)邻接表

邻接表是图的一种顺序存储与链式存储相结合的存储方式。

4、遍历方法

(1)广度优先搜索

(2)深度优先搜索

二、面试常考的算法

1、实现深度优先搜索

 题目:如下无向图,从节点A开始遍历,其深度优先搜索为:A B D E F C。

思路:深度优先搜索,创建visited数组,用于记录所有被访问过的顶点。

(1)从A出发,访问A。

(2)找出A的第一个没有被访问的邻接点,访问该点。以该顶点为新顶点,重复此步骤,直至刚访问过的顶点没有未被访问的邻接点为止。

(3)返回前一个访问过的仍有未被访问邻接点的顶点,继续访问该顶点的下一个未被访问邻接点。

(4)重复2,3步骤,直至所有顶点均被访问,搜索结束。

#include<iostream>
using namespace std;
#include<vector>
#include<set>
#include<unordered_set>
#include<map>

class Graph{
    private:
        map<char, vector<char>> graph;  //创建图,graph记录图的节点及邻接关系
    public:
        void add_edge(char node, vector<char> neibors){
            graph[node] = neibors;
        }

        void dfs_helper(char node, map<char, int>& visited){
            visited[node] = 1;
            cout << node << " ";
            for(auto n: graph[node]){
                // 判断当前节点的邻接节点有无被访问
                if(visited[n] != 1)
                    dfs_helper(n, visited);
                }
            }
        
        void dfs(char start_node){
            map<char,int> visited; // visited数组用来记录所有被访问过的顶点,被访问过,就记为1
            dfs_helper(start_node, visited);
        }  
};

int main(){
    Graph graph;
    graph.add_edge('A', {'B', 'C'});
    graph.add_edge('B', {'A', 'D', 'E'});
    graph.add_edge('C', {'A', 'F'});
    graph.add_edge('D', {'B'});
    graph.add_edge('E', {'B', 'F'});
    graph.add_edge('F', {'C', 'E'});
    graph.dfs('A');
}

 

2、实现广度优先搜索

题目:如下无向图,从节点A开始遍历,其广度优先搜索为:A B D C E,从节点B开始遍历,其广度优先搜索为:B A C D E。

思路:广度优先搜索,利用队列来实现。把访问到的顶点入队,再访问该顶点的所有相邻的顶点,等访问完了该顶点的邻接点,再出队顶点和其相邻的顶点,每出队一个,就入队该顶点的未访问过的邻接顶点,重复上述步骤,直到队列为空。

#include<iostream>
#include<queue>
#include<map>
using namespace std;
class Graph{
    private:
        map<char, vector<char>> graph;  //创建图,graph记录图的节点及邻接关系
    public:
        void add_edge(char node, vector<char> neibors){
            graph[node] = neibors;
        }
        // 层次(广度)遍历
        void bfs(char node){
            map<char, int> visited; // visited数组用来记录所有被访问过的顶点,被访问过,就记为1
            queue<char> que;
            que.push(node);
            visited[node] = 1;
            
            while(!que.empty()){
                char temp = que.front(); //出队
                que.pop();
                cout << temp << " ";
                node = temp; //记录出队的点
                for(auto neibor: graph[node]){
                if(visited[neibor] != 1)
                    que.push(neibor); //访问出队点的邻接点,并入队
                    visited[neibor] = 1; //已访问的顶点标记为1
                }
            }
        }
};

int main(){
    Graph graph;
    graph.add_edge('A', {'B', 'D'});
    graph.add_edge('B', {'A', 'C', 'D'});
    graph.add_edge('C', {'B', 'D', 'E'});
    graph.add_edge('D', {'A', 'B', 'C', 'E'});
    graph.add_edge('E', {'C', 'D'});
    graph.bfs('B');
}

 

3、利用拓扑排序判断图是否有环路。

题目如下有向图,判断是否存在环路。如果不为树,输出其拓扑排序。

思路通过BFS实现拓扑排序。

(1)首先计算每个节点的入度,将所有入度为0的节点放入队列中

(2)开始执行BFS,我们取队首的节点u,放入结果中;移除u的所有出边,即将u的所有相邻节点的入度减少1,判断如果某个相邻的节点v的入度变为0,就将v放入队列中。

(3)当BFS结束后,如果答案中包含的节点数和图中的节点数相等,那么就得到了图G的拓扑排序,否则说明图中存在环,不存在拓扑排序。

// 利用拓扑排序判断有向图是否存在回路
#include<iostream>
#include<map>
#include<vector>
#include<queue>
using namespace std;
class Graph{
    private:
        map<char, vector<char>> graph;
    public:
        void add_edge(char node, vector<char> neibors){
            graph[node] = neibors;
        }
        // 广度优先搜索+队列判断
        void has_cycle(){
            map<char, int> indegree; //记录每个顶点的入度,默认为0
            for(auto g: graph){
                indegree[g.first] = 0;
            }
            //计算每个顶点的入度
            for(auto g: graph){
                char n = g.first;
                for(auto nei: graph[n]){
                    indegree[nei] += 1;
                }
            }

            // 1、将所有入度为0的顶点入队
            queue<char> que;
            for(auto in: indegree){
                char c = in.first;
                if(in.second == 0)
                    que.push(c);
            }
            // 2、开始执行BFS,每出队一个顶点,就将该顶点的所有边移除,
            // 即将该顶点所有相邻节点的入度减1,如果某个相邻节点的入度变为0,就将该节点放入队列中
            vector<char> tuopu;
            while(!que.empty()){
                char temp = que.front();
                que.pop();
                tuopu.push_back(temp);
                for(auto neibor: graph[temp]){
                    indegree[neibor] -= 1;
                    if(indegree[neibor] == 0)
                        que.push(neibor);
                }
                
            }
            // 3.判断拓扑排序结果里的顶点个数,如果和图的个数相等,则没有环
            if(tuopu.size() == graph.size()){
                cout << "该图没有环,为树" << endl << "拓扑排序为:";
                for(auto t: tuopu){
                     cout << t << " ";
                }    
            }
            else
                cout << "该图有环,不为树";
        }
};

int main(){
    // 没有环
    Graph graph;
    graph.add_edge('A', {'B', 'C'});
    graph.add_edge('B',{});
    graph.add_edge('C',{'D','E'});
    graph.add_edge('D', {'B'});
    graph.add_edge('E', {});
    graph.has_cycle();

    // 有环
    Graph graph2;
    graph2.add_edge('A', {'B', 'C'});
    graph2.add_edge('B', {'C'});
    graph2.add_edge('C',{'D','E'});
    graph2.add_edge('D', {'B'});
    graph2.add_edge('E', {});
    graph2.has_cycle();
    return 0;
}

 

4、计算图的边数

题目:给定如下无向图,输出该图的边数7。

思路:遍历图中的节点,若该节点的邻接节点没有被访问,则边数count+1。

#include<iostream>
#include<queue>
#include<map>
using namespace std;
class Graph{
    private:
        map<char, vector<char>> graph;  //创建图,graph记录图的节点及邻接关系
    public:
        void add_edge(char node, vector<char> neibors){
            graph[node] = neibors;
        }
        // 计算图的边数
        void getNumsofEdge(){
            map<char, int> visited; //记录节点是否访问
            int count = 0;
            // 遍历graph中的节点与邻接节点
            for(auto g: graph){
                char n = g.first;
                for(auto neibor: graph[n]){
                    if(visited[neibor] != 1){
                        count += 1;
                    }
                    // 每遍历一个节点,就将该节点标记为1
                    visited[n] = 1;
                }
            }
            cout << count;
        }
};
int main(){
    Graph graph;
    graph.add_edge('A', {'B', 'D'});
    graph.add_edge('B', {'A', 'C', 'D'});
    graph.add_edge('C', {'B', 'D', 'E'});
    graph.add_edge('D', {'A', 'B', 'C', 'E'});
    graph.add_edge('E', {'C', 'D'});
    graph.getNumsofEdge();
}

 

5、找到两个顶点之间的最短路径

题目:如下无向图,A到F的路径有A->B->C->E->F,A->B->C->F,A->D->E->F,输出最短路径A->D->E->F。

 思路:广度优先搜索(BFS)+ 队列实现。

(1)准备queue和map,queue用于BFS,map<char, vector<char>>用于存储当前最短距离。

(2)BFS,将顶点node1入队,并向Map中添加键值对。

(3)当队列非空时,进行循环。现将队首元素x出队,当前路径等x的当前路径,然后遍历x的邻接节点,如果邻接点中的某个节点tmp不在map键值对中(相当于未访问过), 就向Map中加入键值对<tmp,当前路径>,并将tmp入队,如果tmp为node2,就返回Map中tmp对应的路径。

#include<iostream>
#include<queue>
#include<map>
using namespace std;
class Graph{
    private:
        map<char, vector<char>> graph;  //创建图,graph记录图的节点及邻接关系
    public:
        void add_edge(char node, vector<char> neibors){
            graph[node] = neibors;
        }
    // 计算两个顶点的最短路径:A->B->D->F
        vector<char> getShortPath(char node1, char node2){
            map<char, vector<char>> ShortPath;  //用于存储当前最短路径
            queue<char> q; //用于广度优先搜索
            // 如果求自身到自身的最短路径,返回node1->node2
            if(node1 == node2){
                return {node1, node2};
            }
            //否则将node1入队,并向map中加入键值对
            q.push(node1);
            ShortPath[node1] = {node1};

            while(!q.empty()){
                char temp = q.front();
                q.pop();
                vector<char> path = ShortPath[temp];
                for(auto neibor: graph[temp]){
                    if(ShortPath.find(neibor) == ShortPath.end()){
                        // 如果邻接节点不在map中(相当于未访问过),就向map中加入键值对
                        ShortPath[neibor] = path;
                        ShortPath[neibor].push_back(neibor);
                        q.push(neibor);
                        if(neibor == node2)
                            return ShortPath[neibor];
                    }
                }
            }
            return {};
        }
};
int main(){
    Graph graph;
    graph.add_edge('A', {'B', 'D'});
    graph.add_edge('B', {'A', 'C', 'D'});
    graph.add_edge('C', {'B', 'D', 'E'});
    graph.add_edge('D', {'A', 'B', 'C', 'E'});
    graph.add_edge('E', {'C', 'D', 'F'});
    graph.add_edge('F', {'C','E'});

    vector<char> short_path = graph.getShortPath('A', 'F');
    for(auto s: short_path){
        cout << s;
    }      
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/257315.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分享一个冬日雪景

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 先看效果&#xff1a; 再看源码&#xff1a; <body><div id"container"><div id"layer-1" class…

【Lidar】Open3D点云DBSCAN聚类算法:基于密度的点云聚类(单木分割)附Python代码

1 DBSCAN算法介绍 DBSCAN聚类算法是一种基于密度的聚类算法&#xff0c;全称为“基于密度的带有噪声的空间聚类应用”&#xff0c;英文名称为Density-Based Spatial Clustering of Applications with Noise。 DBSCAN聚类算法能够发现任意形状的类别&#xff0c;并且对噪音数据具…

Vue3+Echarts:堆积柱状图的绘制

一、需求 在Vue3项目中&#xff0c;想用Echarts来绘制堆积柱状图&#xff0c;去展示最近一周APP在不同渠道的登录人数效果如下&#xff1a; 二、实现 (关于Echarts的下载安装以及图表的样式设计&#xff0c;此处不展开&#xff01;) 1、Templates部分 <template>&l…

Metashape 自定义比例尺 / 无POS时如何制作DEM

前言操作步骤 前言 Metashape 自定义比例尺 和 无POS时如何制作DEM&#xff0c;此二者的操作步骤本质上是一样的。 当我们输入的照片没有POS&#xff0c;且没有做像控点的时候&#xff0c;比如我们仅仅拍摄了一个比较小的物体&#xff0c;可能是一瓶饮料或者一个椅子。 那么此…

用于噪声和分段相位测量的鲁棒相位展开算法(全文翻译-2区Optics Express)

摘要&#xff1a;本文提出了一种在存在噪声和分段相位的情况下进行相位展开的鲁棒相位展开算法&#xff08;RPUA&#xff09;。RPUA方法提出了一种新的相位导数模型&#xff0c;结合纠错迭代来实现抗噪声效果。此外&#xff0c;它使用数值载波频率和条纹外推法在空间域中桥接相…

git缓存区、本地仓库、远程仓库的同步问题(初始化库无法pull和push)

git新建库与本地库同步 gitee使用教程&#xff0c;git的下载与安装接不在叙述了。 新建远程仓库 新建远程仓库必须要使用仓库提供的api&#xff0c;也就是仓库门户网站&#xff0c;例如gitee&#xff0c;github&#xff0c;gitlab等。在上图中使用gitee网址中新建了一个test仓…

你真的会写 Prompt ? 剖析 RAG 应用中的指代消解

随着 ChatGPT 等大语言模型(LLM)的不断发展&#xff0c;越来越多的研究人员开始关注语言模型的应用。 其中&#xff0c;检索增强生成&#xff08;Retrieval-augmented generation&#xff0c;RAG&#xff09;是一种针对知识密集型 NLP 任务的生成方法&#xff0c;它通过在生成过…

带外应用程序安全测试 (OAST)

Burp Suite的polling.oastify.com的dns请求类似全流量中的旁路检测&#xff0c;或是云原生中的边车模式检测&#xff0c;类似引用带外的DNSLog。 一、传统的动态测试 传统的动态测试简单而优雅。从本质上讲&#xff0c;它将有效负载发送到目标应用程序并分析返回的响应 - 就像…

清华提出ViLa,揭秘 GPT-4V 在机器人视觉规划中的潜力

人类在面对简洁的语言指令时&#xff0c;可以根据上下文进行一连串的操作。对于“拿一罐可乐”的指令&#xff0c;若可乐近在眼前&#xff0c;下意识的反应会是迅速去拿&#xff1b;而当没看到可乐时&#xff0c;人们会主动去冰箱或储物柜中寻找。这种自适应的能力源于对场景的…

Vim命令大全(超详细,适合反复阅读学习)

Vim命令大全 Vim简介Vim中的模式光标移动命令滚屏与跳转文本插入操作文本删除操作文本复制、剪切与粘贴文本的修改与替换文本的查找与替换撤销修改、重做与保存编辑多个文件标签页与折叠栏多窗口操作总结 Vim是一款文本编辑器&#xff0c;是Vi编辑器的增强版。Vim的特点是快速、…

云仓酒庄的品牌雷盛红酒LEESON分享起泡酒要醒酒吗?

常喝葡萄酒的朋友知道&#xff0c;陈年酒、单宁含量重的红酒都需要在喝之前进行醒酒&#xff0c;有朋友问了&#xff0c;起泡酒需要醒酒吗&#xff1f;关于起泡酒醒酒有两种声音&#xff0c;有人反对&#xff0c;认为醒酒会让起泡酒失去细腻的泡泡。有人支持认为醒酒可以让起泡…

蜘点云原生之 KubeSphere 落地实践过程

作者&#xff1a;池晓东&#xff0c;蜘点商业网络服务有限公司技术总监&#xff0c;从事软件开发设计 10 多年&#xff0c;喜欢研究各类新技术&#xff0c;分享技术。 来源&#xff1a;本文由 11 月 25 日广州站 meetup 中讲师池晓东整理&#xff0c;整理于该活动中池老师所分享…

内网安全—Windows系统内核溢出漏洞提权

系统内核溢出漏洞提权 往缓冲区中写入超出限定长度的内容&#xff0c;造成缓冲区溢出&#xff0c;从而破坏程序的堆栈进而运行自己精心准备的指定代码&#xff0c;达到攻击的目的。 分类&#xff1a; 堆溢出 栈溢出 查找补丁的方法 1、手工查找补丁情况 systeminfo Wmic qfe…

福德植保无人机:让植保工作更轻松

亲爱的读者们&#xff0c;欢迎来到我们的公众号&#xff01;今天&#xff0c;我想和大家分享一个我们生活中不可或缺的东西——福德植保无人机。它不仅改变了我们的植保工作&#xff0c;更提升了工作效率&#xff0c;减少了人工负担。福德植保无人机&#xff0c;一家在植保无人…

3ds max软件中的一些常用功能分享!

3ds max软件有很多小伙伴反馈说&#xff0c;明明有很多3ds max教程资料。却不知道如何入门3dmax。 掌握3dmax基本功能是开始使用3dmax的基础之一&#xff0c;所以&#xff0c;小编带大家盘点一下3dmax常用操作。 3dmax常用功能介绍如下&#xff0c;快快跟着小编一起看起来。 1…

回归预测 | MATLAB实现GA-LSSVM基于遗传算法优化最小二乘向量机的多输入单输出数据回归预测模型 (多指标,多图)

回归预测 | MATLAB实现GA-LSSVM基于遗传算法优化最小二乘向量机的多输入单输出数据回归预测模型 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GA-LSSVM基于遗传算法优化最小二乘向量机的多输入单输出数据回归预测模型 &#xff08;多指标&#…

红外二极管发射电路图大全

红外二极管发射电路图&#xff08;一&#xff09; 传感器检测及声光报警电路 传感器模块由热释电传感器、烟雾传感器MQ211和红外传感器组成。 烟雾传感器的内部电阻是随着烟雾的浓度的变化而变化&#xff0c;因此要将其转化为变化的电压信号&#xff0c;在此通过电压比较器LM…

智能监控平台/视频共享融合系统EasyCVR如何做到不被其他软件强制终止?具体如下

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。国标GB28181流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频…

linux 多路径multipath的安装

1. 什么是多路径 在计算机系统中&#xff0c;多路径是指在存储系统中使用多个物理路径来连接主机和存储设备&#xff0c;以增加系统的可用性和容错性。多路径技术的目标是提供冗余路径&#xff0c;以确保在某个路径发生故障时&#xff0c;数据仍然可以通过其他路径进行传输具体…

【UE5.1】M4自动地形材质+UltraDynamicSky+Oceanology插件的使用记录

目录 效果 步骤 一、项目准备 二、插件使用记录 准备过程 M4自动地形插件使用过程 超动态天空插件使用过程 运行时修改天空效果 运行时修改天气效果 海洋插件使用过程 在海洋中游泳 效果 步骤 一、项目准备 1. 创建一个第三人称游戏工程 2. 将M4文件夹和Ultr…
最新文章