Shortest path in complement graph

本文探讨了在给定图的补图中寻找最短路径的算法问题。通过对原始图进行巧妙变换,避免了显式构造补图的过程,提出了一种改进的BFS算法实现方案。

问题描述

      Shortest path in complement graph.:Given a graph G, design an algorithm to find the shortest path (number of edges) between s and every other vertex in the complement graph G'. The complement graph contains the same vertices as G but includes an edge v-w if and only if the edge v-w is not in G. Can you do any better than explicitly computing the complement graph G' and running BFS in G'?

问题分析

  这是一个 比较有意思的问题,它不是一个简单的BFS应用。里面还有一些稍微的变换。

  首先一个,对于一个图的complement graph来说,它是针对每个节点取所有原来和它不相邻的节点建立直接连接。比如说对如下的图来说:

  它对应的complement graph如下:

 

 

  所以这种转换就是针对每个节点将它原来的邻接边换成以前没有邻接的节点。不过,问题里要求计算这个complement graph的最短路径。所以一种办法就是我们根据一个图,先计算它的complement graph。然后再用BFS的方式来计算它的最短路径。这里假定图里的所有边都是一样的,没有特定的权值差别。 可是以这种办法计算的效率如何呢?对于每个节点,我们要对它的边重新计算一遍,所以每个节点的边构造时间就有V,所以光遍历这所有的V个节点它的总体时间复杂度就为V * V了。这里就超出了前面的期望结果。

  所以,这里需要做一个调整。我们可以继续利用BFS的方法,不过这里需要做一点调整。每次我们碰到一个节点的时候,要取它的非当前邻接节点作为后续的候选遍历节点,然后将这个节点加入到队列里。按照这个思路,我们可以得到如下的代码实现:

 

public class ComplementGraph {
	
	private boolean[] marked;
	private int[] distTo;
	private Set<Integer> set;
	public ComplementGraph(Graph g) {
		marked = new boolean[g.vertices()];
		distTo = new int[g.vertices()];
		for(int i = 0; i < g.vertices() i++) {
			distTo[i] = Integer.MAX_VALUE;
		}
	}

	public bfs(Graph g, int s) {
		Queue<Integer> queue = new LinkedList<>();
		marked[s] = true;
		distTo[s] = 0;
		queue.add(s);
		while(!queue.isEmpty()) {
			int e = queue.remove();
			List<Integer> list = makeComplement(set, g.adj(e));
			for(int i : list) {
				if(!marked[i]) {
					marked[i] = true;
					distTo[i] = distTo[e] + 1;
					queue.add(i);
				}
			}
		}
	}

	private List<Integer> makeComplement(Set<Integer> set, List<Integer> adj) {
		// make a complement list which is in set but not in adj
	}

}

  这里就是稍微增加了一个方法在每次遍历一个节点的时候先计算它的complement vertices。这样问题就解决了。 

 

 

参考材料

http://stackoverflow.com/questions/24476027/shortest-path-in-a-complement-graph-algorithm

 http://algs4.cs.princeton.edu/41graph/BreadthFirstPaths.java.html

2.1 图模型定义 (类型:无向、加权/无权、是否含多重边等;顶点与边的实际含义(如:顶点=城市,边=公路)) 2.2 构建方法 (数据来源:真实数据(需注明出处)或仿真生成(如随机图模型);工具:Python的NetworkX) 2.3 可视化展示 3、图的性质分析与计算 输出点、边数量------- num_nodes = G.number_of_nodes() num_edges = G.number_of_edges() 输出一条边的属性 举例 判断是否有点 G.has_node(a) 判断是否有边 G.has_edge(u,v) 输出点a的所有邻居 G.neighbors(a) 输出图邻接对象 G.adj 输出度序列:非增序列 G.degree() 最大度: 最小度: 删除图上度最大的点 输出邻接矩阵 A adj_matrix = nx.adjacency_matrix(G) adj_dense = adj_matrix.todense() 输出关联矩阵 M inc_matrix = nx.incidence_matrix(G) inc_dense = inc_matrix.todense() 输出生成子图 edges_to_keep = [(1, 2), (2, 3), (3, 4), (4, 1)] H = nx.edge_subgraph(G, edges_to_keep) 输出最小生成树 mst = nx.minimum_spanning_tree(G) 输出点导出子图 H = G.subgraph(nodes_subset) 输出边导出子图 edges_subset = [(1, 2), (3, 4)] H = nx.edge_subgraph(G, edges_subset) 计算图的连通分支数目num_components = nx.number_connected_components(G) components = list(nx.connected_components(G)) print("各连通分支的节点集合:", components) 找出图的割点和割边 cut_vertices = list(nx.articulation_points(G)) cut_edges = list(nx.bridges(G)) 找出图的补图 G_complement = nx.complement(G) 计算所有节点对的最短路径长度 lengths = dict(nx.all_pairs_shortest_path_length(G)) 计算半径 r = nx.radius(G) 计算直径 d = nx.diameter(G) 获取中心点 center_nodes = nx.center(G) 计算周长 cycles = list(nx.simple_cycles(G.to_directed())) longest_cycle = max(cycles, key=len, default=[]) circumference = len(longest_cycle) 点连通度 # 图的整体点连通度 k_v = nx.node_connectivity(G) # 两点间的点连通度(如节点 0 和 33) k_v_pair = nx.node_connectivity(G, s=0, t=33) 边连通度 # 图的整体边连通度 k_e = nx.edge_connectivity(G) # 两点间的边连通度 k_e_pair = nx.edge_connectivity(G, s=0, t=33) 找出最小点割集 cut_set = nx.minimum_node_cut(G) 找出最小边割集 cut_edges = nx.minimum_edge_cut(G) 计算最大匹配 matching = nx.algorithms.matching.max_weight_matching(G, maxcardinality=True) # 输出结果(每条匹配边) print("最大匹配结果:") for u, v in matching: print(f"{u} -- {v}") 按照上面格式,根据代码写图模型定义,构建方法,可视化展示,图的性质分析与计算
最新发布
05-28
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值