剑指OFFER题48------按牛客网热度排序
时间:2019.1.18.2113
作者:Waitt
题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
时间限制:2秒 空间限制:32768K 热度指数:236829
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
示例1
输入:
1,2,3,4,5,6,7,0
输出:
7
解答
思路1
可以认为是在冒泡排序的过程中,前面数字比后面数字大的次数,即交换的次数。因此可以选择用冒泡排序统计次数,再取模。
但是这种算法时间复杂度较高,超过了本题的要求,当数组比较大时,就会超时。
class Solution {
public:
int InversePairs(vector<int> data) {
int n=data.size();
if(n<2)
return 0;
int c=0;
bool hu=1;//标志位,防止多余交换判断
for(int i=0;i<n&&hu;i++)//从头到尾
{
hu=0;//标志位置零
for(int j=n-1;j>i;j--)//从尾到当前位
{
if(data[j]<data[j-1])
{
int t=data[j];
data[j]=data[j-1];
data[j-1]=t;
c++;
hu=1;//若本轮存在交换数字,则将标志位置1
}
}
}
return c%1000000007;
}
};
该算法为优化的冒泡算法,但复杂度仍比较高,通过率仅为50%。
思路2
由于此题可以看做是排序过程中数字的变化情况。
而对于排序:
冒泡排序,简单选择排序和直接插入排序,均为简单算法,时间复杂度也较高,均为O(n2)。
希尔排序(改进版插入排序)时间复杂度O(n3/2)。也算是比较高的复杂度。
堆排序,一般使用堆均为标准库的堆,若自己建立,过程较为麻烦,时间复杂度O(nlogn)。
归并排序,时间复杂度O(nlogn)。可取。
快速排序,在某些情况下,时间复杂度仍较高,可尝试。
本思路为采取归并排序,并记录其中的变化情况(逆序对)。
思路详解:https://www.nowcoder.com/questionTerminal/96bd6684e04a44eb80e6a68efc0ec6c5
注意:此题的逆序对的数目可能很大,因此定义计数位不能简单的定义为int
class Solution {
public:
void guib(vector<int> &data,vector<int> &cp,int s,int n,long long &c)
//归并排序函数头
{
if(s==n)
{
cp[s]=data[s];
return;
}
int m=(s+n)/2;//取中分割
guib(data,cp,s,m,c);//分割为前一半
guib(data,cp,m+1,n,c);//分割为后一般
int i=s,j=m+1;//i为前一半数组计数,j为后一半数组计数
int k=s;//k为合并后的数组计数
for(;i<=m&&j<=n;k++)//进行前后两个数组的合并
{
if(data[i]<=data[j])//前侧数字小于后侧数字时,说明无逆序对
{
cp[k]=data[i];
++i;
}
else//后侧数字小于前侧数字时,说明有逆序对,且逆序对数目为m-i+1;i为当前前侧所剩的数字个数
{
cp[k]=data[j];
++j;
c=c+m-i+1;
}
}
for(;i<=m;k++)//将剩余未合并的数字合并
{
cp[k]=data[i];
++i;
}
for(;j<=n;k++)//将剩余未合并的数字合并
{
cp[k]=data[j];
++j;
}
for(int l=s;l<=n;l++)//将cp赋值给data,才可继续进行循环
{
data[l]=cp[l];
}
}
int InversePairs(vector<int> data) {
int n=data.size();
if(n<2)
return 0;
long long c=0;
//由于此处的c可能非常大,会超出INT的范围,因此定义类型为long long,防止初始值不定,必须赋值为0
vector<int> cp(data);//data为辅助向量
guib(data,cp,0,n-1,c);
return c%1000000007;
}
};