前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >(Graph)图,挑着看看

(Graph)图,挑着看看

作者头像
看、未来
发布2020-08-26 11:21:55
4170
发布2020-08-26 11:21:55
举报

前言

这个数据结构很繁琐,说实话我不喜欢。但是不学吧,总觉得我在数据结构这一块有个大漏洞。 在网上各处搜罗,看了大家对于面试会问到的有关图的内容,想了想,就先整理那部分吧。

图的遍历

深度优先搜索(dfs)

所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找子结点,然后找兄弟结点。

在这里插入图片描述
在这里插入图片描述

很明显,在由于边是没有方向的,所以,如果4和5顶点相连,那么4会出现在5的相邻链表中,5也会出现在4的相 邻链表中,那么为了不对顶点进行重复搜索,应该要有相应的标记来表示当前顶点有没有搜索过,可以使用一个布 尔类型的数组 boolean[V] marked,索引代表顶点,值代表当前顶点是否已经搜索,如果已经搜索,标记为true, 如果没有搜索,标记为false;

代码语言:javascript
复制
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; } 
}

广度优先搜索

所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找兄弟结点,然后 找子结点。

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
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; 
	} 
}

拓扑排序

给定一副有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素,此时就可以明 确的表示出每个顶点的优先级。

代码语言:javascript
复制
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 graph\n"; 
        return; 
    }   
    for (int i=0; i<top_order.size(); i++) 
        cout << top_order[i] << " "; 
    cout << endl; 
} 

检测有向图中的环

如果我们要使用拓扑排序解决优先级问题,首先得保证图中没有环的存在。

代码语言:javascript
复制
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)

  1. 维护一个最短路径的的集合(sptSet)和最短距离数组, 直到遍历所有的点, 初始化起始点的距离是0, 集合为空.
  2. 初始化起始点s到所有的点的距离是INF, 注意s到s的距离是0.
  3. 、while sptSet 不包含所有的顶点: 1. 选择当前能到达点的最小距离的点u,加入 sptSet 2. 使用u作为中间顶点,更新所有点的距离,选择最小距离的替换 3. dist[u]+graph[u][v] < dist[v]
代码语言:javascript
复制
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;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-07-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 图的遍历
    • 深度优先搜索(dfs)
      • 广度优先搜索
      • 拓扑排序
      • 检测有向图中的环
      • 最短路径(Dijkstra)
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档