在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
初看题目,简单!!!双重循环嘛,暴力直接
//两次循环遍历
public int InversePairs(int [] array) {
if(array==null)//非空判断
{
return 0;
}
int count=0;//记录逆序数对的对数
for(int i=0;i<array.length-1;i++)
{
for(int j=i+1;j<array.length;j++)
{
if(array[j]<array[i])
{
count++;
}
}
}
return count;
}
然鹅
心累。。。还是让我们看一下大神们是怎么做的
归并排序算法原理
归并排序算法过程
二路归并过程
算法实现
/********************
* 封装归并排序算法
* @param array 需要进行排序的数组
*/
public static void mergeSort(int[] array)
{
mergeSort(array, 0, array.length-1);
}
/***********************
* 归并排序中的二路归并算法
* @param array 需要进行归并的数组
* @param start 要归并序列1的开始位置
* @param mid 序列1的结束位置
* @param end 要归并序列2的结束位置
*/
private static void merge(int[] array,int start,int mid,int end)
{
//设置临时数组用于保存归并排序后的序列
int[] temp=new int[end-start+1];
int index1=start,index2=mid+1;//用于遍历序列1和序列2
int index=0;//遍历临时数组
while(index1<=mid&&index2<=end)
{
//将两序列中小的那个添加到临时数组中
if(array[index1]<=array[index2])
{
temp[index++]=array[index1++];
}
else {
temp[index++]=array[index2++];
}
}
if(index1>mid)
{
//将序列2中剩余的所有值放入临时数组中
while(index2<=end)
{
temp[index++]=array[index2++];
}
}
if(index2>end)
{
while(index1<=mid)
{
temp[index++]=array[index1++];
}
}
//临时数组中的序列已经有序
//将数组拷贝到原数组中
System.arraycopy(temp, 0, array, start, temp.length);
}
/**********************
* 归并排序算法的主体过程 整个排序过程类似二叉树后序遍历的过程
* @param array 需要进行排序的数组
* @param start 数组开始位置
* @param end 数组结束位置
*/
public static void mergeSort(int[] array,int start,int end)
{
if(start>=end)//递归出口 如果只剩下单个元素 递归结束
{
return;
}
int mid=(start+end)>>1;//中间位置
mergeSort(array, start, mid);//左半部分进行归并排序
mergeSort(array, mid+1, end);
merge(array, start, mid, end);//完成二路归并
}
利用归并排序求解逆序数对
理解了归并排序算法后,我们可以发现在二路归并时,就是一个在子序列中比较各元素大小的过程,我们将其中的数据记录下来并返回,就可以以O(nlogn)的时间复杂度求解出逆序数对的组数。
现在主要的问题在于在一次二路归并过程中逆序数对的计算方法。
首先,进行二路归并的两个数组是已经是有序的,因此出现的逆序数只会是两个数组中各有一个数。
所以,逆序数出现在前面数组下标位置的值大于后面数组下标位置的值的时候。
因此这种情况出现时,将逆序数个数加前一数组中剩余元素个数即可。
最后,让我们回到题目
源码如下:
注意int类型的最大范围为-2147483648——2147483647
/* 封装归并排序算法
* @param array 需要进行排序的数组
*/
public int InversePairs(int[] array)
{
int[] temp=new int[array.length];//避免程序中重复开辟空间
return mergeSort(array, 0, array.length-1,temp);
}
/***********************
* 归并排序中的二路归并算法
* @param array 需要进行归并的数组
* @param start 要归并序列1的开始位置
* @param mid 序列1的结束位置
* @param end 要归并序列2的结束位置
*/
private int merge(int[] array,int start,int mid,int end,int[] temp)
{
long count=0;//此次归并时逆序数的个数
//设置临时数组用于保存归并排序后的序列
// int[] temp=new int[end-start+1];
int index1=start,index2=mid+1;//用于遍历序列1和序列2
int index=0;//遍历临时数组
while(index1<=mid&&index2<=end)
{
//将两序列中小的那个添加到临时数组中
if(array[index1]<=array[index2])
{
temp[index++]=array[index1++];
}
else {
temp[index++]=array[index2++];
count+=mid-index1+1;
}
}
if(index1>mid)
{
//将序列2中剩余的所有值放入临时数组中
while(index2<=end)
{
temp[index++]=array[index2++];
}
}
if(index2>end)
{
while(index1<=mid)
{
temp[index++]=array[index1++];
}
}
//临时数组中的序列已经有序
//将数组拷贝到原数组中
System.arraycopy(temp, 0, array, start, index);
return (int)(count%1000000007);
}
/**********************
* 归并排序算法的主体过程
* @param array 需要进行排序的数组
* @param start 数组开始位置
* @param end 数组结束位置
*/
public int mergeSort(int[] array,int start,int end,int[] temp)
{
if(start>=end)//递归出口 如果只剩下单个元素 递归结束
{
return 0;
}
int mid=(start+end)>>1;
int leftCount=mergeSort(array, start, mid,temp);
int rightCount=mergeSort(array, mid+1, end,temp);
return (leftCount+rightCount+merge(array, start, mid, end,temp))%1000000007;
}