
在数组处理中,有时我们需要识别不符合特定排序规则的元素对。本教程关注的是一种特殊的“坏对”:给定一个整数数组 hs,如果存在索引 i < j 使得 hs[i] < hs[j],则 (hs[i], hs[j]) 被视为一个“坏对”。我们的目标是统计数组中所有此类“坏对”的总数。
例如:
这个问题本质上是统计数组中的“逆序对”,但通常逆序对定义为 arr[i] > arr[j] 且 i < j。这里我们是统计 arr[i] < arr[j] 且 i < j 的对,可以理解为在目标是降序排列时,元素顺序错误的对。
最直观的方法是使用两层嵌套循环遍历数组,对每对满足 i < j 的元素进行比较。
public class BadPairCounter {
/**
* 使用暴力法统计数组中的“坏对”数量。
* 时间复杂度:O(N^2)
*
* @param hs 输入整数数组
* @return 坏对的总数
*/
public static int countBaadBruteForce(int[] hs) {
int count = 0;
for (int i = 0; i < hs.length; i++) {
for (int j = i + 1; j < hs.length; j++) {
if (hs[i] < hs[j]) {
// System.out.println("Found bad pair: (" + hs[i] + "," + hs[j] + ")"); // 可选:打印坏对
count++;
}
}
}
return count;
}
public static void main(String[] args) {
int[] arr1 = {7, 3, 5, 4, 1};
System.out.println("Array: " + java.util.Arrays.toString(arr1) + ", Bad pairs (Brute Force): " + countBaadBruteForce(arr1)); // Expected: 2
int[] arr2 = {8, 5, 6, 7, 2, 1};
System.out.println("Array: " + java.util.Arrays.toString(arr2) + ", Bad pairs (Brute Force): " + countBaadBruteForce(arr2)); // Expected: 3
}
}暴力解法简单易懂,但其时间复杂度为 O(N^2),对于大型数据集,性能会急剧下降,导致计算时间过长。
为了提高效率,我们可以利用归并排序(Merge Sort)的特性来解决这个问题。归并排序在合并两个已排序的子数组时,会进行元素间的比较,这正是我们统计“坏对”的绝佳时机。
标准的归并排序将数组递归地分成两半,直到每个子数组只包含一个元素。然后,它将这些子数组两两合并,每次合并都确保子数组是有序的。在这个合并过程中,我们可以有效地统计“坏对”。
为了统计 hs[i] < hs[j] 且 i < j 的对,我们可以修改归并排序,使其在合并两个降序排列的子数组时进行计数。
假设我们有两个已经按降序排列的子数组 L 和 R。在合并它们时,我们从 L 和 R 的头部开始比较:
import java.util.Arrays;
public class BadPairMergeSortCounter {
/**
* 入口方法,统计数组中的“坏对”数量。
* 为了不修改原始数组,先进行复制。
*
* @param hs 输入整数数组
* @return 坏对的总数
*/
public static int countBaad(int[] hs) {
// 复制数组以避免修改原始输入
int[] tempArray = Arrays.copyOf(hs, hs.length);
return mergeSort(tempArray, tempArray.length);
}
/**
* 递归地对数组进行归并排序并统计“坏对”。
*
* @param a 待排序和计数的数组
* @param n 数组长度
* @return 当前子数组及其子问题中发现的坏对总数
*/
public static int mergeSort(int[] a, int n) {
if (n <= 1) { // 基准情况:数组长度为0或1,没有坏对
return 0;
}
int mid = n / 2;
int[] l = new int[mid];
int[] r = new int[n - mid];
// 分割数组
System.arraycopy(a, 0, l, 0, mid);
if (n - mid > 0) { // 确保右子数组有元素
System.arraycopy(a, mid, r, 0, n - mid);
}
int result = 0;
// 递归统计左右子数组中的坏对
result += mergeSort(l, mid);
result += mergeSort(r, n - mid);
// 合并左右子数组,并统计合并过程中产生的坏对
result += merge(a, l, r);
return result;
}
/**
* 合并两个已降序排序的子数组,并统计合并过程中产生的“坏对”。
*
* @param a 目标数组,用于存放合并后的结果
* @param l 左子数组 (已降序排序)
* @param r 右子数组 (已降序排序)
* @return 合并过程中发现的坏对数量
*/
public static int merge(int[] a, int[] l, int[] r) {
// System.out.println("Merging " + Arrays.toString(l) + " and " + Arrays.toString(r)); // 可选:日志输出合并过程
int badPairCount = 0;
int lIdx = 0, rIdx = 0, aIdx = 0;
while (lIdx < l.length && rIdx < r.length) {
// 我们希望结果数组是降序的
if (l[lIdx] >= r[rIdx]) {
// 如果左边元素大于或等于右边元素,符合降序要求,取左边元素
a[aIdx++] = l[lIdx++];
} else {
// 如果左边元素小于右边元素 (l[lIdx] < r[rIdx])
// 这意味着 l[lIdx] 与 r[rIdx] 构成一个“坏对”。
// 由于 l 数组和 r 数组都是降序排列的,
// r[rIdx] 比 l 数组中从 lIdx 位置到末尾的所有元素都大。
// 因此,r[rIdx] 与 l 数组中所有剩余元素 (l.length - lIdx 个) 都构成“坏对”。
badPairCount += (l.length - lIdx);
// System.out.println(" Found " + (l.length - lIdx) + " bad pairs with " + r[rIdx] + " from " + Arrays.toString(Arrays.copyOfRange(l, lIdx, l.length))); // 可选:详细日志
a[aIdx++] = r[rIdx++]; // 取右边元素
}
}
// 将左子数组中剩余的元素复制到目标数组
if (lIdx < l.length) {
System.arraycopy(l, lIdx, a, aIdx, l.length - lIdx);
}
// 将右子数组中剩余的元素复制到目标数组
if (rIdx < r.length) {
System.arraycopy(r, rIdx, a, aIdx, r.length - rIdx);
}
return badPairCount;
}
public static void main(String[] args) {
int[] arr1 = {7, 3, 5, 4, 1};
System.out.println("Array: " + java.util.Arrays.toString(arr1) + ", Bad pairs (Merge Sort): " + countBaad(arr1)); // Expected: 2
int[] arr2 = {8, 5, 6, 7, 2, 1};
System.out.println("Array: " + java.util.Arrays.toString(arr2) + ", Bad pairs (Merge Sort): " + countBaad(arr2)); // Expected: 3
}
}统计数组中特定类型的“坏对”是一个常见的算法问题。虽然暴力解法简单直接,但其 O(N^2) 的时间复杂度使其不适用于大规模数据。通过巧妙地修改归并排序算法,我们可以在 O(N log N) 的时间复杂度内高效地解决这个问题,这在处理大量数据时具有显著的性能优势。理解归并排序中 merge 阶段的计数逻辑是实现这一优化的关键。在实际应用中,还需注意对原始数据的保护(通过复制数组)以及使用高效的数组操作(如 System.arraycopy)。
以上就是使用归并排序高效统计数组中的“坏对”数量的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号