获取利润第K大的值(2026.11.01)
2026年最新华为OD机考题库(按考点分类): 2026年最新华为OD机考题库
题目描述
小张是一名金融交易员,现在记录了某只股票连续 n 个交易日每天的“涨跌金额”(上涨为正、下跌为负、横盘为 0);如果某次交易是“在某个交易日买入、之后某个交易日卖出”,那么这笔交易的利润就等于这两个交易日之间(含买入日与卖出日)所有日子的涨跌金额之和。
小张正在设计一个交易策略,想找出所有可行交易时段里,利润排名第 K 大的那个利润值,以此观察收益分布。
请你设计一个程序,帮小张求出这个第 K 大的交易利润值。
注意:
- 一次交易为「买入日 i → 卖出日 j(0≤i≤j≤n−1)」,利润 =a[i]+a[i+1]+⋯+a[j];
- 只要买入日 / 卖出日不同就视为不同交易时段;利润相同的不同时段也分别计入排名(并列依次占用排名);
- 数组可包含负数,因此利润可正、可负、可为 0,均按排名取第 K 大的值。
输入描述
输入一行,格式为 [a0, a1, ..., a(n-1)],K:先是一个方括号包裹、逗号分隔的整数数组 a,方括号后紧跟一个半角逗号,再给出整数 K。逗号之后可能带有任意空白。对应核心代码模式的两个参数:数组 a 与整数 K。
输出描述
输出一个整数,表示所有连续子数组和按从大到小排序后的第 K 大值(相同值并列各占一个名次)。答案在 64 位整数范围内。
样例
用例输入 1
[3, -2, 5],3
用例输出 1
3
用例解释 1
所有可行交易利润为 [3]=3、[-2]=-2、[5]=5、[3,-2]=1、[-2,5]=3、[3,-2,5]=6,按从大到小为 6, 5, 3, 3, 1, -2;重复的 3 各占一位,从大到小第 3 大为 3。
用例输入 2
[1, 2, 3],1
用例输出 2
6
用例解释 2
利润最大的一段就是整个记录 1+2+3=6,即第 1 大为 6。
用例输入 3
[-5, -2, -1, -3],2
用例输出 3
-2
用例解释 3
全部交易利润从大到小为 -1, -2, -3, -3, -4, -5, -6, -7, -8, -11,第 2 大为 -2(即便全是亏损,也照样按排名取值)。
来源
本题来源于网络、非官方材料、仅供学习交流,侵删
🧠 解题思路
第一步:把题目翻译成一句话
第 i 天买入、第 j 天卖出(i ≤ j)的利润,就是数组从下标 i 到下标 j 这一段连续元素的和 a[i] + a[i+1] + ... + a[j]。题目要找所有这样的连续子数组和里,从大到小排第 K 个的值(相同的值并列各占一个名次)。
第二步:数据范围够小,直接枚举所有区间和
n ≤ 1000,一共有多少个交易时段?起点 i 有 n 种,终点 j ≥ i,总共 n(n + 1) / 2 个,最多 1000 × 1001 / 2 = 500,500 个(约 50 万个)。50 万个数全部算出来再排个序,计算机几十毫秒就能跑完,完全不需要复杂算法。
如果三层循环(枚举 i、枚举 j、再从 i 加到 j)会达到 O(n³) ≈ 1.6×10⁸ 次,偏慢。其实固定买入日 i 后,卖出日 j 从 i 往右一格一格挪时,只要在上一个利润的基础上顺手加上 a[j],就能 O(1) 得到新利润,两层循环 O(n²) 就把所有利润都算出来了。
第三步:排序取第 K 大
把所有区间和放进一个列表,从大到小排序,下标 K - 1 处的值就是第 K 大。因为没有去重,相同的利润在列表里会挨在一起各占一个位置,天然符合“并列依次占用排名”的要求。
注意:单日涨跌可能很大,累加出来的利润可能超过 32 位整数范围(约 2×10⁹),在 C++ / Java 中要用 64 位整数(long long / long)保存利润。
用样例 1 走一遍
a = [3, -2, 5],K = 3。
| 买入日 i | 卖出日 j 依次往右累加 | 得到的利润 |
|---|---|---|
| 0 | j=0: 3;j=1: 3 + (-2) = 1;j=2: 1 + 5 = 6 | 3, 1, 6 |
| 1 | j=1: -2;j=2: -2 + 5 = 3 | -2, 3 |
| 2 | j=2: 5 | 5 |
共 6 个利润:[3, 1, 6, -2, 3, 5]。从大到小排序为 [6, 5, 3, 3, 1, -2],两个 3 各占第 3、第 4 名,第 3 大(下标 2)是 3,输出 3。
样例 3:[-5, -2, -1, -3],K = 2。全部是负数时利润也都是负数,共 10 个区间和,最大的是 [-1](-1),第二大的是 [-2](-2),输出 -2。
⚠️ 易错点
- 利润可能超过 32 位整数范围,累加变量和存利润的数组要用 64 位整数(
long/long long),用int会溢出。 - 相同利润的不同时段要分别保留,不能用集合去重。
- K 是从 1 开始的名次,降序排序后取下标
K - 1。 - 利润可以为负数(如全跌的行情),累加初值要从 0 开始,但不能把 0(不交易)放进候选列表。
- JavaScript 数组的
sort()不传比较函数时按字符串比较,要传入比较函数。
⏱️ 复杂度
时间复杂度:O(n² log n),共 n(n + 1) / 2 ≤ 500,500 个区间和。
空间复杂度:O(n²),存下全部区间和,n = 1000 时约 50 万个 64 位整数。
💻 参考代码(Java)
import java.util.Arrays;
class Solution {
public long getKthLargestProfit(int[] a, int k) {
int n = a.length;
long[] sums = new long[n * (n + 1) / 2];
int idx = 0;
for (int i = 0; i < n; i++) {
long s = 0; // 利润最大约 10^12,必须用 long
for (int j = i; j < n; j++) {
s += a[j]; // 第 i 天买入、第 j 天卖出的利润
sums[idx++] = s;
}
}
// 升序排序后,第 k 大在倒数第 k 位(相同利润各占一个名次)
Arrays.sort(sums);
return sums[sums.length - k];
}
}
💻 参考代码(C++)
class Solution {
public:
long long getKthLargestProfit(vector<int>& a, int k) {
int n = a.size();
vector<long long> sums;
sums.reserve((long long)n * (n + 1) / 2);
for (int i = 0; i < n; i++) {
long long s = 0; // 利润最大约 10^12,必须用 long long
for (int j = i; j < n; j++) {
s += a[j]; // 第 i 天买入、第 j 天卖出的利润
sums.push_back(s);
}
}
// 从大到小排序,第 k 大在下标 k-1(相同利润各占一个名次)
sort(sums.begin(), sums.end(), greater<long long>());
return sums[k - 1];
}
};
💻 参考代码(Python)
from itertools import accumulate
class Solution:
def getKthLargestProfit(self, a, k):
sums = []
for i in range(len(a)):
# a[i:] 的前缀和依次是:第 i 天买入、第 i, i+1, ... 天卖出的利润
sums.extend(accumulate(a[i:]))
# 从大到小排序,第 k 大在下标 k-1(相同利润各占一个名次)
sums.sort(reverse=True)
return sums[k - 1]
💻 参考代码(JavaScript)
class Solution {
getKthLargestProfit(a, k) {
const n = a.length;
const sums = [];
for (let i = 0; i < n; i++) {
let s = 0; // 利润绝对值不超过 10^12,在 JS 能精确表示的整数范围内
for (let j = i; j < n; j++) {
s += a[j]; // 第 i 天买入、第 j 天卖出的利润
sums.push(s);
}
}
// 必须传比较函数,否则会按字符串排序;从大到小排,第 k 大在下标 k-1
sums.sort((x, y) => y - x);
return sums[k - 1];
}
}
module.exports = { Solution };
8万+



被折叠的 条评论
为什么被折叠?



