这道来自华为OD机考C卷的编程题,考察的是在双机位监考环境下,应试者对算法和数据结构的基础应用能力。题目要求找出数组中几何平均值最大的连续子数组,并用Java实现解决方案。这类题型在互联网大厂的笔试中属于中等偏上难度,既考察数学思维又检验编码基本功。
几何平均值(Geometric Mean)是n个正数乘积的n次方根,与算术平均值的计算方式有本质区别。对于数组[1, 2, 4],其几何平均值为(1×2×4)^(1/3)≈2.0,而算术平均值则是(1+2+4)/3≈2.33。这种差异导致解题时需要采用特殊的处理策略。
关键提示:几何平均值对数值分布更敏感,极端小值会显著拉低结果,这与算术平均值形成鲜明对比
最直观的解法是双重循环遍历所有可能的子数组,计算每个子数组的几何平均值并记录最大值。对于长度为n的数组,时间复杂度为O(n²),在n较大时(如n>10^4)会导致超时。
java复制// 伪代码示例(实际需处理数值溢出等问题)
double maxGeoMean = 0;
for(int i=0; i<n; i++){
double product = 1;
for(int j=i; j<n; j++){
product *= nums[j];
double current = Math.pow(product, 1.0/(j-i+1));
maxGeoMean = Math.max(maxGeoMean, current);
}
}
几何平均值有个重要特性:对数值取对数后,几何平均值问题可转化为算术平均值问题。具体来说:
code复制(∏a_i)^(1/n) = exp( (∑ln(a_i))/n )
利用这个性质,我们可以将原数组转换为对数数组,问题就转化为寻找最大算术平均值的子数组。虽然算术平均值最大子数组问题本身也没有O(n)解法,但可以通过以下观察优化:
虽然滑动窗口是处理子数组问题的常用技巧,但在此题中并不适用。原因在于:
java复制public static double maxGeometricMean(int[] nums) {
double maxMean = 0;
double[] logNums = new double[nums.length];
// 转换为对数数组并处理0值(题目应保证无0和负数)
for(int i=0; i<nums.length; i++){
logNums[i] = Math.log(nums[i]);
}
// 检查所有长度为1和2的子数组
for(int i=0; i<nums.length; i++){
double sum = logNums[i];
maxMean = Math.max(maxMean, Math.exp(sum/1)); // 单元素情况
if(i < nums.length-1){
sum += logNums[i+1];
maxMean = Math.max(maxMean, Math.exp(sum/2)); // 双元素情况
}
}
return maxMean;
}
华为OD机考采用双机位监考:
在这种高压环境下,解题时需注意:
由于无法使用IDE调试,建议:
java复制// 调试输出示例
System.err.println("Debug: current max="+maxMean); // 使用err输出避免干扰正常输出
java复制// 常规用例
int[] case1 = {1, 2, 4}; // 期望2.0
int[] case2 = {2, 2, 2}; // 期望2.0
// 边界用例
int[] case3 = {5}; // 期望5.0
int[] case4 = {1,1,1,1,2}; // 期望2.0
// 极值用例
int[] case5 = {Integer.MAX_VALUE, Integer.MAX_VALUE}; // 期望MAX_VALUE
对于正数数组a₁,a₂,...,aₙ,几何平均值最大子数组问题等价于:
max (∏a_i)^(1/k) = max exp( (∑ln a_i)/k )
由于exp函数单调递增,因此只需最大化(∑ln a_i)/k
可以证明:这个最大值要么由单个元素(k=1)取得,要么由某两个相邻元素(k=2)取得
原始实现使用了O(n)额外空间存储对数数组,实际上可以优化为O(1):
java复制public static double maxGeometricMeanOpt(int[] nums) {
double maxMean = 0;
for(int i=0; i<nums.length; i++){
double logSum = Math.log(nums[i]);
maxMean = Math.max(maxMean, Math.exp(logSum)); // 单元素
if(i < nums.length-1){
logSum += Math.log(nums[i+1]);
maxMean = Math.max(maxMean, Math.exp(logSum/2)); // 双元素
}
}
return maxMean;
}
虽然题目要求Java实现,但了解其他语言的实现方式有助于拓宽思路:
Python示例(利用内置库更简洁):
python复制import math
def max_geometric_mean(nums):
max_mean = 0
for i in range(len(nums)):
current = math.exp(math.log(nums[i]))
max_mean = max(max_mean, current)
if i < len(nums)-1:
current = math.exp((math.log(nums[i]) + math.log(nums[i+1]))/2)
max_mean = max(max_mean, current)
return max_mean
几何平均值最大子数组问题在以下场景有实际应用:
根据近期考生反馈,华为OD机考C卷常考:
建议采用以下时间分配:
在准备过程中要特别注意: