微服务部署依赖依赖
华为笔试真题 5月27号 非AI方向 200分题型
题目内容
你正在参与一个大规模微服务架构的部署平台开发,系统中包含多个微服务,每个微服务在上线前需要其所有依赖的微服务都已部署并运行。
一个微服务通过字符串来表示,如:"userservice",一个微服务可能依赖多个微服务的部署。
如果依赖关系存在环 (例如:AAA 依赖 BBB,BBB 依赖 CCC,CCC 依赖 AAA),那么就无法部署,因为存在"循环依赖"。
请给出一个函数,判断给定的微服务依赖"无环",并返回部署顺序,如果存在环,返回字符串 CanCanCan notnotnot deploydeploydeploy。
输入描述
- 第一行输入:NNN 为参与部署的微服务的数量(1≤N≤10001 \le N \le 10001≤N≤1000,整数)
- 接下来接着 NNN 行,每行描述一个服务的依赖关系(依赖关系是有向的,即 AAA 依赖 BBB,表示 BBB 要在 AAA 之前部署,否则 AAA 就会依赖失败):
- 格式:serviceName1serviceName1serviceName1 M1M1M1 upstreamService1upstreamService1upstreamService1 upstreamService2upstreamService2upstreamService2 .........
- 格式:serviceName2serviceName2serviceName2 M2M2M2 upstreamService1upstreamService1upstreamService1 upstreamService2upstreamService2upstreamService2 .........
- 格式:serviceName3serviceName3serviceName3 M3M3M3 upstreamService1upstreamService1upstreamService1 upstreamService2upstreamService2upstreamService2 .........
- serviceNameserviceNameserviceName:微服务名称,名称不重复,每个微服务名称字符长度 ≤100\le 100≤100 个字符,字符包含 262626 个小写字母,数字 0−90-90−9。
- MMM:本行为微服务依赖的前驱服务数量,整数,0≤M≤9990 \le M \le 9990≤M≤999,当 M=0M=0M=0 时,说明本微服务不依赖其他微服务。
- upstreamServiceupstreamServiceupstreamService:本行为微服务依赖的前驱服务名称。
注意:
- 被依赖的前驱服务 upstreamServiceupstreamServiceupstreamService 必须存在 serviceNameserviceNameserviceName 中,且同一 upstreamServiceupstreamServiceupstreamService 不重复。
- 本行微服务不能依赖自己,即同一行的被依赖的前驱服务 upstreamServiceupstreamServiceupstreamService 的值不等于本行的 serviceNameserviceNameserviceName。
输出描述
如果本微服务图无环,返回部署顺序,一行输出全部微服务名称,以空格隔开。 规则:
- 一个微服务部署时,它所依赖的前驱服务都已经部署。
- 如果存在多个前置依赖已经部署的微服务,输出时以微服务名称字符从小到大排序(即首字符 asciiasciiascii 码小的微服务排在前面,首字符相同的则比较下一个字符,依次类推)。
- 如果存在环,返回字符串 CanCanCan notnotnot deploydeploydeploy。
样例1
输入
3
aa 1 bb
bb 1 cc
cc 1 aa
输出
Can not deploy
说明
图中依赖存在环,故输出 CanCanCan notnotnot deploydeploydeploy。
样例2
输入
5
payment 2 auth order
order 1 user
auth 1 database
user 0
database 0
输出
database auth user order payment
说明

