目录
目录
前言
个人关于图的基本概念梳理整合
一、图相关的基本概念
1.图的定义
图G由顶点集V和边集E组成,记为 G = (V , E);
V(G) 表示图G中顶点的有限非空集(这与树、线性表可为空集不同);
E(G)表示图G中顶点之间的关系集合。
2.生成子图与路径
生成子图定义:
包含原图G中所有顶点的子图。
路径:
起始顶点到终点顶点的顶点序列。(若是不包含重复的顶点的路径,则称为简单路径)
路径的长度:
路径上边的数目,若该路径最短,则称其为距离。
回路:
第一个顶点和最后一个顶点相同的路径。
3.图的分类
(1)有向图与无向图
无向图
(1)连通图:任意两个顶点之间都是连通的,即两顶点间都存在路径,可以互相往来
(2) 极大连通子图(连通分量):在保持连通的情况下,尽可能多的包含顶点和边的子图。(若原图为连通图,则连通分量就是其本身;若原图为非连通图时,则连通分量有多个)
(3)极小连通子图:在某一顶点子集所确定的连通子图中包含的边最少的连通子图。(若有N个顶点,则有N-1条边,极小连通子图个数不一定唯一)
(4)生成树:由原图的全部顶点所确定的一个极小连通子图。(包含生成子图和极小连通子图的概念统一,原图必须为连通图,生成树的个数不一定唯一)
(5)生成森林:由原非连通图的每个连通分量的生成树组成的非连通图。
(6)顶点的度:以该顶点为端点的边的个数
有向图
(1)强连通图:任意两个顶点之间都是强连通的,即两顶点间都有相互达到的弧。
(2)极大强连通子图:概念类似无向图
(3)极小强连通子图:概念类似无向图
(4)顶点的出度(或入度):以该顶点为起点(或终点)的弧的个数
(5)顶点的度:该顶点的出度和入度的和
(6)有向树:一个顶点的入度为0、其余顶点的入度均为1的有向图。(与树的区别,结点的度表示含义不同,树的结点度表示该结点的孩子结点的个数,而有向图的顶点的度表示该顶点的入度和出度之和)
(2)简单图与多重图
简单图:无重复边且不存在顶点到自身的边
多重图:有重复边或存在顶点到自身的边
(3)有向与无向完全图
无向完全图:任意两个顶点之间都有边
有向完全图:任意两个顶点之间都存在方向相反的弧
注意:这与连通图顶点间要求有路径是不同的
(4)稠密图和稀疏图(边少与边多的图)
二、图的存储结构
1.邻接矩阵法
实现:用二维数组来表示边的关系
#include<bits/stdc++.h>
using namespace std;
struct Vex{
int num;
string name;
};
typedef struct Graph{
Vex vex[20];
int edges[20][20];
}ALGraph;
2.邻接表法
用数组和链表相结合表示边的关系,用链表来存储图中边或弧的信息,也可以用动态数组(如vector)代替链表。存储效率优于邻接矩阵。
#include<bits/stdc++.h>
using namespace std;
//------------------------------------------------
1.基本写法
//边表
typedef struct ArcNode{
int num;
struct ArcNode *next;
}ArcNode;
//顶点表
typedef struct VNode{
int data;
string name;
ArcNode *frist;
}VNode,AdjList[20];
//邻接表
typedef struct{
AdjList vetices;
int vexnum,arcnum;
}ALGraph;
2.使用vector代替链表,进行边的存储
vector<int> edge[500];
void add(int from, int to){
edge[from].push_back(to);
}
3.用结构体代替链表,进行边的存储
struct Edge{
int to, next;
}edge[V];
int head[N], tot;//tot表示下一个可用边
void init(){
memset(head, oxff, sizeof(head)); //head初始化-1
tot = 0; //当前可用边为0
}
void addedge(int from, int to){
edge[tot] = {to, head[from]};
head[from] = tot++;
}
3.邻接多重表法
4.十字链表法
三、图的搜索
1.深度优先搜索DFS
#include<bits/stdc++.h>
#include<queue>
#define MAX 100
struct Vex{
int num;
string name;
};
struct Edge{
int from,to,value;
};
class Graph{
private:
Vex vex[MAX];
int AdjMatrix[MAX][MAX];
bool visited[MAX];
int vexNum;
queue<Vex> q;
public:
void Init();
bool DFS(int num);
bool BFS(int num);
};
void Graph:: Init(){
for(int i = 0; i < MAX; i++)
visited[i] = false;
}
bool Graph::DFS(int num){
visited[num] = true;
cont << vex[num].num << endl;
for(int j=0; j< MAX;j++)
if(visited[j]!= true && AdjMatrix[num][j] == 1)
DFS(j);
}
bool Graph::BFS(int num){
visited[num] = true;
q.push(vex[num]);
while(!q.empty()){
int n = q.size();
while(n--){
Vex vex = q.front();
q.pop();
visited[vex] = true;
for(int j=0; j< MAX;j++)
if(visited[j]!= true && AdjMatrix[num][j] == 1)
q.push(vex[j]);
}
}
}
2.广度优先搜索BFS
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
class Solution {
public:
/**
*
* @param root TreeNodeÀà
* @return intÕûÐÍvector<vector<>>
*/
vector<vector<int> > levelOrder(TreeNode* root) {
// write code here
vector<vector<int>> res;
if(!root )
return res;
queue<TreeNode *> qu;
qu.push(root);
while(!qu.empty()){
vector<int> vec;
int size = qu.size(); //统计每层个数
while(size--){
TreeNode *node = qu.front();
qu.pop();
vec.push_back(node->val);
if(node->left)
qu.push(node->left);
if(node->right)
qu.push(node->right);
}
if(vec.size() > 0)
res.push_back(vec);
}
return res;
}
};
四、图的应用
memset初始化:
const int N = 2e5 + 7;
int a[N][N];
memset初始化数组,是以第二个参数的转换为二进制后,最后面的8位来初始化所有内存的
memset(a, 0, sizeof(a));
memset(a, -1, sizeof(a)); 等价于 memset(a, 0xff, sizeof(a)); //初始化-1
//初始化为无穷
#define INF 0x3f3f3f3f
memset(a, INF, sizeof(a));
1.最小生成树
(1)Kruskal算法
此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。
1. 把图中的所有边按代价从小到大排序;
2. 把图中的n个顶点看成独立的n棵树组成的森林;
3. 按权值从小到大选择边,所选的边连接的两个顶点ui,viui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。
4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。
(2)Prim算法
此算法可以称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。
- 图的所有顶点集合为VV;初始令集合u={s},v=V−uu={s},v=V−u;
- 在两个集合u,vu,v能够组成的边中,选择一条代价最小的边(u0,v0)(u0,v0),加入到最小生成树中,并把v0v0并入到集合u中。
- 重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。
由于不断向集合u中加点,所以最小代价边必须同步更新;需要建立一个辅助数组closedge,用来维护集合v中每个顶点与集合u中最小代价边信息,:
2.最短路径
总结
简单介绍了图的基本概念,需要重要理解的是两个顶点之间有边与有路径区别、无向图的连通分量与生成树的概念、有向图的顶点的度、