图论基础,如何快速上手图论?

devtools/2025/1/17 20:55:03/

目录

引言-对前面数据结构的总结和图论的引导

一.图的基础概念和基本术语

1.1,图的定义

 1.2,图的种类 -有向图和无向图以及完全图

1.2.1有向图和无向图

1.2.2完全图和非完全图

1.3,图的基本术语

1.3.1度,路径,环...

 1.3.2强连通图和弱连通图

 1.3.3权与网

1.3.4连通分量->生成树

二,图的存储结构

2.1邻接矩阵法

2.2邻接表法

2.3.1无向图的邻接表法​编辑 

2.3.2有向图的邻接表法 

2.3 邻接表和邻接矩阵的比较

三.图的遍历-BFS/DFS

3.1,DFS遍历

3.2,BFS遍历


引言-对前面数据结构的总结和图论的引导

前面我们学过了一些基本的数据结构,像顺序表,链表,栈,队列,树等...其中最有难度的就属树的部分了,而图论的与树也是有关联的,在后续我们经常可以看到一些图类似树,但是又不是树,他们的区别在哪?二叉树是父亲节点和孩子的关联,是从上到下的,而图没有父亲节点和孩子节点,他主要使用节点描述各个事件之间的关系;

一.图的基础概念和基本术语

1.1,图的定义

图是由顶点集合及顶点间的关系组成的一种数据结构:G(Graph) = (V, E),其中:V代表顶点,E代表边;

 1.2,图的种类 -有向图和无向图以及完全图

1.2.1有向图和无向图

这里我举个例子就十分显而易见了:所谓向就是方向的意思,有向,就是节点的连接是有方向性的;

上图的G2就是一个无向图,各个节点之间连接的边是没有箭头的;

G1是一个有向图,G1的各个节点之间连接的边是有箭头的,那就是方向;

1.2.2完全图和非完全图

什么完全图呢,完全图就是每个节点都要与其他所有的节点连接;

对于无向图来说:假设图的节点个数是n,如果满足这个图的边数是n(n-1)/2,那他就是无向完全图;

对于有向图来说:假设图的节点个数是n,如果满足这个图的边数是n(n-1),那他就是有向完全图;

注意:无向完全图一定成环;

非完全图:不是完全图的图就是非完全图;

是否是完全图的判断方法:因为完全图是每个顶点之间都有连接的,所以我们只要发现有任意两个顶点之间没有连接,就说明不是完全图;反之,如果找不到,就是完全图;

1.3,图的基本术语

1.3.1度,路径,环...

 

 1.3.2强连通图和弱连通图

强连通图(相对有向图)):任意顶点到达其他的顶点,也能从其他顶点回到该顶点;
就下图来说:强连通图部分;因为是连通图,所有V1是可以到达V2,V3,V0的;如果要说他强不强,那就看V2,V3,V4可不可以回到V1;可以回来,就是强连通图;

 1.3.3权与网

权:就是边所代表的值,一般是长度,也叫权值;
网:边有权值的图叫网;

子图:从原图中抽出至少一条边或者一个顶点以及该顶点有关的边的图就是子图;子图要满足的条件是定点与原图的顶点数量一样,但是边的数量要小于原图的边的数量;

图(b)抽掉了V0-V3,V1-V2,

1.3.4连通分量->生成树

连通分量:是原图的连通子图,

极大连通分量:在该连通子图中,在加上任意一条边或一个顶点,都不再联通; 

上图中,(V0,V1,V2,V3)所组成的联通子图和(V4,V5)组成联通子图都是极大联通子图;

下面是有向图的联通分量;

 同理极小联通子图就是本身是联通的,但是再删除任意一条边和定点就不联通了,通常极小联通子图中,两个顶点之间只有一条边;(正因如此,才可以转化为二叉树)

 最小生成树:就是权值之和最小的生成树;后序会提到求最小生成树的两种算法:克鲁斯卡尔(kruskal)和普利姆(Prim)算法;

二,图的存储结构

2.1邻接矩阵法

邻接矩阵法是以顶点映射的下标创建二维数组:通常需要两步;

一:创建顶点集合:这里需要获取定点的个数和提供通过顶点映射对应下标的方法;

二:通过顶点的数量构建二维数组:横坐标表示顶点的出度,纵坐标表示顶点的入度;如果是无向图且V1->V2连接,那么graph[V1][V2]=1(二维矩阵初始化为0);如果是有向图,还需要令graph[V2][V1]=0;

下面是邻接矩阵的code模拟: 


