问题描述
小C 和小U 有两个数组,分别是 a 和 b,它们的长度相同。小U 想通过重新排列数组 a 的元素,来最小化 a 和 b 之间的差异。具体来说,他们要最小化所有元素差值绝对值之和,即 sum(abs(a[i] - b[i]))。
你能帮助小C 和小U 找到这个最小化的值吗?
测试样例
样例1:
输入:
a = [2, 1, 3, 2], b = [5, 2, 4, 2]
输出:5
样例2:
输入:
a = [1, 4, 6], b = [2, 5, 7]
输出:3
样例3:
输入:
a = [1, 9, 6], b = [2, 5, 7]
输出:4
解题思路
-
核心目标:
通过匹配数组a和b中的元素,使得匹配的差值绝对值总和最小。 -
步骤:
- 对数组
a和b排序。 - 使用双指针方法,将两个数组的元素一一匹配,计算差值并累加到结果中。
- 对
b中的元素进行计数以处理重复值。
- 对数组
为什么从小到大排序后得到的差值最小
贪心思想
排序的核心思想是:把小的数尽量匹配小的数,大的数尽量匹配大的数。
假设我们在两个数组中随意匹配(未排序),可能会导致较小的数字与较大的数字匹配,这样会使得差值变得更大。而通过排序后,从小到大匹配,可以让每一步的差值保持局部最优,进而整体最优。
数学证明
排序后,数组元素按照从小到大的顺序排列,我们希望找到两个数组之间的最小差值总和 ∑∣a[i]−b[j]∣。可以证明:
- 如果排序后的 a 和 b 是单调递增的,那么 a[i] 对应 b[i] 是全局最优匹配。
- 反例证明:若 a[i] 对应 b[j] 且 i≠j,我们交换 b[i] 和 b[j] 后,差值总和一定不会减小。
交换的反例证明
假设两数组未排序:
- 数组 a={1,4,6}
- 数组 b={7,2,5}
随机匹配:(1,7),(4,2),(6,5),差值总和为:
∣1−7∣+∣4−2∣+∣6−5∣=6+2+1=9
排序后匹配:
- 排序 a={1,4,6},b={2,5,7}
- 匹配:(1,2),(4,5),(6,7),差值总和为:
∣1−2∣+∣4−5∣+∣6−7∣=1+1+1=3
结论:通过排序后匹配,相同位置的元素差值是最小的,累加总和也就最小。
直观理解
- 假设 a[i] 对应的是 b[j],而 a[i+1] 对应的是 b[j+1]。如果 b[j]>b[j+1],交换 b[j]b[j]b[j] 和 b[j+1],差值只会变小。
- 按照排序后逐一匹配,能够避免大的数匹配小的数带来的巨大差值。
代码实现(java版)
import java.util.Arrays;
public class Main {
public static long solution(int[] a, int[] b) {
// 1. 对数组 a 和 b 排序
Arrays.sort(a);
Arrays.sort(b);
// 2. 使用双指针计算最小差值
int i = 0; // 指向数组 a
int j = 0; // 指向数组 b
long result = 0; // 累加最小差值
while (i < a.length && j < b.length) {
result += Math.abs(a[i] - b[j]); // 累加当前最小差值
i++; // 移动指针 i
j++; // 移动指针 j
}
return result;
}
public static void main(String[] args) {
System.out.println(solution(new int[]{2, 1, 3, 2}, new int[]{5, 2, 4, 2}) == 5);
// System.out.println(solution(new int[]{1, 4, 6}, new int[]{2, 5, 7}) == 3);
// System.out.println(solution(new int[]{1, 9, 6}, new int[]{2, 5, 7}) == 4);
}
}
代码解析
-
排序:
- 通过排序,将两个数组的元素从小到大排列,确保后续匹配时差值最小。
-
双指针匹配:
- 分别使用指针
i和j遍历两个数组,同时匹配相应元素。 - 由于两个数组已排序,直接逐个匹配的差值总是局部最优解。
- 分别使用指针
-
时间复杂度:
- 排序的复杂度为
O(nlogn)。 - 双指针遍历的复杂度为
O(n)。 - 总复杂度为
O(nlogn)。
- 排序的复杂度为