题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
牛客上的题目添加了一个取模要求,不妨碍,只需要在求的数目后取模即可。
这道题就是归并排序的应用,复习以下归并排序~
public class MergeSortTest {
public static void main(String[] args) {
int[] data = new int[] { 5, 3, 6, 2, 1, 9, 4, 8, 7 };
print(data);
mergeSort(data);
System.out.println("排序后的数组:");
print(data);
}
public static void mergeSort(int[] data) {
sort(data, 0, data.length - 1);
}
public static void sort(int[] data, int left, int right) {
if (left >= right)
return;
// 找出中间索引
int center = (left + right) / 2;
// 对左边数组进行递归
sort(data, left, center);
// 对右边数组进行递归
sort(data, center + 1, right);
// 合并
merge(data, left, center, right);
print(data);
}
/*
* @param data
* 数组对象
* @param left
* 左数组的第一个元素的索引
* @param center
* 左数组的最后一个元素的索引,center+1 是右数组第一个元素的索引
* @param right
* 右数组最后一个元素的索引
*/
public static void merge(int[] data, int left, int center, int right) {
// 临时数组
int[] tmpArr = new int[data.length];
// 右数组第一个元素索引
int mid = center + 1;
// third 记录临时数组的索引
int third = left;
// 缓存左数组第一个元素的索引
int tmp = left;
while (left <= center && mid <= right) {
// 从两个数组中取出最小的放入临时数组
if (data[left] <= data[mid]) {
tmpArr[third++] = data[left++];
} else {
tmpArr[third++] = data[mid++];
}
}
// 剩余部分依次放入临时数组(实际上两个 while 只会执行其中一个)
while (mid <= right) {
tmpArr[third++] = data[mid++];}
while (left <= center) {
tmpArr[third++] = data[left++];
}
// 将临时数组中的内容拷贝回原数组中
// (原 left-right 范围的内容被复制回原数组)
while (tmp <= right) {
data[tmp] = tmpArr[tmp++];
}
}
public static void print(int[] data) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + "\t");
}
System.out.println();
}
}
输出结果:
5 3 6 2 1 9 4 8 7
3 5 6 2 1 9 4 8 7
3 5 6 2 1 9 4 8 7
3 5 6 1 2 9 4 8 7
1 2 3 5 6 9 4 8 7
1 2 3 5 6 4 9 8 7
1 2 3 5 6 4 9 7 8
1 2 3 5 6 4 7 8 9
1 2 3 4 5 6 7 8 9
排序后的数组:
1 2 3 4 5 6 7 8 9
归并排序的平均时间复杂度O(nlogn),并且稳定
回到题目上,就是排序在合并时候进行统计个数,理解归并排序后结合注解就可以明白
public class test1 {
public int result=0;
public int InversePairs(int [] array) {
mergeCount(array,0,array.length-1);
return result;
}
public void mergeCount(int[] nums, int left, int right){
if (left>=right) return;
int mid=(right+left)/2;
mergeCount(nums,left,mid);
mergeCount(nums,mid+1,right);
merge(nums,left,mid,right);
}
private void merge(int[] nums, int left, int mid, int right) {
int [] temp=new int[right-left+1];
//mid是第一个数组的最后 j是第二个数组的开始
int k=0,i=left,j=mid+1;
while(i<=mid&&j<=right)
{
//从左右两个数组中 前面的小于后面的 不能构成逆序对
if (nums[i]<=nums[j])
{
//把小的放进入
temp[k++]=nums[i++];
}
//前面的大于后面的 说明前面数组之后的所有数都能和后面数组的这意味构成逆序对 那么算出前面数组元素的个数即可 mid-i+1
else{
temp[k++]=nums[j++];
result=(result+(mid-i+1))%1000000007;
}
}
//把两个数组剩余元素放入辅助数组 所有两个数组的循环只会执行一个 必定有一个已经被放完
while(i<=mid)
temp[k++]=nums[i++];
while(j<=right)
temp[k++] = nums[j++];
//将临时数组的元素放入原来的数组中
for (int m=0;m<k;m++)
nums[left+m]=temp[m];
}
}