销售层级人数统计
问题背景
某部门的销售负责人按严格的层级树进行管理。每个级别的负责人有且仅有一位直接上级。层级的顶端是销售总裁,其编号固定为 1。
例如,销售总裁(第1层)直接管理多个国家的销售负责人(第2层),每个国家负责人再管理其下属的大区负责人(第3层),以此类推,形成一个树状的汇报结构。
核心定义
-
层级关系 (Hierarchy):
整个部门的组织架构可以看作一棵树,节点是销售负责人,边代表直接的上下级汇报关系。
-
管辖范围 (Subordinate Tree):
对于任意指定的负责人,其“名下”或“管辖范围”包括其自身以及所有直接或间接向他汇报的下属。这同样构成一棵以该负责人为根的子树。
-
相对层级 (Relative Level):
在分析某个指定负责人的管辖范围时,我们将该负责人自身定义为第 1 层。其直接下属构成第 2 层,下属的下属构成第 3 层,依此类推。
任务要求
给定整个部门的层级关系数据 relations 和一个指定的负责人 designatedHeader,你需要:
- 分析该指定负责人
designatedHeader的管辖范围(子树)。 - 统计其管辖范围内,每一“相对层级”的人数。
- 找出人数最多的那个层级。
最终,返回这个人数最多层级的层级编号和对应的人数。
平局规则 (Tie-Breaking Rule)
如果在指定负责人的管辖范围内,有多个层级的人数相同且均为最大值,则选择层级编号最小(即最高、最靠近指定负责人)的那个层级作为结果。
输入格式
-
relations: 第一个参数,一个二维数组(或列表的列表),表示所有负责人的层级关系。0 < relations.length < 100relations中的每个元素[id, dirHeader]表示负责人id的直接上级是dirHeader。1 <= id <= relations.length + 1(根据样例推断,ID可能不连续,但最大值与长度相关)。- 特例: 销售总裁的编号为
1,其dirHeader固定为-1。
-
designatedHeader: 第二个参数,一个整数,表示待分析的指定负责人id。- 输入保证
designatedHeader是一个有效的负责人ID。
- 输入保证
输出格式
-
一个包含两个数字的数组或列表
[level, count]。level: 指定负责人名下人数最多的相对层级。count: 该层级的人数。
样例说明
主样例
-
输入:
relations:[[1, -1], [3, 1], [2, 1], [4, 1], [5, 1], [6, 3], [7, 3], [8, 3], [12, 6], [13, 6], [21, 13], [15, 8], [16, 8], [9, 2], [10, 2], [19, 11], [20, 11], [22, 17], [11, 5], [14, 7], [17, 9], [18, 10], [23, 21]]designatedHeader:1
-
输出:
[4, 9] -
解释:
-
首先,根据
relations数据构建整个组织的树状结构。 -
我们分析指定负责人
1(总裁) 的管辖范围,也就是整棵树。 -
统计其相对层级的人数:
- 第 1 层: 仅负责人
1自身。人数: 1 - 第 2 层:
1的直接下属[2, 3, 4, 5]。人数: 4 - 第 3 层:
2、3、5的直接下属[6, 7, 8, 9, 10, 11]。人数: 6 - 第 4 层:
6、7、8、9、10、11的直接下属[12, 13, 14, 15, 16, 17, 18, 19, 20]。人数: 9 - 第 5 层:
13、17的直接下属[21, 22]。人数: 2 - 第 6 层:
21的直接下属[23]。人数: 1
- 第 1 层: 仅负责人
-
比较各层人数
[1, 4, 6, 9, 2, 1],最大值为9,它出现在第4层。 -
因此,输出为
[4, 9]。
-
补充说明中的样例
-
指定负责人
9:- 其管辖范围为
9 -> 17 -> 22。 - 第1层:
{9}(1人), 第2层:{17}(1人), 第3层:{22}(1人)。 - 最大人数为
1,出现在第1、2、3层。根据平局规则,取层级编号最小的,即第1层。 - 输出:
[1, 1]
- 其管辖范围为
-
指定负责人
6:- 其管辖范围为
6 -> {12, 13},其中13 -> 21 -> 23。 - 第1层:
{6}(1人), 第2层:{12, 13}(2人), 第3层:{21}(1人), 第4层:{23}(1人)。 - 最大人数为
2,出现在第2层。 - 输出:
[2, 2]
- 其管辖范围为
-
指定负责人
20:- 其管辖范围仅有他自己,他没有下属。
- 第1层:
{20}(1人)。 - 最大人数为
1,出现在第1层。 - 输出:
[1, 1]
import java.util.*;
import java.util.stream.Collectors;
public class Solution {
/**
* 内部静态类,用于表示题目给出的输入格式。
* 在主逻辑中我们会将其转换为更易于处理的 Map 结构。
*/
static class Relation {
int id;
int dirHeader;
public Relation(int id, int dirHeader) {
this.id = id;
this.dirHeader = dirHeader;
}
}
/**
* 统计指定负责人名下人数最多的层级及其人数。
*
* @param relations 二维数组,表示 [员工ID, 直接上级ID] 的关系列表。
* @param designatedHeader 要查询的负责人ID。
* @return 一个包含两个元素的数组 [层级, 人数]。
*/
public int[] getLevelWithMostPeople(int[][] relations, int designatedHeader) {
// --- 1. 构建树形结构(邻接表) ---
// 我们需要一个从经理指向下属的映射,方便从上到下遍历。
// Key: 经理 ID (Integer)
// Value: 该经理的所有直接下属 ID 列表 (List<Integer>)
Map<Integer, List<Integer>> managerToSubordinatesMap = new HashMap<>();
// 遍历输入的原始关系数据
for (int[] relation : relations) {
int subordinateId = relation[0];
int managerId = relation[1];
// 总裁的上级是 -1,我们只关心从经理到下属的映射
if (managerId != -1) {
// computeIfAbsent: 如果 managerId 不在 map 中,则创建一个新的 ArrayList并返回,
// 否则返回现有的 List。然后将 subordinateId 加入该列表。
managerToSubordinatesMap.computeIfAbsent(managerId, k -> new ArrayList<>()).add(subordinateId);
}
}
// --- 2. 使用广度优先搜索 (BFS) 按层级统计人数 ---
// levelCounts 列表将按顺序存储每一层的人数。
// levelCounts.get(0) 是第1层的人数,get(1) 是第2层,以此类推。
List<Integer> levelCounts = new ArrayList<>();
// BFS 使用的队列,初始时只包含要查询的负责人
Queue<Integer> queue = new LinkedList<>();
queue.offer(designatedHeader);
// 只要队列不为空,就说明还有层级需要处理
while (!queue.isEmpty()) {
// 获取当前层的节点数量(即当前层级的人数)
int currentLevelSize = queue.size();
// 将当前层级的人数记录下来
levelCounts.add(currentLevelSize);
// 遍历当前层的所有节点
for (int i = 0; i < currentLevelSize; i++) {
// 从队列中取出一个当前层的负责人
int currentManager = queue.poll();
// 找到他所有的直接下属
List<Integer> subordinates = managerToSubordinatesMap.getOrDefault(currentManager, Collections.emptyList());
// 将所有直接下属加入队列,以备下一轮(下一层级)处理
for (int sub : subordinates) {
queue.offer(sub);
}
}
}
// --- 3. 从层级统计结果中找出最优解 ---
// 如果 levelCounts 为空(例如 designatedHeader 无效或不存在),
// 尽管题目保证输入有效,但作为健壮性代码,我们可以处理这种情况。
// 一个负责人至少包含他自己,所以至少是 [1, 1]。
if (levelCounts.isEmpty()) {
// 根据题目补充说明,即使没有下属,也应返回自身
return new int[]{1, 1};
}
int bestLevel = 1; // 最佳层级编号 (1-based)
int maxPeople = levelCounts.get(0); // 最多人数,初始化为第1层的人数 (即1)
// 从第2层 (索引1) 开始遍历,寻找人数更多的层级
for (int i = 1; i < levelCounts.size(); i++) {
int currentLevelNumber = i + 1; // 层级编号是 1-based
int peopleOnThisLevel = levelCounts.get(i);
// 规则1:如果当前层级的人数更多,则更新为最佳
if (peopleOnThisLevel > maxPeople) {
maxPeople = peopleOnThisLevel;
bestLevel = currentLevelNumber;
}
// 规则2:如果人数相同,选择最高的层级。
// 这个规则通过只在 ">" 时更新来自然满足。如果 "==",我们不更新,
// 从而保留了之前找到的、层级编号更小的(即更高的)那个。
}
// --- 4. 返回结果 ---
return new int[]{bestLevel, maxPeople};
}
}
DFS版本:
import java.util.*;
/**
* 使用深度优先搜索 (DFS) 解决“销售点分布调查”问题的方案类。
*/
public class Solution_DFS {
/**
* 统计指定负责人名下人数最多的层级及其人数。
*
* @param relations 二维数组,表示 [员工ID, 直接上级ID] 的关系列表。
* @param designatedHeader 要查询的负责人ID。
* @return 一个包含两个元素的数组 [层级, 人数]。
*/
public int[] getLevelWithMostPeople(int[][] relations, int designatedHeader) {
// --- 1. 构建树形结构(邻接表)---
// 这个步骤与 BFS 版本完全相同,因为数据预处理与遍历算法无关。
// Key: 经理 ID (Integer)
// Value: 该经理的所有直接下属 ID 列表 (List<Integer>)
Map<Integer, List<Integer>> managerToSubordinatesMap = new HashMap<>();
for (int[] relation : relations) {
int subordinateId = relation[0];
int managerId = relation[1];
if (managerId != -1) {
managerToSubordinatesMap.computeIfAbsent(managerId, k -> new ArrayList<>()).add(subordinateId);
}
}
// --- 2. 使用深度优先搜索 (DFS) 按层级统计人数 ---
// levelCounts 列表将按顺序存储每一层的人数 (索引0代表第1层)。
List<Integer> levelCounts = new ArrayList<>();
// 启动 DFS 遍历,从指定负责人开始,其相对层级为 1 (程序中用索引 0 表示)
dfs(designatedHeader, 0, managerToSubordinatesMap, levelCounts);
// --- 3. 从层级统计结果中找出最优解 ---
// 这部分逻辑与 BFS 版本完全相同,因为它只依赖于最终的 levelCounts 结果。
if (levelCounts.isEmpty()) {
// 一个负责人至少包含他自己,所以至少是 [1, 1]。
return new int[]{1, 1};
}
int bestLevel = 1; // 最佳层级编号 (1-based)
int maxPeople = levelCounts.get(0); // 最多人数
for (int i = 1; i < levelCounts.size(); i++) {
int currentLevelNumber = i + 1;
int peopleOnThisLevel = levelCounts.get(i);
if (peopleOnThisLevel > maxPeople) {
maxPeople = peopleOnThisLevel;
bestLevel = currentLevelNumber;
}
}
// --- 4. 返回结果 ---
return new int[]{bestLevel, maxPeople};
}
/**
* DFS 辅助方法,递归地遍历树并统计每层的人数。
*
* @param currentNode 当前正在访问的节点ID。
* @param currentLevel 当前节点的相对层级 (0-based)。
* @param adjList 邻接表表示的树结构。
* @param levelCounts 用于记录各层级人数的列表。
*/
private void dfs(int currentNode, int currentLevel, Map<Integer, List<Integer>> adjList, List<Integer> levelCounts) {
// --- 确保 levelCounts 列表有足够的空间来记录当前层级 ---
// 如果当前层级超出了列表的现有大小,需要添加新的元素来扩展它。
while (currentLevel >= levelCounts.size()) {
levelCounts.add(0); // 用0初始化新层级的人数
}
// --- 核心操作:为当前节点所在的层级增加计数 ---
levelCounts.set(currentLevel, levelCounts.get(currentLevel) + 1);
// --- 递归地访问所有下属 ---
// 获取当前节点的所有直接下属
List<Integer> subordinates = adjList.getOrDefault(currentNode, Collections.emptyList());
// 对每一个下属,进行深度优先的递归调用
// 下属的层级是当前层级 + 1
for (int sub : subordinates) {
dfs(sub, currentLevel + 1, adjList, levelCounts);
}
}
}