华为OD机试 2024D卷题库疯狂收录中,刷题点这里
专栏导读
本专栏收录于《华为OD机试(JAVA)真题(D卷+C卷+A卷+B卷)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。
一、题目描述
某电商APP希望基于用户的商品浏览历史记录,基于商品的热度值,给用户推荐一些关联的商品。
用户浏览过的商品和类似商品会关联在一起,每个商品最多有3个商品进行关联。每个商品都有一个热度值,是系统基于当前商品的购买量和搜索热度等计算出来的值。
现给出用户浏览的商品关联信息,找出最合适的推荐商品序列,要其推荐序列中商品热度值总和最高,并且返回该热度值。
二、输入描述
第一行:商品关联数组Q的长度,长度取值是[1,10000];
第二行:商品关联数组,其中除第一个元素单独一组外,其他每连续三个元素一组,为前面商品的关联商品;
数组里面值表示商品的热度值,取值范围[-2000, 2000],其中-1表示这个商品没有和其他商品进行关联。
注意:商品关联关系之间不存在环。
输入中的-1表示这个商品没有和其他商品进行关联
三、输出描述
现在基于用户浏览的商品关联信息,找出最合适的推荐商品序列,推荐序列中商品热度值总和最高,并且返回该热度值。
注意:每个商品只能在推荐序列中出现一次。
四、测试用例
测试用例1:
1、输入
19
20 12 30 15 -1 -1 -1 -1 -1 -1 15 5 25 -1 -1 -1 16 -1 22
2、输出
92
3、说明
我们要找出从根节点开始路径和最大的路径。在这个例子中,我们需要考虑所有可能的路径和选择其中最大的一个。
从根节点20开始,有以下几条可能的路径:
20 -> 12:20 + 12 = 32
20 -> 30:20 + 30 = 50
20 -> 15 -> 15:20 + 15 + 15 = 50
20 -> 15 -> 5 -> 16:20 + 15 + 5 + 16 = 56
20 -> 15 -> 5 -> 22:20 + 15 + 5 + 22 = 62
20 -> 15 -> 5:20 + 15 + 5 = 40
20 -> 15 -> 25:20 + 15 + 25 = 60
计算每条路径的和:
20 -> 12:20 + 12 = 32
20 -> 30:20 + 30 = 50
20 -> 15 -> 15:20 + 15 + 15 = 50
20 -> 15 -> 5 -> 16:20 + 15 + 5 + 16 = 56
20 -> 15 -> 5 -> 22:20 + 15 + 5 + 22 = 62
20 -> 15 -> 5:20 + 15 + 5 = 40
20 -> 15 -> 25:20 + 15 + 25 = 60
计算路径和最大的路径是:20 -> 15 -> 5 -> 22,总和为92。
五、解题思路
1、数据结构与算法
在constructTree方法中使用BFS来构建树的结构。BFS通过层次遍历节点,从根节点开始依次将每个节点的子节点添加到队列中,直至遍历完所有节点。
在calculateMaxPathSum方法中使用DFS来计算树中的最大路径和。DFS从根节点开始,递归遍历每个节点及其子节点,计算每条路径的和,并更新全局最大路径和。
通过使用数组、列表、队列等数据结构,以及广度优先搜索(BFS)和深度优先搜索(DFS)算法,该代码实现了从输入构建三叉树结构,并计算树中最大路径和的功能。DFS递归遍历树中的每一个节点,计算每条路径的和,并更新全局最大路径和。BFS用于按层次构建树的结构,以便后续的DFS遍历。
2、具体步骤:
给定一个表示商品浏览历史的关联数组,每个商品最多有三个关联商品,每个商品有一个热度值。目的是找到一个推荐商品序列,使得该序列中商品热度值总和最高,并返回这个最大热度值。
输入的第一行是关联数组的长度。
输入的第二行是关联数组,每个商品最多有三个关联商品,关联商品的热度值可能为-1(表示没有关联商品)。
使用广度优先搜索(BFS)构建三叉树,将输入的关联数组转换为树结构。
父节点索引为 i,其子节点索引分别为 3i+1,3i+2,3*i+3。
使用深度优先搜索(DFS)从根节点开始遍历树,计算每条路径的热度值总和。
在遍历过程中更新全局最大路径和。
最终输出全局最大路径和。
六、Java算法源码
public class OdTest {
static ArrayList<Integer> treeNodes; // 存储树的节点值
static int maxPathSum = Integer.MIN_VALUE; // 用于记录全局最大路径和
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取节点数量
int nodeCount = Integer.parseInt(scanner.nextLine());
// 读取节点值数组
int[] nodeValues = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
// 构建三叉树结构
constructTree(nodeCount, nodeValues);
// 计算树中的最大路径和
calculateMaxPathSum(0);
// 输出最大路径和
System.out.println(maxPathSum);
}
/**
* 构建三叉树结构
* 父节点索引i,左子节点索引3*i+1,中子节点索引3*i+2,右子节点索引3*i+3
*/
public static void constructTree(int nodeCount, int[] nodeValues) {
treeNodes = new ArrayList<>(); // 初始化树节点列表
LinkedList<Integer> nodeQueue = new LinkedList<>(); // 用于构建树的队列
nodeQueue.add(nodeValues[0]); // 将根节点添加到队列
int currentIndex = 1; // 当前处理的节点索引
while (!nodeQueue.isEmpty() && currentIndex < nodeCount) {
int currentNode = nodeQueue.removeFirst(); // 取出当前节点
treeNodes.add(currentNode); // 添加到树节点列表
if (currentNode != -1) { // 如果当前节点不是-1
// 添加当前节点的三个子节点
for (int i = 0; i < 3; i++) {
nodeQueue.add(nodeValues[currentIndex++]);
}
} else {
// 如果当前节点为-1,添加三个-1作为占位符
nodeQueue.add(-1);
nodeQueue.add(-1);
nodeQueue.add(-1);
}
}
// 将剩余的节点添加到树节点列表中
treeNodes.addAll(nodeQueue);
}
/**
* 深度优先搜索计算最大路径和
*
* @param nodeIndex 当前处理的节点索引
* @return 当前节点作为子树可以贡献的最大路径和
*/
public static int calculateMaxPathSum(int nodeIndex) {
// 如果节点不存在(值为-1),返回0
if (treeNodes.get(nodeIndex) == -1) return 0;
// 计算左、中、右子树的最大路径和
int leftSum = getChildPathSum(nodeIndex, 1);
int midSum = getChildPathSum(nodeIndex, 2);
int rightSum = getChildPathSum(nodeIndex, 3);
// 计算当前节点所有子树的路径和
int totalSum = leftSum + midSum + rightSum;
// 找到最小和最大的子树路径和
int minChildSum = Math.min(Math.min(leftSum, midSum), rightSum);
int maxChildSum = Math.max(Math.max(leftSum, midSum), rightSum);
// 更新全局最大路径和
// 当前节点为根的最大路径和 = 当前节点值 + 最大的两个子树和
maxPathSum = Math.max(maxPathSum, treeNodes.get(nodeIndex) + totalSum - minChildSum);
// 返回当前节点作为子树可以贡献的最大路径和(当前节点值 + 最大的子树和)
return treeNodes.get(nodeIndex) + maxChildSum;
}
/**
* 获取子节点的路径和
*
* @param parentIndex 父节点索引
* @param childOffset 子节点在父节点中的偏移量
* @return 子节点的最大路径和
*/
private static int getChildPathSum(int parentIndex, int childOffset) {
int childIndex = 3 * parentIndex + childOffset; // 计算子节点索引
if (childIndex < treeNodes.size()) { // 如果子节点索引在范围内
return Math.max(0, calculateMaxPathSum(childIndex)); // 递归计算子节点路径和,并返回非负值
}
return 0; // 如果子节点索引超出范围,返回0
}
}
七、效果展示
1、输入
10
20 -50 -1 15 30 -1 -1 10 -1 8
2、输出
45
3、说明
我们要找出从根节点开始路径和最大的路径。在这个例子中,我们需要考虑所有可能的路径和选择其中最大的一个。
- 从根节点20开始,有以下几条可能的路径:
- 20 -> -50 -> 30
- 20 -> -50
- 20 -> 15 -> 10
- 20 -> 15 -> 8
- 20 -> 15
- 计算每条路径的和:
- 20 -> -50 -> 30:20 - 50 + 30 = 0
- 20 -> -50:20 - 50 = -30
- 20 -> 15 -> 10:20 + 15 + 10 = 45
- 20 -> 15 -> 8:20 + 15 + 8 = 43
- 20 -> 15:20 + 15 = 35
- 其中,路径和最大的路径是20 -> 15 -> 10,总和为45。
🏆下一篇:华为OD机试 - 简易内存池 - 逻辑分析(Java 2024 D卷 200分)
🏆本文收录于,华为OD机试(JAVA)真题(D卷+C卷+A卷+B卷)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。