参考博文,代码原创
http://blog.csdn.net/alongela/article/details/8142965
http://blog.csdn.net/alongela/article/details/8119732
采用分治思想,将数组一分为2,先求字数组中的逆序,然后把子数组排好序,再合并有序数组求逆序,即在归并排序的过程中求逆序,算法时间复杂度可以由O(N^2)减为O(logN)
给定n个数,要求这些数构成的逆序对的个数。除了用归并排序来求逆序对个数,还可以使用树状数组来求解。
树状数组求解的思路:开一个能大小为这些数的最大值的树状数组,并全部置0。从头到尾读入这些数,每读入一个数就更新树状数组,查看它前面比它小的已出现过的有多少个数sum,然后用当前位置减去该sum,就可以得到当前数导致的逆序对数了。把所有的加起来就是总的逆序对数。
题目中的数都是独一无二的,这些数最大值不超过999999999,但n最大只是500000。如果采用上面的思想,必然会导致空间的巨大浪费,而且由于内存的限制,我们也不可能开辟这么大的数组。因此可以采用一种称为“离散化”的方式,把原始的数映射为1-n一共n个数,这样就只需要500000个int类型的空间。
离散化的方式:
struct Node
{
int val;
int pos;
};
Node node[500005];
int reflect[500005];
val存放原数组的元素,pos存放原始位置,即node[i].pos = i。
把这些结构体按照val的大小排序。
reflect数组存放离散化后的值,即reflect[node[i].pos] = i。
这样从头到尾读入reflect数组中的元素,即可以保持原来的大小关系,又可以节省大部分空间。
树状数组是一种非常巧妙的数据结构。对于一个普通数组,可以构造出一个对应的树状数组。普通数组中的元素存放的只是本身的信息,但树状数组中的元素存放的却是多个原数组的信息。
对于一个非负整数x,我们可以将它表示为二进制,设它的二进制形式中,结尾的0的个数为k。
对于树状数组,它的x位置上的元素表示的是原数组从x位置开始,往后数2^k个元素的信息(一般是它们的和,也可以是最大值或最小值)。
比如:
x=1时2^k=1,即它只包含本身的信息。其他奇数也一样。
x=2时2^k=2,即它包含2和1位置上的元素信息
x=4时2^k=4,即它包含4、3、2、1位置上的元素信息
x=6时2^k=2,即它包含6和5位置上的元素信息
……
对于x计算出它对应的2^k的方法非常简单,只要通过下面的函数:
int lowbit(int x)
{
return x & (-x);
}
这是一个非常巧妙的函数,对于树状数组的某个位置x而言,x + lowbit(x)表示它的上一级的结点,即包含它的信息的结点,而x - lowbit(x)表示比它小的不被它包含的最大结点。
比如当x=2时,lowbit(x)=2,则x+lowbit(x)=4,表示它的上一级结点是4,因为4包含了4、3、2、1的信息。
当x=7时,lowbit(x)=1,则x-lowbit(x)=6,表示比它小的不被它包含的最大结点是6。
树状数组的主要操作:
1、在某个位置的结点处增加一个值:在改变了原数组中元素的值之后,需要逐步向上修改上一级结点的信息。
2、统计第一个元素到当前元素的和:设置一个表示和的树状数组,然后使用上面的lowbit函数不断查找下一个不被包含的点,把所有的和加起来即可。
3、统计区间和:使用操作2中的方法,用较大的区间减去较小的区间就得到所求区间和了。
</pre><pre name="code" class="cpp">#include <iostream>
using namespace std;
#define N 500002
typedef struct mpair
{
int data;
int id;
}mpair;
//离散化输入数组到a当中,数据顺序和输入数据保持一致
//a中存储输入数据在输入数组中的排序序号
mpair inArray[N];
int a[N];
//树状数组,累计逆序,每一位只加1即可,统计和用于统计数字个数
int c[N];
void display(mpair *array, int len)
{
int i;
for (i = 0; i < len; ++i)
{
cout << array[i].data << " ";
}
cout << endl;
}
void displayIntArray(int *array, int len)
{
int i;
for (i = 0; i < len; ++i)
{
cout << array[i] << " ";
}
cout << endl;
}
//快速排序
void quickSort(mpair *array, int st, int ed)
{
if (st < ed)
{
int left = st;
int right = ed;
mpair t = array[left];
while (left < right)
{
while (left < right && array[right].data > t.data)
{
--right;
}
if (left < right)
{
array[left++] = array[right];
}
while (left < right && array[left].data < t.data)
{
++left;
}
if (left < right)
{
array[right--] = array[left];
}
}
array[left] = t;
quickSort(array, st, left - 1);
quickSort(array, left + 1, ed);
}
}
//返回2 ^ k, k为数据t末尾0的个数,用于计算树状数组中节点i的前驱和后继节点
int lowbit(int t)
{
return t & (t ^ (t - 1));
//return t & (-t);
}
//idx节点添加数据data
void addDataTreeArray(int *c, int n, int idx, int data)
{
while (idx <= n)
{
c[idx] += data;
idx += lowbit(idx);
}
}
//0-idx区间的累加和
int getSumTreeArray(int *c, int n, int idx)
{
int sum = 0;
if (idx > n)
{
idx = n;
}
while (idx > 0)
{
sum += c[idx];
idx -= lowbit(idx);
}
return sum;
}
void mergeReverse(int *c, int st, int ed, int *a, int &count)
{
if (st < ed)
{
int mid = (st + ed) >> 1;
mergeReverse(c, st, mid, a, count);
mergeReverse(c, mid + 1, ed, a, count);
int i,j,k;
i = st;
j = mid + 1;
k = st;
while (i <= mid && j <= ed)
{
if (c[i] < c[j])
{
a[k++] = c[i++];
}
else if (c[i] > c[j])
{
a[k++] = c[j++];
++count;
}
else
{
a[k++] = c[i++];
}
}
if (i <= mid)
{
count += (mid - i) * (ed - mid);
}
while (i <= mid)
{
a[k++] = c[i++];
}
while (j <= ed)
{
a[k++] = c[j++];
}
//归并排序关键的一步必须把合并后的结果拷贝回原来的数组
memcpy(c + st, a + st, (ed - st + 1) * sizeof(int));
cout << "st = " << st << " ed = " << ed << endl;
displayIntArray(c, ed - st + 1);
cout << endl;
}
}
//归并排序算法求解
int mergeReverseNumber(int *c, int n)
{
int *a = new int[n + 1];
int count = 0;
displayIntArray(c, n);
mergeReverse(c, 0, n - 1, a, count);
displayIntArray(c, n);
cout << endl;
delete []a;
return count;
}
int main()
{
int n;
cin >> n;
int i;
//树状数组下标从1开始便于处理,0节点直接为0
for (i = 1; i <= n; ++i)
{
cin >> inArray[i].data;
inArray[i].id = i;
}
display(inArray, n + 1);
cout << endl;
quickSort(inArray, 1, n);
//离散化到数组a中,如果出现相同的数据则让离散化后的结果一致
a[inArray[1].id] = 1;
for (i = 2; i <= n; ++i)
{
if (inArray[i].data == inArray[i - 1].data)
{
a[inArray[i].id] = a[inArray[i - 1].id];
}
else
{
a[inArray[i].id] = i;
}
}
display(inArray, n + 1);
cout << endl;
displayIntArray(a, n + 1);
cout << endl;
int count = 0;
memset(c, 0, sizeof(c));
for (i = 1; i <= n; ++i)
{
//树状数组中a[i]节点数据累加1次,累加会向上一级节点延伸,相当于数据a[i]出现了一次
addDataTreeArray(c, n, a[i], 1);
//input i data, how many data low than i, and how many data large than i after i
//输入了i个数据,包括i在内(相等的)前面出现的数据(节点)个数,差值为比i大的数据个数,也即逆序数
count += i - getSumTreeArray(c, n, a[i]);
}
cout << "reverse number is : " << count << endl;
cout << endl;
cout << "merge sort reverse number is : " << mergeReverseNumber(&(a[1]), n) << endl;
return 0;
}