111.以拓扑排序顺序输出,当前 databasedatabasedatabase 和 useruseruser 依赖的微服务均为 000,按照字符序优先输出 databasedatabasedatabase
222.从拓扑网中删除 databasedatabasedatabase 及其入边,形成新图的 authauthauth 和 useruseruser 依赖的微服务均为 000,按照字符序优先输出 authauthauth
333.重复步骤1、2,最终顺序为 database、auth、user、order、paymentdatabase、auth、user、order、paymentdatabase、auth、user、order、payment
题解
思路
标准的拓扑排序遍历题,借助拓扑排序的概念理解这个题,一个服务依赖另一个服务对应入度 + 1, 只有入度为0的服务才能被部署。代码求解逻辑如下:
- 定义
depend哈希表保存指定服务 依赖于它的服务,使用dependCount记录每个服务的入度 ,定义ans存储部署结果。 - 使用
队列进行每一轮遍历,初始找出其中入度为0的服务并且尚未激活加入队列,队列中的服务为本轮被激活的服务,遍历队列中服务,依靠depend更新依赖于它服务的入度,如果入度变为0,保存用于下一轮遍历。每一轮被激活的服务排序之后加入ans中 - 按照2的逻辑进行处理,直到无服务可被激活。如果
ans.size() != 总服务数说明存在循环依赖,输出Can not deploy. 不存在循环依赖的情况下按顺序输出ans中服务即可。
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
// 依赖关系
unordered_map<string, vector<string>> depend;
// 入度
unordered_map<string, int> dependCount;
// 是否已激活
unordered_map<string, bool> serviceFlag;
vector<string> ans;
while (n--) {
string serviceName;
int m;
cin >> serviceName;
cin >> m;
string upstreamService;
serviceFlag[serviceName] = false;
// 入度初始化为0
dependCount[serviceName] = 0;
for (int i = 0; i < m; i++) {
cin >> upstreamService;
depend[upstreamService].push_back(serviceName);
// 入度+1
dependCount[serviceName]++;
}
}
// 存储入度为0的服务
queue<string> q;
for (auto& service : dependCount) {
if (service.second == 0) {
q.push(service.first);
serviceFlag[service.first] = true;
}
}
queue<string> q_back;
while (!q.empty()) {
vector<string> current_ans;
while (!q.empty()) {
string service = q.front();
q.pop();
current_ans.push_back(service);
// 更新指定节点入度
for (auto& name : depend[service]) {
// 防止重复入队
if (serviceFlag[name]) {
continue;
}
dependCount[name]--;
if (dependCount[name] == 0) {
q_back.push(name);
serviceFlag[name] = true;
}
}
}
swap(q, q_back);
sort(current_ans.begin(), current_ans.end());
for (auto &name : current_ans) {
ans.push_back(name);
}
}
// 说明存在环
if (ans.size() != serviceFlag.size()) {
cout << "Can not deploy";
return 0;
}
// 输出结果
for (int i = 0; i < ans.size(); i++) {
if (i != 0) {
cout << " ";
}
cout << ans[i];
}
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
// 依赖关系
Map<String, List<String>> depend = new HashMap<>();
// 入度
Map<String, Integer> dependCount = new HashMap<>();
// 是否已激活
Map<String, Boolean> serviceFlag = new HashMap<>();
List<String> ans = new ArrayList<>();
while (n-- > 0) {
String serviceName = sc.next();
int m = sc.nextInt();
serviceFlag.put(serviceName, false);
// 入度初始化为0
dependCount.put(serviceName, 0);
for (int i = 0; i < m; i++) {
String upstreamService = sc.next();
depend.computeIfAbsent(upstreamService, k -> new ArrayList<>())
.add(serviceName);
// 入度+1
dependCount.put(serviceName,
dependCount.get(serviceName) + 1);
}
}
// 存储入度为0的服务
Queue<String> q = new LinkedList<>();
for (Map.Entry<String, Integer> entry : dependCount.entrySet()) {
if (entry.getValue() == 0) {
q.offer(entry.getKey());
serviceFlag.put(entry.getKey(), true);
}
}
Queue<String> qBack = new LinkedList<>();
while (!q.isEmpty()) {
List<String> currentAns = new ArrayList<>();
while (!q.isEmpty()) {
String service = q.poll();
currentAns.add(service);
for (String name :
depend.getOrDefault(service, Collections.emptyList())) {
// 防止重复入队
if (serviceFlag.get(name)) {
continue;
}
dependCount.put(name,
dependCount.get(name) - 1);
if (dependCount.get(name) == 0) {
qBack.offer(name);
serviceFlag.put(name, true);
}
}
}
Queue<String> tmp = q;
q = qBack;
qBack = tmp;
Collections.sort(currentAns);
ans.addAll(currentAns);
}
// 说明存在环
if (ans.size() != serviceFlag.size()) {
System.out.print("Can not deploy");
return;
}
// 输出结果
for (int i = 0; i < ans.size(); i++) {
if (i != 0) {
System.out.print(" ");
}
System.out.print(ans.get(i));
}
}
}
python
python
from collections import deque
import sys
tokens = sys.stdin.read().split()
idx = 0
n = int(tokens[idx])
idx += 1
# 依赖关系
depend = {}
# 入度
depend_count = {}
# 是否已激活
service_flag = {}
ans = []
for _ in range(n):
service_name = tokens[idx]
idx += 1
m = int(tokens[idx])
idx += 1
service_flag[service_name] = False
# 入度初始化为0
depend_count[service_name] = 0
for _ in range(m):
upstream_service = tokens[idx]
idx += 1
depend.setdefault(upstream_service, []).append(service_name)
# 入度+1
depend_count[service_name] += 1
# 存储入度为0的服务
q = deque()
for service, cnt in depend_count.items():
if cnt == 0:
q.append(service)
service_flag[service] = True
q_back = deque()
while q:
current_ans = []
while q:
service = q.popleft()
current_ans.append(service)
for name in depend.get(service, []):
# 防止重复入队
if service_flag[name]:
continue
depend_count[name] -= 1
if depend_count[name] == 0:
q_back.append(name)
service_flag[name] = True
q, q_back = q_back, q
current_ans.sort()
ans.extend(current_ans)
# 说明存在环
if len(ans) != len(service_flag):
print("Can not deploy", end="")
else:
print(" ".join(ans), end="")
javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const lines = [];
rl.on("line", line => {
lines.push(line.trim());
});
rl.on("close", () => {
let idx = 0;
const n = Number(lines[idx++]);
// 依赖关系
const depend = new Map();
// 入度
const dependCount = new Map();
// 是否已激活
const serviceFlag = new Map();
const ans = [];
for (let t = 0; t < n; t++) {
const arr = lines[idx++].split(" ");
const serviceName = arr[0];
const m = Number(arr[1]);
serviceFlag.set(serviceName, false);
// 入度初始化为0
dependCount.set(serviceName, 0);
for (let i = 0; i < m; i++) {
const upstreamService = arr[i + 2];
if (!depend.has(upstreamService)) {
depend.set(upstreamService, []);
}
depend.get(upstreamService).push(serviceName);
// 入度+1
dependCount.set(
serviceName,
dependCount.get(serviceName) + 1
);
}
}
// 存储入度为0的服务
let q = [];
for (const [service, cnt] of dependCount) {
if (cnt === 0) {
q.push(service);
serviceFlag.set(service, true);
}
}
let qBack = [];
while (q.length > 0) {
const currentAns = [];
while (q.length > 0) {
const service = q.shift();
currentAns.push(service);
// 更新指定节点入度
for (const name of (depend.get(service) || [])) {
// 防止重复入队
if (serviceFlag.get(name)) {
continue;
}
dependCount.set(
name,
dependCount.get(name) - 1
);
if (dependCount.get(name) === 0) {
qBack.push(name);
serviceFlag.set(name, true);
}
}
}
[q, qBack] = [qBack, q];
currentAns.sort();
ans.push(...currentAns);
}
// 说明存在环
if (ans.length !== serviceFlag.size) {
console.log("Can not deploy");
return;
}
// 输出结果
console.log(ans.join(" "));
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
"sort"
)
func main() {
reader := bufio.NewReader(os.Stdin)
var n int
fmt.Fscan(reader, &n)
// 依赖关系
depend := make(map[string][]string)
// 入度
dependCount := make(map[string]int)
// 是否已激活
serviceFlag := make(map[string]bool)
var ans []string
for i := 0; i < n; i++ {
var serviceName string
var m int
fmt.Fscan(reader, &serviceName, &m)
serviceFlag[serviceName] = false
// 入度初始化为0
dependCount[serviceName] = 0
for j := 0; j < m; j++ {
var upstreamService string
fmt.Fscan(reader, &upstreamService)
depend[upstreamService] =
append(depend[upstreamService], serviceName)
// 入度+1
dependCount[serviceName]++
}
}
// 存储入度为0的服务
var q []string
for service, cnt := range dependCount {
if cnt == 0 {
q = append(q, service)
serviceFlag[service] = true
}
}
var qBack []string
for len(q) > 0 {
var currentAns []string
for len(q) > 0 {
service := q[0]
q = q[1:]
currentAns = append(currentAns, service)
// 更新指定节点入度
for _, name := range depend[service] {
// 防止重复入队
if serviceFlag[name] {
continue
}
dependCount[name]--
if dependCount[name] == 0 {
qBack = append(qBack, name)
serviceFlag[name] = true
}
}
}
q, qBack = qBack, q
sort.Strings(currentAns)
ans = append(ans, currentAns...)
}
// 说明存在环
if len(ans) != len(serviceFlag) {
fmt.Print("Can not deploy")
return
}
// 输出结果
for i := 0; i < len(ans); i++ {
if i > 0 {
fmt.Print(" ")
}
fmt.Print(ans[i])
}
}