机试题——微服务群组

[复制链接]
发表于 2025-10-19 12:10:10 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
标题形貌

小明为了调研微服务调用情况,对n个微服务调用数据举行了收罗分析。微服务利用数字0到n-1举行编号,给定一个数组edges,此中edges体现存在一条从微服务i到微服务edges的接口调用。
为了更好地维护,小明将形成环的多个微服务称为微服务群组。一个微服务群组的全部微服务数量为L,可以大概访问到该微服务群组的微服务数量为V,这个微服务群组的内聚值为H = L - V。
已知提供的数据中有1个或多个微服务群组,请按照内聚值H的结果从大到小的次序对全部微服务群组排序(H相称时,取环中最大的数举行比力),输出排在第一的微服务群组,输出时每个微服务群组输出的起始编号为环中最小的数。
输入形貌


  • 第一行:一个整数n,体现有n个微服务(2 ≤ n ≤ 10^5)。
  • 第二行:数组edges,此中edges体现存在一条从微服务i到微服务edges的接口调用(0 ≤ edges ≤ n-1,edges ≠ i)。
输出形貌

输出排在第一的微服务群组的编号数组,按照环的访问次序输出,起始编号为环中最小的数,数字以空格分隔。
用例输入
  1. 4
  2. 3 3 0 2
复制代码
  1. 0 3 2
复制代码
0,3,2构成了微服务群组(环)a,其L值为3,只有编号为1的一个微服务可以访问到a,因此V值为1,内聚值H = L - V = 2。
  1. 12
  2. 2 6 10 1 6 0 3 0 5 4 5 8
复制代码
  1. 0 2 10 5
复制代码
1,6,3构成了微服务群组(环)a1,L1值为3,编号为4、9的2个微服务可以访问到a1,因此V1值为2,H1 = L1 - V1 = 1。
0,2,10,5构成了微服务群组(环)a2,L2值为4,编号为7、8、11的3个微服务可以访问到a2,因此V2值为3,H2 = L2 - V2 = 1。
内聚值H1 = H2,但a2中最大编号为10,大于a1中的最大编号6,因此输出a2。
解题思绪(65%)


  • 标题建模:

    • 该标题可以看作是一个图论标题,目标是找到全部微服务群组(环)并盘算其内聚值H。
    • 利用深度优先搜刮(DFS)找到全部环,并记载每个环的访问次序。

  • 数据结构:

    • 利用unordered_map<int, int>存储图的边关系。
    • 利用vector<node>存储全部微服务群组,此中node结构体包罗:

      • w:环中的微服务编号。
      • v:可以访问该环的微服务数量。
      • maxid:环中编号最大的微服务。
      • size:环的巨细。


  • DFS遍历:

    • 利用DFS找到全部环,并记载每个环的访问次序。
    • 利用状态数组states记载每个节点的访问状态:

      • 0:未访问。
      • 1:已完全访问。
      • 2:正在访问(用于检测环)。


  • 盘算内聚值:

    • 对于每个环,盘算其内聚值H = L - V。
    • 盘算每个环的V值,即可以访问该环的微服务数量。

  • 排序:

    • 按内聚值H从大到小排序。
    • 如果H值雷同,按环中最大编号从大到小排序。

  • 输出结果:

    • 输出排在第一的微服务群组,起始编号为环中最小的数。

