【大厂面试真题--字节】是否存在循环调用

170 阅读2分钟

题目描述

在分布式项目中,各个服务之间可以相互调用,现在给你一个字符串二维数组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;
    }

总结

这个题目在我看来就是很中规中矩的算法题目,首先要能提炼出数据结构--有向图,然后知道对应判断环的算法--拓扑排序,基本上没有什么弯弯绕绕,做一遍,理解到了,这样的题基本上没有难度了。