计算图中从一个点到另一个点最少的边数
前言:如果只是想查看计算图中从一个点到另一个点最少的边数的方法,请直接移步到代码部分查看getDistance函数。
问题描述
在这里我们模拟社交关系网络图,图中的一个顶点就是一个人,一条边就代表两个人相识。
图结构设计
一、Person类
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
二、图结构类
在该类内部创建了两个静态内部类Node、Edge,分别代表图里的顶点。
Node声明如下:
Edge声明如下:
Graph类两个关键属性如下:
属性graph的每一个key为图里的一个结点,value为以该结点为起始结点的边的集合。
属性map每一个Person和Node的一一对应关系,通过它可以很方便的根据Person获取它在图中的对应Node。
三、代码实现
public class Graph {
private final Map<Node, Set<Edge>> graph; // 图
private Map<Person, Node> map; // person和node的映射关系
private int min = Integer.MAX_VALUE;
public Graph() {
this.graph = new HashMap<>();
this.map = new HashMap<>();
}
/**
* 向图里添加一个结点
* @param vertex
*/
public void addVertex(Person vertex) {
Node node = new Node(vertex);
graph.put(node, new HashSet<>());
map.put(vertex, node);
}
/**
* 向图里添加一条边
* @param start
* @param end
*/
public void addEdge(Person start, Person end) {
// 获取Person所对应的Node
Node startNode = map.get(start);
Node endNode = map.get(end);
if(startNode == null || endNode == null) {
throw new RuntimeException("输入异常!");
}
// 创建一条边
Edge edge = new Edge(startNode, endNode);
Node node = map.get(start);
if (node == null) {
throw new RuntimeException("输入异常!");
}
// 向图里添加一条边
graph.get(node).add(edge);
}
/**
* 求两个结点间的最短距离
* @param start
* @param end
* @return
*/
public int getDistance(Person start, Person end) {
if(start == end) {
return 0;
}
min = Integer.MAX_VALUE; // 将min初始化为Integer.MAX_VALUE
Node startNode = map.get(start); // 获取start对应的图结点
startNode.setVisit(true); // 设置startNode已经访问过
getDistance(start, end, new Integer(1));
return min == Integer.MAX_VALUE ? -1 : min-1; // min此时为结点数,需要减一
}
// curr代表当前的遍历路径已经走过几个结点
private void getDistance(Person start, Person end, Integer curr) {
// 当start == end时说明找到了目标结点,需要去更新min
if(start == end) {
min = Math.min(min, curr);
return;
}
Node startNode = map.get(start);
Set<Edge> startEdges = graph.get(startNode);
// 遍历与startNode 存在边的全部结点
for(Edge edge: startEdges) {
Node endNode = edge.getEnd();
if(!endNode.isVisit) {
curr++;
endNode.setVisit(true);
getDistance(endNode.getPerson(), end, curr);
// 回溯的核心:恢复状态
endNode.setVisit(false);
curr--;
}
}
}
static class Edge {
private Node start;
private Node end;
public Edge(Node start, Node end) {
this.start = start;
this.end = end;
}
public Node getStart() {
return start;
}
public void setStart(Node start) {
this.start = start;
}
public Node getEnd() {
return end;
}
public void setEnd(Node end) {
this.end = end;
}
@Override
public String toString() {
return "Edge{" +
"start=" + start +
", end=" + end +
'}';
}
}
static class Node {
private final Person person;
private boolean isVisit = false;
public Node(Person person) {
this.person = person;
}
public Person getPerson() {
return person;
}
public boolean isVisit() {
return isVisit;
}
public void setVisit(boolean visit) {
isVisit = visit;
}
@Override
public String toString() {
return "Node{" +
"person=" + person +
", isVisit=" + isVisit +
'}';
}
}
}
四、getDistance方法思想
使用回溯法来尝试每一种可能的结果,找出所有可能的结果中最小的路径长度。
如果你想了解更多我对编程和人生的思考,请关注公众号:青云学斋