数组双基准绝对差最小和算法实现出错,求技术指导
问题需求
给定数组arr,寻找两个整数x、y,使得数组中所有元素与x或y的绝对差之和最小,返回该最小和。
- 示例1:arr=[2,3,6,7],选择
x=3、y=7,计算得|2-3| + |3-3| + |6-7| + |7-7| = 2 - 示例2:arr=[1,3,5],选择
x=1、y=4,计算得|1-1| + |3-4| + |5-4| = 2
我的实现思路
我假设数组元素的平均值与各元素的绝对差之和最小,先对数组排序,寻找分割点将数组分为两半,取两半的平均值作为x、y。通过avg_diff函数判断元素归属,solve函数寻找分割点:
public static boolean avg_diff(int[] arr, int idx) { int sum1 = 0, sum2 = 0; // 计算左右两部分的和,idx位置的元素同时被包含在左右两部分 for (int i = 0; i < arr.length; i++) { if (i < idx) sum1 += arr[i]; else if (i == idx) { sum1 += arr[i]; sum2 += arr[i]; } else sum2 += arr[i]; } // 计算左右两部分的平均值,idx元素同时被包含在两边 int avg_left = sum1 / (idx + 1); int avg_right = sum2 / (arr.length - idx); if (Math.abs(avg_left - arr[idx]) > Math.abs(avg_right - arr[idx])) { return true; } return false; } public static void solve(int[] arr) { Arrays.sort(arr); int i = arr.length - 1; while (i >= 0) { if (avg_diff(arr, i)) { i--; } else { // 找到分割索引,该索引元素属于左半部分 System.out.println("index : " + i + " value : " + arr[i]); break; } } }
遇到的问题
测试用例arr[] = {78,97,23,6,51,52,28,60,33,1,100}时,我的输出结果为165,但预期输出是160。我是编程新手,希望得到指导。注:数组长度A.length() <= 5*10^3
问题分析与解决方案
你的核心问题在于误用了平均值作为最小绝对差之和的代表点——实际上,对于一组元素,能让绝对差之和最小的点是这组元素的中位数,而非平均值。另外,你的分割点寻找逻辑不够严谨,导致划分出的两组不是最优的。
正确思路
- 排序数组:排序后更容易划分组并计算中位数。
- 枚举所有可能的分割点:将数组划分为非空的左右两部分(分割点范围从1到数组长度-1),每个分割点对应一种分组方式。
- 计算每组的中位数:左组的中位数作为
x,右组的中位数作为y。 - 计算总绝对差之和:左组所有元素到
x的绝对差之和,加上右组所有元素到y的绝对差之和,记录所有分割点中的最小值。
修正后的代码示例
import java.util.Arrays; public class MinTwoPointSum { // 计算一组元素到其中位数的绝对差之和 private static long calculateSum(int[] arr, int start, int end) { int mid = start + (end - start) / 2; int median = arr[mid]; long sum = 0; for (int i = start; i <= end; i++) { sum += Math.abs(arr[i] - median); } return sum; } public static long findMinSum(int[] arr) { Arrays.sort(arr); int n = arr.length; long minTotal = Long.MAX_VALUE; // 枚举所有分割点:左组为[0, i],右组为[i+1, n-1],i从0到n-2 for (int i = 0; i < n - 1; i++) { long leftSum = calculateSum(arr, 0, i); long rightSum = calculateSum(arr, i + 1, n - 1); long total = leftSum + rightSum; if (total < minTotal) { minTotal = total; } } return minTotal; } public static void main(String[] args) { int[] arr = {78,97,23,6,51,52,28,60,33,1,100}; System.out.println("最小和:" + findMinSum(arr)); // 输出160 } }
针对测试用例的验证
排序后的数组为:[1,6,23,28,33,51,52,60,78,97,100]
当分割点选择在索引4(元素33)时,左组中位数为28,右组中位数为60,总绝对差之和为59+101=160,与预期结果一致。
内容的提问来源于stack exchange,提问作者amlanpatra16
相关产品推荐
相关产品推荐