//非类型模版参数分别为  定点类型,权值,权值最大值(标示值/无穷大),有向/无相
template <class V,class W,W MAX_W=INT_MAX,bool direction=true>         
class Graph  
{
public:Graph() = default;    Graph(const V*a,size_t n) {_vertexs.reserve(n);//预留空间  //初始化顶点集for (size_t i = 0; i < n; i++) {_vertexs.push_back(a[i]);    //把数组中的顶点放到容器中 _indexMap[a[i]] = i;//映射下标  }_matirx.resize(n);//可有可无        //初始化邻接矩阵for (size_t i = 0; i < _matirx.size(); i++){//使用最大的权值去表示_matirx[i].resize(n, MAX_W);       //size_t src=_indexMap[]}}//根据起点,终点,权值添加边void Addedge(const V& src, const V& dst, const W& w){//获取定点下标size_t srci = GetVertexIndex(src);size_t dsti = GetVertexIndex(dst);//在邻接矩阵中对应位置更新权值_matirx[srci][dsti] = w;        if (direction == false)//无向图   {_matirx[dsti][srci] = w; //多添加一次          }}
//打印邻接矩阵
void showmatirx()
{cout << "邻接矩阵:" << endl; cout << "   ";for (int i = 0; i < _vertexs.size(); i++)printf("%3c", _vertexs[i]);cout << endl;    for (int i = 0; i < _matirx.size(); i++){printf("%3c", _vertexs[i]);  for (int j = 0; j < _matirx.size(); j++){if (_matirx[i][j] != INT_MAX)printf("%3d", _matirx[i][j]);//如果有权值,就打印elseprintf("  *");}cout << endl;    }
}
private: //确定顶点的下标size_t GetVertexIndex(const V& v){//检查定点是否合法auto it = _indexMap.find(v);if (it != _indexMap.end()){return it->second;}else{assert(false);//如果没有找到就断死    return -1;}}vector<V>_vertexs;//顶点集合        map<V, int>_indexMap;//获得定点对应的下标关系   vector<vector<W>>_matirx;//邻接矩阵         };

2.2邻接表法

邻接表法就是采用链表的方式存储;下面是无向图和有向图的邻接表法示意图;

2.3.1无向图的邻接表法
 

2.3.2有向图的邻接表法 

下面是邻接表的code模拟实现:

namespace  GraphLink
{template<class W>      struct Edge{int _dsti;     W _w;        Edge<W>* _next;     Edge(const int& dsti, const W& w)   :_dsti(dsti),_w(w),_next(nullptr){}};template <class V, class W, W MAX_W = INT_MAX, bool direction = true>class Graph{public:typedef Edge<W> Edge;     Graph() = default;  Graph(const V* a, size_t n){_vertexs.reserve(n);//预留空间  //初始化顶点集for (size_t i = 0; i < n; i++){_vertexs.push_back(a[i]);    //把数组中的顶点放到容器中 _indexMap[a[i]] = i;//映射下标  }//初始化邻接表_tables.resize(n, nullptr); //开出n个指针,指向空}//根据起点,终点,权值添加边void Addedge(const V& src, const V& dst, const W& w){//获取定点下标size_t srci = GetVertexIndex(src);size_t dsti = GetVertexIndex(dst);Edge* eg=new Edge(dsti,w);//创建节点//头插eg->_next = _tables[srci]; _tables[srci] = eg;   //如果是无向图就需要再添加一条边if (direction == false){Edge* eg = new Edge(srci, w);eg->_next = _tables[dsti];  _tables[dsti] = eg;    }}//打印邻接表void show(){cout << "顶点信息" << endl;     //打印顶点信息for (int i = 0; i < _vertexs.size(); i++)cout << '[' << i << ']' << _vertexs[i] << endl;cout << endl;  //遍历邻接表cout << "邻接表:" << endl;      for (int i = 0; i < _tables.size(); i++){cout << _vertexs[i] << '[' << i << "]->";Edge* cur = _tables[i];//该行的头结点while (cur){cout << _vertexs[cur->_dsti] << '[' << cur->_dsti << ']'<<"("<<cur->_w<<")"<<"->";cur = cur->_next;}cout << "nullptr"; cout << endl;   }}private://确定顶点的下标size_t GetVertexIndex(const V& v){//检查定点是否合法auto it = _indexMap.find(v);if (it != _indexMap.end()){return it->second;}else{assert(false);//如果没有找到就断死    return -1;}}vector<V>_vertexs;//顶点集合        map<V, int>_indexMap;//获得定点对应的下标关系   vector<Edge*>_tables;//邻接表                };void graphtest()   {const char str[4] = { 'A','B','C','D' };cout << "无向图:" << endl;     Graph<char, int, INT_MAX, false>g(str, 4);g.Addedge('A', 'B', 6);g.Addedge('A', 'C', 3);g.Addedge('B', 'C', 10);g.Addedge('A', 'D', 4);g.show(); cout << endl << "有向图:" << endl; Graph<char, int, INT_MAX, true>g2(str, 4);g2.Addedge('A', 'B', 6);g2.Addedge('A', 'C', 3);g2.Addedge('B', 'C', 10);g2.Addedge('A', 'D', 4);g2.show();return;}};

2.3 邻接表和邻接矩阵的比较

区别:对于任意无向图和有向图,邻接矩阵都是唯一的(编号按照顶点顺序),但是邻接表是不唯一的,因为他的连接顺序跟顶点编号无关;

空间复杂度:

1.邻接矩阵因需要双层循环遍历,所以空间复杂度是O(n2);

2.邻接表的空间复杂度是O(n);

三.图的遍历-BFS/DFS

图的遍历主要氛围两种:1.深度优先遍历(DFS),2.广度优先遍历(BFS);

深度优先遍历主要实现方法是赌递归调用(堆栈),而广度优先遍历的实现方法是队列;

3.1,DFS遍历

分析:深度优先遍历每次每的每一层都会遍历顶点集合,找到没有访问过的顶点就会递归调用;

void _DFS(size_t srci, vector<bool>& check) //下一个位置的起点以及需要一个标记数组
{cout << srci << ":" << _vertexs[srci] << " " << endl; //先访问 然后标记为访问过check[srci] = true;for (size_t i = 0; i < _vertexs.size(); ++i)if (_matrix[srci][i] != MAX_W && check[i] == false)_DFS(i, check);
}void DFS(const V& src) //需要有一个起点
{size_t srci = GetVertexIndex(src);//找到起点的下标vector<bool> check(_vertexs.size()); //标记数组_DFS(srci, check);//如果是一个非连通图,可以在后面再进行一层检查check数组,然后对false的数组再进行一次访问
}

3.2,BFS遍历

广度优先遍历其实就是层序遍历,一层一层的扩散;需要用到普通队列;

层序遍历 但是没有一层一层出
void BFS(const V& src) //src表示我们的起点
{size_t srci = GetVertexIndex(src);//找到起点的下标queue<int> q;//存储下标的队列size_t n = _vertexs.size();//表示有多少个节点vector<bool> check(n);q.push(srci);check[srci] = true;//入队列就标记while (!q.empty()){int front = q.front();//取队头q.pop();cout << _vertexs[front] << ":"<<front<<endl;//然后让他的朋友进for (size_t i = 0; i < n; ++i)if (_matrix[front][i] != MAX_W && check[i] == false){q.push(i);check[i] = true;}}
}

http://www.ppmy.cn/devtools/151358.html

相关文章

《AI与鸿蒙Next:建筑设计可视化的革新力量》

在建筑设计领域&#xff0c;可视化对于呈现设计理念、与客户沟通以及指导施工等环节都至关重要。人工智能与鸿蒙Next图形渲染技术的发展&#xff0c;为建筑设计可视化带来了前所未有的变革与机遇。 人工智能在建筑设计可视化中的作用 快速生成设计方案&#xff1a;人工智能可以…

React 中hooks之useLayoutEffect 用法总结以及与useEffect的区别

React useLayoutEffect 1. useLayoutEffect 基本概念 useLayoutEffect 是 React 的一个 Hook&#xff0c;它的函数签名与 useEffect 完全相同&#xff0c;但它会在所有的 DOM 变更之后同步调用 effect。它可以用来读取 DOM 布局并同步触发重渲染。 2. useLayoutEffect vs us…

学习微信小程序的下拉列表控件-picker

1、创建一个空白工程 2、index.wxml中写上picker布局&#xff1a; <!--index.wxml--> <view class"container"><picker mode"selector" range"{{array}}" bindchange"bindPickerChange"><view class"pick…

微软与腾讯技术交锋,TRELLIS引领3D生成领域多格式支持新方向

去年 11 月&#xff0c;腾讯推出 Hunyuan3D 生成模型&#xff0c;是业界首个同时支持文字和图像生成 3D 的开源大模型。紧接着不到一个月&#xff0c;微软便发布了全新框架 TRELLIS&#xff0c;加入 3D 资产生成领域的竞争中。TRELLIS 支持多格式输出&#xff0c;包括辐射场、3…

ubuntu22.04:解决google chrome 访问百度页面加载慢的问题

前因&#xff1a;ubuntu22.04系统&#xff0c;本地有两个浏览器&#xff0c;firefox和chrome&#xff0c;firefox无论是使用代理还是不使用代理访问百度网站的速度都十分的快&#xff0c;chrome即使开代理访问百度网站也很慢&#xff0c;但是访问其他网站却很快。 1、在chrome地…

【2024年华为OD机试】(B卷,100分)- 数据分类 (Java JS PythonC/C++)

一、问题描述 题目描述 对一个数据a进行分类&#xff0c;分类方法为&#xff1a; 此数据a&#xff08;四个字节大小&#xff09;的四个字节相加对一个给定的值b取模&#xff0c;如果得到的结果小于一个给定的值c&#xff0c;则数据a为有效类型&#xff0c;其类型为取模的值&…

web程序防止xss攻击和跨域

什么是XSS攻击&#xff1f; xss攻击是指有人恶意编写了一段脚本链接&#xff0c;当用户点击的时候就会向用户所在的服务器发送一个请求&#xff0c;这个请求包含了一段js代码&#xff0c;如果不防止xss那么这段js代码会被执行&#xff0c;这样子后果十分严重&#xff0c;容易被…

Jenkins-简介/安装!

一. 关于持续集成&#xff1a; 持续集成(CI ) [ Continuous Integration ]&#xff0c;通俗来讲&#xff0c;就是一个能监控版本控制系统变化的工具&#xff0c;可以自动编译和测试集成的应用程序。出现问题&#xff0c;能够及时的通知相应人员。持续集成是一种思维工具集&…