题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。例如:在数组{7,5,6,4}中,一共存在5对逆序对,分别是{7,5},{7,4},{7,6},{5,4},{6,4}。
解题思路
(1)暴力法
顺序扫描整个数组,然后每扫到一个数字,就拿后面的数字与它比较,如果后面的数字比它小,则这两个数字就组成了一个逆序对。假设数组中含有n个数,由于每个数字都要和O(n)个数字进行比较,因此这种算法的时间复杂度是O(n^2)。
源代码
package Arithmetic;
public class InversePairs {
public static int inversePairs(int[] data) {
int nums = 0;
for (int i = 0; i < data.length; i++) {
for (int j = i; j < data.length; j++)
if (data[i] > data[j])
nums++;
}
return nums;
}
public static void main(String[] args) {
System.out.println(inversePairs(new int[]{7, 5, 6, 4 }));
}
}
(2)归并排序
如下图所示,先把数组分解成两个长度为2的子数组,再把这两个子数组分别拆分成两个长度为1的子数组。接下来一边合并相邻的子数组,以便统计逆序对的数目。在第一对长度为1的子数组{7}、{5}中,7大于5,因此(7,5)也是一个逆序对。同样的,在第二对长度为1的子数组{6}、{4}中,也有逆序对(6,4)。由于已经统计了这两对子数组内部的逆序对了,因此需要把这两对子数组排序,以免在以后的统计过程中再避免重复。
源代码
package Arithmetic;
public class InversePairs {
public static int iPairs(int[] array) {
if (array == null)
throw new IllegalArgumentException();
// 创建辅助数组
int length = array.length;
int[] copy = new int[length];
System.arraycopy(array, 0, copy, 0, length);
int numberOfInversePairs = iPairs(array, copy, 0, length - 1);
return numberOfInversePairs;
}
/**
* @param array 未归并数组
* @param copy 用于存储归并后数据的数组
* @param begin 起始位置
* @param end 结束位置
* @return 逆序数
* @author Thanos
*/
public static int iPairs(int[] array, int[] copy, int begin, int end) {
if (begin == end)
return 0;
int mid = (begin + end) / 2;//中间位置
// 递归调用
int left = iPairs(copy, array, begin, mid);
int right = iPairs(copy, array, mid + 1, end);
// 归并
int i = mid, j = end, pos = end;
int count = 0; // 记录相邻子数组间逆序数
while (i >= begin && j >= mid + 1) {
if (array[i] > array[j]) {
copy[pos--] = array[i--];
count += j - mid;
} else
copy[pos--] = array[j--];
}
while (i >= begin)
copy[pos--] = array[i--];
while (j >= mid + 1)
copy[pos--] = array[j--];
return left + right + count;
}
public static void main(String[] args) {
System.out.println(iPairs(new int[]{7, 5, 6, 4, 3}));
}
}