100%须要利用Tarjan 算法
代码(65%)
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<map>
  6. #include<algorithm>
  7. #include<string>
  8. #include<vector>
  9. #include<unordered_map>
  10. #include<unordered_set>
  11. #include<queue>
  12. #include<set>
  13. #include<list>
  14. #include<sstream>
  15. #include<bitset>
  16. #include<stack>
  17. #include<climits>
  18. #include<iomanip>
  19. #include<cstdint>
  20. using namespace std;
  21. // 用于存储图的边关系
  22. unordered_map<int, int> mp;
  23. // 定义微服务群组的结构体
  24. struct node {
  25.     vector<int> w; // 微服务的环
  26.     int v; // 其他微服务可访问数量
  27.     int maxid; // 环中编号最大的节点
  28.     int size; // 环的大小
  29. };
  30. // 临时遍历的栈,用于记录DFS路径
  31. vector<int> temp;
  32. // 存储所有微服务群组
  33. vector<node> res;
  34. // 状态表,记录每个节点的访问状态
  35. unordered_map<int, int> states;
  36. // 状态定义:
  37. // 0 - 未遍历
  38. // 1 - 已完全遍历
  39. // 2 - 正在遍历(用于检测环)
  40. void dfs(int cur, unordered_map<int, int>& states) {
  41.     if (states[cur] == 1) {
  42.         // 如果当前节点已完全遍历,直接返回
  43.         return;
  44.     }
  45.     if (states[cur] == 2) {
  46.         // 如果当前节点正在遍历中,说明遇到了环
  47.         node tres;
  48.         auto index = find(temp.begin(), temp.end(), cur); // 找到环的起始位置
  49.         tres.w = {};
  50.         while (index != temp.end()) {
  51.             tres.w.push_back(*index); // 将环中的节点加入到当前群组
  52.             index++;
  53.         }
  54.         tres.maxid = 0; // 初始化环中最大编号
  55.         tres.v = 0; // 初始化可访问数量
  56.         res.push_back(tres); // 将当前群组加入到结果中
  57.         return;
  58.     }
  59.     temp.push_back(cur); // 将当前节点加入到临时路径
  60.     states[cur] = 2; // 标记当前节点为正在遍历
  61.     dfs(mp[cur], states); // 递归访问下一个节点
  62.     temp.pop_back(); // 回溯,移除当前节点
  63.     states[cur] = 1; // 标记当前节点为已完全遍历
  64. }
  65. // 从最小的节点开始遍历环,确保环的输出顺序正确
  66. void find_w(int root, vector<int>& temp) {
  67.     if (temp.size() && temp[0] == root) return; // 如果已经遍历到根节点,结束
  68.     temp.push_back(root); // 将当前节点加入到路径
  69.     find_w(mp[root], temp); // 递归访问下一个节点
  70. }
  71. // 比较函数,用于排序
  72. bool cmp(node& a, node& b) {
  73.     if ((a.size - a.v) != (b.size - b.v)) {
  74.         // 如果内聚值不同,按内聚值从大到小排序
  75.         return (a.size - a.v) > (b.size - b.v);
  76.     }
  77.     // 如果内聚值相同,按环中最大编号从大到小排序
  78.     return a.maxid > b.maxid;
  79. }
  80. int main() {
  81.     ios::sync_with_stdio(false);
  82.     cin.tie(nullptr);
  83.     int n;
  84.     cin >> n; // 输入微服务的数量
  85.     for (int i = 0; i < n; i++) {
  86.         int x;
  87.         cin >> x; // 输入每个微服务的调用关系
  88.         mp[i] = x; // 构建图
  89.     }
  90.     for (int i = 0; i < n; i++) {
  91.         // 从每个节点开始DFS,确保找到所有环
  92.         dfs(i, states);
  93.     }
  94.     // 初始化每个节点的群组标记
  95.     unordered_map<int, int> is;
  96.     for (int i = 0; i < n; i++) is[i] = -1;
  97.     for (int i = 0; i < res.size(); i++) {
  98.         int minnid = INT_MAX;
  99.         for (int j = 0; j < res[i].w.size(); j++) {
  100.             res[i].maxid = max(res[i].w[j], res[i].maxid); // 更新环中最大编号
  101.             minnid = min(res[i].w[j], minnid); // 更新环中最小编号
  102.             is[res[i].w[j]] = i; // 标记当前节点属于哪个群组
  103.         }
  104.         // 从环中最小的节点开始重新遍历环,确保输出顺序正确
  105.         res[i].w.clear();
  106.         find_w(minnid, res[i].w);
  107.         res[i].size = res[i].w.size(); // 更新环的大小
  108.     }
  109.     // 计算每个群组的可访问数量
  110.     for (int i = 0; i < n; i++) {
  111.         if (is[i] == -1) {
  112.             // 如果当前节点不属于任何群组,检查它可以访问的群组
  113.             int ne = mp[i];
  114.             while (is[ne] == -1) {
  115.                 ne = mp[ne]; // 沿着调用链找到属于某个群组的节点
  116.             }
  117.         }
  118.     }
  119.     // 按内聚值和环中最大编号排序
  120.     sort(res.begin(), res.end(), cmp);
  121.     // 输出排在第一的微服务群组
  122.     for (int i = 0; i < res[0].w.size(); i++) {
  123.         cout << res[0].w[i] << " ";
  124.     }
  125.     return 0;
  126. }
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表