(Graph)图,挑着看看
时间:2022-07-23
本文章向大家介绍(Graph)图,挑着看看,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
前言
这个数据结构很繁琐,说实话我不喜欢。但是不学吧,总觉得我在数据结构这一块有个大漏洞。 在网上各处搜罗,看了大家对于面试会问到的有关图的内容,想了想,就先整理那部分吧。
图的遍历
深度优先搜索(dfs)
所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找子结点,然后找兄弟结点。
很明显,在由于边是没有方向的,所以,如果4和5顶点相连,那么4会出现在5的相邻链表中,5也会出现在4的相 邻链表中,那么为了不对顶点进行重复搜索,应该要有相应的标记来表示当前顶点有没有搜索过,可以使用一个布 尔类型的数组 boolean[V] marked,索引代表顶点,值代表当前顶点是否已经搜索,如果已经搜索,标记为true, 如果没有搜索,标记为false;
public class DepthFirstSearch {
//索引代表顶点,值表示当前顶点是否已经被搜索
private boolean[] marked;
//记录有多少个顶点与s顶点相通
private int count;
//构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相邻顶点
public DepthFirstSearch(Graph G,int s){
//创建一个和图的顶点数一样大小的布尔数组
marked = new boolean[G.V()];
//搜索G图中与顶点s相同的所有顶点 dfs(G,s);
}
//使用深度优先搜索找出G图中v顶点的所有相邻顶点
private void dfs(Graph G, int v){
//把当前顶点标记为已搜索
marked[v]=true;
//遍历v顶点的邻接表,得到每一个顶点w
for (Integer w : G.adj(v)){
//如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点
if (!marked[w]){
dfs(G,w);
}
}
//相通的顶点数量+1
count++;
}
//判断w顶点与s顶点是否相通
public boolean marked(int w){ return marked[w]; }
//获取与顶点s相通的所有顶点的总数 4
public int count(){ return count; }
}
广度优先搜索
所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找兄弟结点,然后 找子结点。
public class BreadthFirstSearch {
//索引代表顶点,值表示当前顶点是否已经被搜索
private boolean[] marked;
//记录有多少个顶点与s顶点相通
private int count;
//用来存储待搜索邻接表的点
private Queue<Integer> waitSearch;
//构造广度优先搜索对象,使用广度优先搜索找出G图中s顶点的所有相邻顶点
public BreadthFirstSearch(Graph G, int s) {
//创建一个和图的顶点数一样大小的布尔数组
marked = new boolean[G.V()];
//初始化待搜索顶点的队列
waitSearch = new Queue<Integer>();
//搜索G图中与顶点s相同的所有顶点
dfs(G, s);
}
//使用广度优先搜索找出G图中v顶点的所有相邻顶点
private void dfs(Graph G, int v) {
//把当前顶点v标记为已搜索
marked[v]=true;
//把当前顶点v放入到队列中,等待搜索它的邻接表
waitSearch.enqueue(v);
//使用while循环从队列中拿出待搜索的顶点wait,进行搜索邻接表
while(!waitSearch.isEmpty()){
Integer wait = waitSearch.dequeue();
//遍历wait顶点的邻接表,得到每一个顶点w
for (Integer w : G.adj(wait)) {
//如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点
if (!marked[w]) {
dfs(G, w);
}
}
}
//相通的顶点数量+1
count++;
}
//判断w顶点与s顶点是否相通
public boolean marked(int w) {
return marked[w];
}
//获取与顶点s相通的所有顶点的总数
public int count() {
return count;
}
}
拓扑排序
给定一副有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素,此时就可以明 确的表示出每个顶点的优先级。
class Graph {
int V; // 顶点的个数
list<int> *adj; // 所有顶点的起始指针
};
void topologicalSort(int V, list<int> *adj) {
// 计算所有入度
vector<int> in_degree(V, 0);
for (int u=0; u<V; u++) {
list<int>::iterator itr;
for (itr = adj[u].begin(); itr != adj[u].end(); itr++) {
in_degree[*itr]++;
}
}
// 加入入度为0的点
queue<int> q;
for (int i = 0; i < V; i++) {
if (in_degree[i] == 0) q.push(i);
}
int cnt = 0;
vector <int> top_order;
while (!q.empty()) {
int u = q.front();
q.pop();
top_order.push_back(u);
// 所有连接点, 入度减去1
list<int>::iterator itr;
for (itr = adj[u].begin(); itr != adj[u].end(); itr++) {
if (--in_degree[*itr] == 0) q.push(*itr);
}
cnt++;
}
if (cnt != V) {
cout << "There exists a cycle in the graphn";
return;
}
for (int i=0; i<top_order.size(); i++)
cout << top_order[i] << " ";
cout << endl;
}
检测有向图中的环
如果我们要使用拓扑排序解决优先级问题,首先得保证图中没有环的存在。
bool isCyclicGraph(vector<int> &start, vector<int> &end) {
// 找到最大顶点值,构造图,
int n = 0;
for (int s : start) {
n = max(n, s);
}
for (int e : end) {
n = max(n, e);
}
// 构造图
vector<vector<int>> graph(n + 1);
vector<int> d(n + 1);
int m = start.size();
// 计算所有顶点的入度
for (int i = 0; i < m; i++) {
graph[start[i]].push_back(end[i]);
d[end[i]]++;
}
queue<int> que;
// 将所有入度为0的点,加入队列
for (int i = 1; i <= n; i++) {
if (graph[i].size() && !d[i]) {
que.push(i);
}
}
while (!que.empty()) {
int h = que.front();
que.pop();
// 将多有入度为0的点,对应的顶点 入度减去1
for (int y : graph[h]) {
d[y]--;
if (!d[y]) {
que.push(y);
}
}
}
// 判断是否所有顶点的入度都是0, 如果是,则没有环,否则就有
for (int i = 1; i <= n; i++) {
if (d[i]) {
return true;
}
}
return false;
}
最短路径(Dijkstra)
- 维护一个最短路径的的集合(sptSet)和最短距离数组, 直到遍历所有的点, 初始化起始点的距离是0, 集合为空.
- 初始化起始点s到所有的点的距离是INF, 注意s到s的距离是0.
- 、while sptSet 不包含所有的顶点: 1. 选择当前能到达点的最小距离的点u,加入 sptSet 2. 使用u作为中间顶点,更新所有点的距离,选择最小距离的替换 3. dist[u]+graph[u][v] < dist[v]
int minDistance(vector<int> dist, set<int> sptSet) {
int min_d = INT_MAX, u;
for(int i = 0, i < dist.size(); i ++) {
if(sptSet.find(i) == sptSet.end() && dist[v] < min_d) {
min_d = dist[i], u = i;
}
}
return u;
}
// 使用vector 表示的邻接矩阵, return 起始点到所有点的最小距离
// 没有边的用0填充
vector<int> dijstra(vector<vector<int>> graph, set<int> &sptSet,int src) {
int V = graph.size();
vector<int> dist(V, 0);
for(int i = 0;i < V; i ++) {
if(i != src) dist[i] = INT_MAX;
}
while(sptSet.size() < V-1) {
// pick mininum distance u
int u = minDistance(dist, sptSet);
sptSet.insert(u);
// 使用u更新距离
for(int v = 0; v < V; v ++) {
if(sptSet.find(v)==sptSet.end() && graph[u][v]
&& dist[u] != INT_MAX
&& dist[u]+graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}
return dist;
}
- 排序算法对比、总结(Python代码)
- 记一道未能答出的算法面试题
- 关于numpy mean函数的axis参数
- 在Keras+TF环境中,用迁移学习和微调做专属图像识别系统
- Tensorflow的LRN是怎么做的
- 存储Tensorflow训练网络的参数
- 用Ansible部署ELK STACK
- 十六个有用的Linux命令行技巧
- keras系列︱深度学习五款常用的已训练模型
- 基于VGG19的识别中国人、韩国人、日本人分类器
- 机器学习算法GBDT的面试要点总结
- 了解、接受和利用Java中的Optional (类)
- 一个强化学习 Q-learning 算法的简明教程
- 天池大赛—商场中精确定位用户所在店铺 作品分享
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 渗透测试实战 | 一次信息泄露引发的越权
- Vue创建项目及基本语法 一
- 学 Java 开发怎么能不知道 Filter 与 Listener
- Windows 安装 MySQL 常见问题
- LootCode-链表排序-Java
- 好久不用 jQuery, 来复习一下
- Spring学习一、组件注册
- 复习 EL 表达式与 JSTL
- Spring 学习二、Bean生命周期相关注解
- 十分钟学会 HTML
- 聊一下会话跟踪技术
- 朝花夕拾之Matlab基础回顾:向量的点积、叉积、混合积
- 详解响应消息 response
- Kubernetes 1.19.0——deployment(3)
- Selenium-01-测试环境搭建使用