题目描述
在分布式项目中,各个服务之间可以相互调用,现在给你一个字符串二维数组call,call[i]=["x","y"],表示微服务"x"调用微服务"y",请你判断这个任务数组中是否存在循环调用,如果存在,true,不存在返回false。
示例
输入:[["A","B"],["B","C"]]输出:false输入:[["A","B"],["B","C"],["C","A"],["C","D"]]输出:true
思路
首先分析题意,在题目给我们的信息中,我们可以知道各个服务之间存在调用关系,并且有明确的指向,比如A调用B,并且A除了调用B还可以调用其他的服务,所以很容易对应到数据结构--有向图。
然后,题目要求我们判断是否存在循环调用,也就是A调用B,B又去调用A的情况。由于我们确定的数据结构是有向图,求解有向图是否存在环状结构,我们能够很轻易的想到拓扑排序。(不了解拓扑排序的,点击这里)
实现
实现需要两个步骤
public boolean isLoopCall(String[][] call) {
//1.表示出图,key:表示的节点C,value表示后继【A,D】
Map<String, List<String>> graph = this.initGraph(call);
//2.开始进行拓扑排序
//a.申请一个map用来记录节点的入度
Map<String, Integer> inDegree = this.buildInDegree(graph);
//3。进行拓扑排序
return this.sort(graph, inDegree);
}
表示出有向图
/**
* 初始化一个图
* 这里使用一个map存储邻接表,表示有向图,key表示节点,value表示key指向的节点
* @param call
* @return
*/
private Map<String, List<String>> initGraph(String[][] call) {
Map<String, List<String>> graph = new HashMap<>();
for (int i = 0 ; i < call.length ; i++) {
//需要注意,出现过的每个节点都需要存到map的key中,所以这里需要两次判断,put
if (!graph.containsKey(call[i][0])) {
graph.put(call[i][0], new ArrayList<>());
}
if (!graph.containsKey(call[i][1])) {
graph.put(call[i][1], new ArrayList<>());
}
List<String> nodes = graph.get(call[i][0]);
nodes.add(call[i][1]);
graph.put(call[i][0], nodes);
}
return graph;
}
进行拓扑排序
这个步骤之前文章有很清楚的讲了,这里就不赘述了。
private boolean sort(Map<String, List<String>> graph, Map<String, Integer> inDegree) {
//存储已经访问的节点
Set<String> visited = new HashSet<>();
//存放当前入度为0的节点,即没有前置服务调用的节点
Deque<String> deque = new LinkedList<>();
//寻找入度为0的节点
for (String key : inDegree.keySet()) {
if (inDegree.get(key) == 0) {
deque.add(key);
}
}
while (!deque.isEmpty()) {
//当前结点出栈
String currentNode = deque.poll();
//之前已经访问,不再访问
if (visited.contains(currentNode)) {
continue;
}
//标记当前结点访问
visited.add(currentNode);
//获取当前结点调用的结点
List<String> nodes = graph.get(currentNode);
for (String node : nodes) {
//将结点入度减一
inDegree.put(node, inDegree.get(node) - 1);
if (inDegree.get(node) == 0) {
//如果入度为0,入栈
deque.add(node);
}
}
}
//当入度为0的节点遍历完,访问节点数等于图的节点数,证明没有环,否则有环
return !(visited.size() == graph.size());
}
/**
* 获取图节点的入度
* @param graph
* @return
*/
private Map<String, Integer> buildInDegree(Map<String, List<String>> graph) {
Map<String, Integer> inDegree = new HashMap<>();
for (String key : graph.keySet()) {
if (!inDegree.containsKey(key)) {
inDegree.put(key, 0);
}
for (String node : graph.get(key)) {
if (!inDegree.containsKey(node)) {
inDegree.put(node, 0);
}
inDegree.put(node, inDegree.get(node) +1 );
}
}
return inDegree;
}
总结
这个题目在我看来就是很中规中矩的算法题目,首先要能提炼出数据结构--有向图,然后知道对应判断环的算法--拓扑排序,基本上没有什么弯弯绕绕,做一遍,理解到了,这样的题基本上没有难度了。