2025 A卷 200分 题型
本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析;
并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式!
华为OD机试真题《阿里巴巴找黄金宝箱 IV》:
目录
- 题目名称:阿里巴巴找黄金宝箱 IV
- 题目描述
- Java
- 题目分析
- 解决思路
- Java 代码实现
- 代码解析
- 示例测试
- 示例1:
- 示例2:
- 综合分析
- python
- 题目分析
- 解决思路
- Python 代码实现
- 代码解析
- 示例测试
- 示例1:
- 示例2:
- 综合分析
- JavaScript
- 题目分析
- 解决思路
- JavaScript 代码实现
- 代码解析
- 示例测试
- 示例1:
- 示例2:
- 综合分析
- C++
- 题目分析
- 解决思路
- C++ 代码实现
- 代码解析
- 示例测试
- 示例1:
- 示例2:
- 综合分析
- C语言
- 题目分析
- 解决思路
- C 代码实现
- 代码解析
- 1. 栈结构定义
- 2. 栈操作函数
- 3. 核心算法
- 4. 输入处理
- 示例测试
- 示例1:
- 示例2:
- 综合分析
- GO
- 题目分析
- 解决思路
- Go 代码实现
- 代码解析
- 示例测试
- 示例1:
- 示例2:
- 综合分析
- 更多内容:
题目名称:阿里巴巴找黄金宝箱 IV
知识点: 字符串、栈操作(单调栈算法)、逻辑处理
时间限制: 1秒
空间限制: 256MB
限定语言: 不限
题目描述
一贫如洗的樵夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地。藏宝地有编号从0-N的箱子排列成环(编号最大的箱子的下一个是编号为0的箱子),每个箱子贴有一个数字。请输出每个箱子之后的第一个比它大的数字,若不存在则输出-1。
输入描述
输入一个数字子串,数字之间用逗号分隔,例如:1,2,3,1
。
- 1 ≤ 子串中数字个数 ≤ 10000
- -100000 ≤ 每个数字值 ≤ 100000
输出描述
结果以逗号分隔,例如:2,3,6,-1,6
。
测试用例
- 输入:
1,2,3,4,5
→ 输出:2,3,4,5,-1
- 输入:
2,5,2
→ 输出:5,-1,5
(需循环搜索)
Java
题目分析
我们需要在给定的环形数组中,为每个元素找到其后第一个更大的元素。环形数组意味着最后一个元素的下一个元素是第一个元素。例如,输入 2,5,2
中,第三个元素的下一个元素是第一个元素,但第一个更大的元素是第二个元素 5。
解决思路
单调栈算法:
- 遍历两次数组:模拟环形结构,每个元素会被处理两次。
- 维护单调递减栈:栈中保存元素索引,确保栈顶元素对应的值是递减的。
- 处理每个元素:当当前元素比栈顶元素大时,栈顶元素的下一个更大元素即为当前元素,记录结果并出栈。
Java 代码实现
import java.util.*;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);String input = scanner.nextLine();String[] parts = input.split(",");int[] nums = new int[parts.length];for (int i = 0; i < parts.length; i++) {nums[i] = Integer.parseInt(parts[i]);}int[] result = nextGreaterElements(nums);System.out.println(arrayToString(result));}private static int[] nextGreaterElements(int[] nums) {int n = nums.length;int[] res = new int[n];Arrays.fill(res, -1);Deque<Integer> stack = new ArrayDeque<>();// 遍历两次数组,模拟环形结构for (int i = 0; i < 2 * n; i++) {int num = nums[i % n];// 维护单调递减栈,处理栈顶元素while (!stack.isEmpty() && num > nums[stack.peek()]) {int index = stack.pop();res[index] = num;}// 只在第一次遍历时压入索引if (i < n) {stack.push(i);}}return res;}private static String arrayToString(int[] arr) {StringBuilder sb = new StringBuilder();for (int i = 0; i < arr.length; i++) {if (i > 0) {sb.append(",");}sb.append(arr[i]);}return sb.toString();}
}
代码解析
-
输入处理:
String input = scanner.nextLine(); String[] parts = input.split(","); int[] nums = new int[parts.length]; for (int i = 0; i < parts.length; i++) {nums[i] = Integer.parseInt(parts[i]); }
- 读取输入字符串并分割为整数数组。
-
核心算法
nextGreaterElements
:int[] res = new int[n]; Arrays.fill(res, -1); Deque<Integer> stack = new ArrayDeque<>();
- 初始化结果数组
res
,默认值为 -1。 - 使用双端队列
Deque
作为栈,保存索引。
- 初始化结果数组
-
遍历两次数组:
for (int i = 0; i < 2 * n; i++) {int num = nums[i % n];while (!stack.isEmpty() && num > nums[stack.peek()]) {int index = stack.pop();res[index] = num;}if (i < n) {stack.push(i);} }
- 遍历逻辑:遍历
2n
次,i % n
模拟环形访问。 - 维护栈:栈中保存未找到更大元素的索引,确保栈中元素对应的值是递减的。
- 处理栈顶元素:当当前元素
num
大于栈顶元素对应的值时,栈顶元素的下一个更大元素即为num
,记录结果并出栈。 - 压入索引:仅在第一次遍历时压入索引,避免重复处理。
- 遍历逻辑:遍历
-
结果转换:
private static String arrayToString(int[] arr) {// 将数组转换为逗号分隔的字符串 }
- 将结果数组转换为题目要求的输出格式。
示例测试
示例1:
输入:
1,2,3,4,5
输出:
2,3,4,5,-1
解析:
- 每个元素的下一个更大元素依次为后一个元素,最后一个元素无更大值。
示例2:
输入:
2,5,2
输出:
5,-1,5
解析:
- 第一个 2 的下一个更大元素是 5。
- 5 之后没有更大元素。
- 最后一个 2 的下一个更大元素是循环后的 5。
综合分析
- 时间复杂度 O(n):每个元素最多入栈和出栈两次,总操作次数为
4n
,仍为线性复杂度。 - 空间复杂度 O(n):栈和结果数组的空间均为
O(n)
。 - 正确性保证:
- 环形处理:通过遍历两次数组,确保每个元素能访问到环形后的元素。
- 单调栈特性:确保每个元素找到第一个更大元素。
- 适用场景:高效处理大规模数据(
n ≤ 10000
)。
python
题目分析
给定一个环形数组,每个元素需要找到其后的第一个更大元素。若不存在更大元素则输出 -1
。例如:
- 输入
2,5,2
的输出为5,-1,5
,最后一个元素循环到开头找到更大值。 - 输入
1,2,3,4,5
的输出为2,3,4,5,-1
。
解决思路
单调栈算法:
- 遍历两次数组:模拟环形结构,每个元素处理两次。
- 维护单调递减栈:栈中保存元素索引,确保栈顶元素对应的值是递减的。
- 处理当前元素:若当前元素比栈顶元素大,则栈顶元素的下一个更大元素即为当前元素。
Python 代码实现
def next_greater_elements(nums):n = len(nums)res = [-1] * n # 初始化结果数组,默认-1stack = [] # 单调递减栈,保存索引# 遍历两倍数组长度,模拟环形结构for i in range(2 * n):current_num = nums[i % n] # 当前元素(环形取模)# 维护单调栈:当前元素比栈顶元素大时,更新结果while stack and current_num > nums[stack[-1]]:index = stack.pop() # 弹出栈顶索引res[index] = current_num # 记录下一个更大元素# 只在第一次遍历时压入索引,避免重复处理if i < n:stack.append(i % n)return res# 输入处理与结果输出
input_str = input().strip()
nums = list(map(int, input_str.split(',')))
result = next_greater_elements(nums)
print(','.join(map(str, result)))
代码解析
-
初始化结果数组:
n = len(nums) res = [-1] * n
- 创建长度与输入相同的数组,默认值为
-1
。
- 创建长度与输入相同的数组,默认值为
-
遍历两次数组:
for i in range(2 * n):current_num = nums[i % n]
i % n
将索引映射到原数组范围,模拟环形结构。
-
维护单调递减栈:
while stack and current_num > nums[stack[-1]]:index = stack.pop()res[index] = current_num
- 当当前元素比栈顶元素大时,弹出栈顶并记录结果。
-
压入索引:
if i < n:stack.append(i % n)
- 仅在第一次遍历时压入索引,避免重复处理。
示例测试
示例1:
输入:
1,2,3,4,5
输出:
2,3,4,5,-1
解析:
- 每个元素的下一个更大元素是后一个元素,最后一个元素无更大值。
示例2:
输入:
2,5,2
输出:
5,-1,5
解析:
- 第一个
2
的下一个更大元素是5
。 5
之后没有更大元素。- 最后一个
2
循环到开头找到5
。
综合分析
- 时间复杂度 O(n):每个元素最多入栈和出栈两次,总操作次数为
4n
,时间复杂度为线性。 - 空间复杂度 O(n):栈和结果数组的空间均为
O(n)
。 - 正确性保障:
- 环形处理:通过遍历两次数组,确保每个元素能访问到环形后的元素。
- 单调栈特性:栈中保存未找到更大元素的索引,保证找到的是第一个更大值。
- 适用场景:高效处理大规模数据(
n ≤ 10000
)。
JavaScript
题目分析
给定一个环形数组,每个元素需要找到其后第一个更大的元素。若不存在,则返回 -1
。例如:
- 输入
2,5,2
的输出为5,-1,5
,最后一个元素在循环中找到更大的5
。 - 输入
1,2,3,4,5
的输出为2,3,4,5,-1
。
解决思路
单调栈算法:
- 遍历两次数组:模拟环形结构,确保每个元素处理两次。
- 维护单调递减栈:栈中保存元素索引,栈顶元素对应的值最小。
- 处理当前元素:若当前元素比栈顶元素大,则栈顶元素的下一个更大元素为当前元素。
JavaScript 代码实现
function nextGreaterElements(nums) {const n = nums.length;const result = new Array(n).fill(-1); // 初始化结果数组为-1const stack = []; // 单调栈,保存索引// 遍历两倍数组长度以模拟环形结构for (let i = 0; i < 2 * n; i++) {const currentNum = nums[i % n]; // 当前元素(环形取模)// 维护栈:当前元素比栈顶元素大时,更新结果while (stack.length > 0 && currentNum > nums[stack[stack.length - 1]]) {const index = stack.pop();result[index] = currentNum; // 栈顶元素的下一个更大元素是当前元素}// 仅在第一次遍历时压入索引,避免重复处理if (i < n) {stack.push(i % n);}}return result;
}// 输入处理与结果输出
const input = '2,5,2'; // 示例输入
const nums = input.split(',').map(Number);
const result = nextGreaterElements(nums);
console.log(result.join(',')); // 输出:5,-1,5
代码解析
-
初始化结果数组:
const result = new Array(n).fill(-1);
- 创建长度与输入数组相同的数组,默认值为
-1
。
- 创建长度与输入数组相同的数组,默认值为
-
遍历两次数组:
for (let i = 0; i < 2 * n; i++) {const currentNum = nums[i % n];
i % n
将索引映射到原数组范围,模拟环形结构。
-
维护单调递减栈:
while (stack.length > 0 && currentNum > nums[stack[stack.length - 1]]) {const index = stack.pop();result[index] = currentNum; }
- 当当前元素比栈顶元素大时,弹出栈顶并记录结果。
-
压入索引:
if (i < n) {stack.push(i % n); }
- 仅在第一次遍历时压入索引,避免重复处理。
示例测试
示例1:
输入:
const input = '1,2,3,4,5';
输出:
2,3,4,5,-1
解析:
- 每个元素的下一个更大元素是后一个元素,最后一个元素无更大值。
示例2:
输入:
const input = '2,5,2';
输出:
5,-1,5
解析:
- 第一个
2
的下一个更大元素是5
。 5
之后没有更大元素。- 最后一个
2
循环到开头找到5
。
综合分析
- 时间复杂度 O(n):每个元素最多入栈和出栈两次,总操作次数为
4n
,时间复杂度为线性。 - 空间复杂度 O(n):栈和结果数组的空间均为
O(n)
。 - 正确性保障:
- 环形处理:通过遍历两次数组,确保每个元素能访问到环形后的元素。
- 单调栈特性:栈中保存未找到更大元素的索引,保证找到的是第一个更大值。
- 适用场景:高效处理大规模数据(
n ≤ 10000
)。
C++
题目分析
给定一个环形数组,每个元素需要找到其后的第一个更大元素。若不存在,则返回 -1
。例如:
- 输入
1,2,3,4,5
的输出为2,3,4,5,-1
。 - 输入
2,5,2
的输出为5,-1,5
。
解决思路
单调栈算法:
- 遍历两次数组:通过取模操作模拟环形数组,确保每个元素的后继元素被检查两次。
- 维护单调递减栈:栈中保存元素索引,确保栈顶元素对应的值最小。
- 更新结果数组:当当前元素大于栈顶元素时,栈顶元素的下一个更大元素即为当前元素。
C++ 代码实现
#include <iostream>
#include <vector>
#include <stack>
#include <sstream>
#include <string>using namespace std;vector<int> nextGreaterElements(vector<int>& nums) {int n = nums.size();vector<int> res(n, -1); // 初始化结果数组为-1stack<int> st; // 单调栈,保存未找到更大元素的索引// 遍历两倍数组长度以模拟环形结构for (int i = 0; i < 2 * n; ++i) {int current = nums[i % n]; // 当前元素(环形取模)// 维护单调栈:当前元素比栈顶元素大时,更新结果while (!st.empty() && current > nums[st.top()]) {int idx = st.top();st.pop();res[idx] = current; // 记录栈顶索引对应的下一个更大元素}// 仅在第一次遍历时压入索引,避免重复处理if (i < n) {st.push(i % n);}}return res;
}int main() {string input;getline(cin, input); // 读取输入字符串// 解析输入为整数数组vector<int> nums;stringstream ss(input);string token;while (getline(ss, token, ',')) {nums.push_back(stoi(token));}// 计算下一个更大元素数组vector<int> res = nextGreaterElements(nums);// 输出结果for (int i = 0; i < res.size(); ++i) {if (i != 0) {cout << ",";}cout << res[i];}cout << endl;return 0;
}
代码解析
-
结果数组初始化:
vector<int> res(n, -1);
- 创建一个长度为
n
的数组,所有元素初始化为-1
。
- 创建一个长度为
-
遍历两倍数组长度:
for (int i = 0; i < 2 * n; ++i) {int current = nums[i % n];
- 通过
i % n
模拟环形数组,遍历两次原数组。
- 通过
-
维护单调栈:
while (!st.empty() && current > nums[st.top()]) {int idx = st.top();st.pop();res[idx] = current; }
- 当前元素比栈顶元素大时,弹出栈顶并更新结果。
-
压入索引:
if (i < n) {st.push(i % n); }
- 仅在第一次遍历时压入索引,确保每个元素只处理一次。
示例测试
示例1:
输入:
1,2,3,4,5
输出:
2,3,4,5,-1
解析:
- 每个元素的后一个元素更大,最后一个元素无更大值。
示例2:
输入:
2,5,2
输出:
5,-1,5
解析:
- 第二个
2
循环到数组开头找到5
作为下一个更大元素。
综合分析
- 时间复杂度 O(n):每个元素最多入栈和出栈两次,总操作次数为
4n
,时间复杂度为线性。 - 空间复杂度 O(n):栈和结果数组的空间均为
O(n)
。 - 正确性保障:
- 环形处理:通过遍历两次数组,确保每个元素的后继被完整检查。
- 单调栈特性:确保每个元素找到第一个更大的元素。
- 适用场景:高效处理大规模数据(
n ≤ 10000
)。
C语言
题目分析
给定一个环形数组,每个元素需要找到其后第一个更大的元素。若不存在,则返回 -1
。例如:
- 输入
2,5,2
的输出为5,-1,5
- 输入
1,2,3,4,5
的输出为2,3,4,5,-1
解决思路
单调栈算法:
- 遍历两次数组:通过取模操作模拟环形结构
- 维护单调递减栈:栈中保存元素索引,确保栈顶元素的值最小
- 更新结果数组:当当前元素大于栈顶元素时,更新结果
C 代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define INIT_CAPACITY 100 // 栈初始容量// 定义栈结构
typedef struct {int *data; // 存储索引的数组int top; // 栈顶指针int capacity; // 栈容量
} Stack;// 初始化栈
void initStack(Stack *s) {s->data = (int*)malloc(INIT_CAPACITY * sizeof(int));s->top = -1;s->capacity = INIT_CAPACITY;
}// 扩展栈容量
void resizeStack(Stack *s) {s->capacity *= 2;s->data = (int*)realloc(s->data, s->capacity * sizeof(int));
}// 压栈操作
void push(Stack *s, int val) {if (s->top == s->capacity - 1) resizeStack(s);s->data[++s->top] = val;
}// 弹栈操作
int pop(Stack *s) {return s->data[s->top--];
}// 获取栈顶元素
int top(Stack *s) {return s->data[s->top];
}// 判断栈是否为空
int isEmpty(Stack *s) {return s->top == -1;
}// 释放栈内存
void freeStack(Stack *s) {free(s->data);
}// 核心算法函数
int* nextGreaterElements(int* nums, int numsSize, int* returnSize) {int* res = (int*)malloc(numsSize * sizeof(int));*returnSize = numsSize;memset(res, -1, numsSize * sizeof(int)); // 初始化结果数组Stack s;initStack(&s);// 遍历两倍数组长度模拟环形for (int i = 0; i < 2 * numsSize; i++) {int current = nums[i % numsSize];// 维护单调栈while (!isEmpty(&s) && current > nums[top(&s)]) {int idx = pop(&s);res[idx] = current;}// 只压入第一次遍历的索引if (i < numsSize) {push(&s, i % numsSize);}}freeStack(&s);return res;
}// 字符串分割函数
int* parseInput(const char* input, int* size) {char* str = strdup(input);int count = 0;char* token = strtok(str, ",");int* arr = NULL;while (token) {arr = (int*)realloc(arr, (count + 1) * sizeof(int));arr[count++] = atoi(token);token = strtok(NULL, ",");}*size = count;free(str);return arr;
}int main() {char input[100000];fgets(input, sizeof(input), stdin);input[strcspn(input, "\n")] = '\0'; // 去除换行符int numsSize;int* nums = parseInput(input, &numsSize);int returnSize;int* res = nextGreaterElements(nums, numsSize, &returnSize);// 输出结果for (int i = 0; i < returnSize; i++) {if (i > 0) printf(",");printf("%d", res[i]);}free(nums);free(res);return 0;
}
代码解析
1. 栈结构定义
typedef struct {int *data; // 存储索引的数组int top; // 栈顶指针int capacity; // 栈容量
} Stack;
- 使用动态数组实现栈,支持自动扩容
2. 栈操作函数
void push(Stack *s, int val) {if (s->top == s->capacity - 1) resizeStack(s);s->data[++s->top] = val;
}
- 压栈时自动检查容量,不足时自动扩容
3. 核心算法
int* nextGreaterElements(int* nums, int numsSize, int* returnSize) {int* res = (int*)malloc(numsSize * sizeof(int));memset(res, -1, numsSize * sizeof(int));Stack s;initStack(&s);for (int i = 0; i < 2 * numsSize; i++) {int current = nums[i % numsSize];while (!isEmpty(&s) && current > nums[top(&s)]) {int idx = pop(&s);res[idx] = current;}if (i < numsSize) push(&s, i % numsSize);}freeStack(&s);return res;
}
- 关键逻辑:
- 遍历
2*n
次模拟环形 - 维护单调递减栈
- 只在第一次遍历时压入索引
- 遍历
4. 输入处理
int* parseInput(const char* input, int* size) {char* str = strdup(input);int count = 0;int* arr = NULL;// 使用 strtok 分割字符串
}
- 动态解析逗号分隔的输入字符串
示例测试
示例1:
输入:
1,2,3,4,5
输出:
2,3,4,5,-1
解析过程:
- 每个元素的后继即为其下一个更大值
- 最后一个元素没有更大值
示例2:
输入:
2,5,2
输出:
5,-1,5
解析过程:
- 第一个
2
找到后面的5
- 第二个
5
没有更大值 - 第三个
2
循环找到开头的5
综合分析
- 时间复杂度 O(n):每个元素最多入栈和出栈两次
- 空间复杂度 O(n):栈和结果数组各占用线性空间
- 内存管理:
- 动态数组自动扩容
- 严格释放所有分配的内存
- 健壮性:
- 处理任意长度输入(最大 100,000 字符)
- 支持正负数输入
- 优势:
- 完全手动实现栈操作
- 更精细的内存控制
- 适合嵌入式等资源受限环境
GO
题目分析
给定一个环形数组,每个元素需要找到其后的第一个更大元素。若不存在则返回 -1
。例如:
- 输入
2,5,2
的输出为5,-1,5
,最后一个元素循环后找到更大的5
。 - 输入
1,2,3,4,5
的输出为2,3,4,5,-1
。
解决思路
单调栈算法:
- 遍历两次数组:通过取模操作模拟环形结构。
- 维护单调递减栈:栈中保存元素索引,确保栈顶元素对应的值最小。
- 更新结果数组:当当前元素大于栈顶元素时,栈顶元素的下一个更大元素即为当前元素。
Go 代码实现
package mainimport ("fmt""strings"
)func nextGreaterElements(nums []int) []int {n := len(nums)res := make([]int, n)for i := range res {res[i] = -1 // 初始化结果数组为-1}stack := []int{} // 单调栈,保存索引// 遍历两倍数组长度以模拟环形结构for i := 0; i < 2*n; i++ {current := nums[i%n] // 当前元素(环形取模)// 维护单调栈:当前元素比栈顶元素大时,更新结果for len(stack) > 0 && current > nums[stack[len(stack)-1]] {top := stack[len(stack)-1]stack = stack[:len(stack)-1] // 弹出栈顶res[top] = current // 记录下一个更大元素}// 仅在第一次遍历时压入索引if i < n {stack = append(stack, i%n)}}return res
}func main() {// 示例测试input1 := "1,2,3,4,5"nums1 := parseInput(input1)result1 := nextGreaterElements(nums1)fmt.Println("示例1输出:", strings.Join(strings.Fields(fmt.Sprint(result1)), ",")) // 输出: 2,3,4,5,-1input2 := "2,5,2"nums2 := parseInput(input2)result2 := nextGreaterElements(nums2)fmt.Println("示例2输出:", strings.Join(strings.Fields(fmt.Sprint(result2)), ",")) // 输出: 5,-1,5
}// 将输入字符串转换为整数数组
func parseInput(input string) []int {strs := strings.Split(input, ",")nums := make([]int, len(strs))for i, s := range strs {fmt.Sscanf(s, "%d", &nums[i])}return nums
}
代码解析
-
初始化结果数组:
res := make([]int, n) for i := range res {res[i] = -1 }
- 创建长度与输入相同的数组,默认值为
-1
。
- 创建长度与输入相同的数组,默认值为
-
模拟环形遍历:
for i := 0; i < 2*n; i++ {current := nums[i%n]
- 遍历
2n
次,i%n
将索引映射到原数组范围。
- 遍历
-
维护单调栈:
for len(stack) > 0 && current > nums[stack[len(stack)-1]] {top := stack[len(stack)-1]stack = stack[:len(stack)-1]res[top] = current }
- 当当前元素大于栈顶元素时,弹出栈顶并更新结果。
-
压入索引:
if i < n {stack = append(stack, i%n) }
- 只在第一次遍历时压入索引,避免重复处理。
示例测试
示例1:
输入:1,2,3,4,5
输出:2,3,4,5,-1
解析:
- 每个元素的后一个元素更大,最后一个元素无更大值。
示例2:
输入:2,5,2
输出:5,-1,5
解析:
- 第一个
2
的下一个更大元素是5
。 5
之后没有更大元素。- 最后一个
2
循环到开头找到5
。
综合分析
- 时间复杂度 O(n):每个元素最多入栈和出栈两次,总操作次数为
4n
,时间复杂度为线性。 - 空间复杂度 O(n):栈和结果数组的空间均为
O(n)
。 - 正确性保障:
- 环形处理:通过遍历两次数组,确保每个元素的后继被完整检查。
- 单调栈特性:栈中保存未找到更大元素的索引,确保找到的是第一个更大值。
- 适用场景:高效处理大规模数据(
n ≤ 10000
)。
更多内容:
https://www.kdocs.cn/l/cvk0eoGYucWA
本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!