
在处理两个整数数组 a 和 b 时,我们经常会遇到这样的需求:对于数组 b 中的每一个元素,统计数组 a 中有多少个元素大于或等于它。例如,给定 a = [1, 2, 3, 4, 5] 和 b = [6, 5, 4, 3, 2],期望的输出是 [0, 1, 2, 3, 4]。这意味着对于 b 中的 6,a 中没有元素大于等于它(计数为0);对于 b 中的 5,a 中有 5(计数为1);对于 b 中的 4,a 中有 4, 5(计数为2),以此类推。
一个直观的解决方案是使用嵌套循环:外层循环遍历数组 b 的每个元素,内层循环遍历数组 a 的每个元素进行比较和计数。
import java.util.ArrayList;
import java.util.List;
public class ArrayComparison {
/**
* 传统嵌套循环方法,计算数组a中大于等于b中每个元素的个数
* @param a 数组a
* @param b 数组b
* @return 结果列表
*/
public static List<Integer> giantArmyNaive(int a[], int b[]) {
List<Integer> list = new ArrayList<>();
// 特殊情况处理:如果a只有一个元素且为0,则直接返回[0]
// 实际应用中,此条件可能需要根据具体业务逻辑调整或移除
if (a.length == 1 && a[0] == 0) {
list.add(0);
return list;
}
for (int i = 0; i < b.length; i++) {
int count = 0; // 每次循环b的元素时重置计数器
for (int j = 0; j < a.length; j++) {
if (a[j] >= b[i]) {
count++;
}
}
list.add(count);
}
return list;
}
}这种方法的时间复杂度是 O(N * M),其中 N 是数组 a 的长度,M 是数组 b 的长度。当 N 和 M 都很大时(例如,百万级别),这种方法会导致性能急剧下降,可能造成程序响应缓慢甚至超时。
为了显著提升性能,我们可以利用排序和二分查找的优势。核心思想是:如果数组 a 是有序的,那么查找大于或等于某个特定值的元素将变得非常高效。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArrayComparisonOptimized {
/**
* 优化后的方法,使用排序和二分查找计算数组a中大于等于b中每个元素的个数
* @param a 数组a
* @param b 数组b
* @return 结果列表
*/
public static List<Integer> giantArmyOptimized(int a[], int b[]) {
int aLength = a.length;
List<Integer> result = new ArrayList<>();
// 步骤1: 对数组a进行排序
Arrays.sort(a); // O(N log N)
// 步骤2 & 3 & 4: 遍历b,对每个元素在a中进行二分查找并计算
for (int b_element : b) { // O(M) 次循环
int index = Arrays.binarySearch(a, b_element); // O(log N)
// 如果二分查找结果为负,说明b_element不存在于a中
// 此时index = -(插入点) - 1
// 真实的插入点 (即第一个大于b_element的元素的索引) = -index - 1
if (index < 0) {
index = -index - 1;
}
// 此时index代表了a中第一个大于或等于b_element的元素的索引
// 那么,从这个索引到数组末尾的所有元素都符合条件
result.add(aLength - index);
}
return result;
}
public static void main(String[] args) {
int[] a = {1, 2, 3, 4, 5};
int[] b = {6, 5, 4, 3, 2};
System.out.println("原始数组 a: " + Arrays.toString(a));
System.out.println("原始数组 b: " + Arrays.toString(b));
List<Integer> optimizedResult = giantArmyOptimized(a, b);
System.out.println("优化方法计算结果: " + optimizedResult); // 输出: [0, 1, 2, 3, 4]
// 验证传统方法(如果需要)
// System.out.println("传统方法计算结果: " + giantArmyNaive(new int[] {1, 2, 3, 4, 5}, new int[] {6, 5, 4, 3, 2}));
}
}因此,总的时间复杂度为 O(N log N + M log N)。这比传统方法的 O(N * M) 效率要高得多,尤其是在 N 和 M 都很大的情况下。
为了更好地理解 aLength - index 的工作原理,我们以 a = [1, 2, 3, 4, 5] 为例:
数组 a: [1, 2, 3, 4, 5] 索引: 0 1 2 3 4 aLength = 5 当 b_element = 6: Arrays.binarySearch(a, 6) 返回 -6 (表示应插入到索引5) index = -(-6) - 1 = 5 结果 = aLength - index = 5 - 5 = 0 (没有元素 >= 6) 当 b_element = 5: Arrays.binarySearch(a, 5) 返回 4 (5在索引4) index = 4 结果 = aLength - index = 5 - 4 = 1 (元素: 5) 当 b_element = 4: Arrays.binarySearch(a, 4) 返回 3 (4在索引3) index = 3 结果 = aLength - index = 5 - 3 = 2 (元素: 4, 5) 当 b_element = 3: Arrays.binarySearch(a, 3) 返回 2 (3在索引2) index = 2 结果 = aLength - index = 5 - 2 = 3 (元素: 3, 4, 5) 当 b_element = 2: Arrays.binarySearch(a, 2) 返回 1 (2在索引1) index = 1 结果 = aLength - index = 5 - 1 = 4 (元素: 2, 3, 4, 5) 当 b_element = 1: Arrays.binarySearch(a, 1) 返回 0 (1在索引0) index = 0 结果 = aLength - index = 5 - 0 = 5 (元素: 1, 2, 3, 4, 5) 当 b_element = 0 (假设): Arrays.binarySearch(a, 0) 返回 -1 (表示应插入到索引0) index = -(-1) - 1 = 0 结果 = aLength - index = 5 - 0 = 5 (元素: 1, 2, 3, 4, 5)
通过采用排序与二分查找的组合,我们能够将双数组比较的性能从平方级提升到对数线性级,这对于处理大规模数据集至关重要。理解并掌握这种优化技巧,是编写高效、可扩展代码的关键一步。
以上就是优化双数组比较性能:排序与二分查找的高效实践的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号