树状数组求逆序对

3  5  4  8  2  6  9

大体思路为:新建一个数组,将数组中每个元素置0

0  0  0  0  0  0  0

取数列中最大的元素,将该元素所在位置置1

0  0  0  0  0  0  1

统计该位置前放置元素的个数,为0

接着放第二大元素8,将第四个位置置1

0  0  0  1  0  0  1

统计该位置前放置元素的个数,为0

继续放第三大元素6,将第六个位置置1

0  0  0  1  0  1  1

统计该位置前放置元素的个数,为1

这样直到把最小元素放完,累加每次放元素是该元素前边已放元素的个数,这样就算出总的逆序数来了

在统计和计算每次放某个元素时,该元素前边已放元素的个数时如果一个一个地数,那么一趟复杂度为O(n),总共操作n趟,复杂度为O(n^2),和第一种方法的复杂度一样了,那我们为什么还用这么复杂的方法

当然,在每次统计的过程中用树状数组可以把每一趟计数个数的复杂度降为O(logn),这样整个复杂度就变为O(nlogn)

 

树状数组是一种很好的数据结构,这有一篇专门描述树状数组的文章

将序列中的每个数按照从大到小的顺序插入到树状数组中,给当前插入节点及其父节点的个数加1,然后统计该节点下边及右边放置元素的个数

//用树状数组求逆序数  
const int Length=8;  
struct Node  
{  
    int data;  
    int pos;  
};  
int LowBit(int num)  
{  
    return num&(num^(num-1));  
}  
//自定义排序方法  
int cmp(const void * num1,const void * num2)  
{  
    return ((Node*)num2)->data-((Node *)num1)->data;  
}  
//统计pos节点下方及右方节点元素的个数  
int sum(int *TreeArray,int pos)  
{  
    int result=0;  
    while (pos)  
    {  
        result+=TreeArray[pos];  
        pos-=LowBit(pos);  
    }  
    return result;  
}  
//使pos位置的节点及其父节点的元素个数加1  
void INC(int *TreeArray,int pos)  
{  
    while(pos<Length)  
    {  
        TreeArray[pos]++;  
        pos+=LowBit(pos);  
    }  
}  
void insertNum(int *TreeArray,Node * seq,int &reverseNum)  
{  
    for (int i=1;i<Length;i++)  
    {  
        reverseNum+=sum(TreeArray,seq[i].pos);  //累加逆序数  
        INC(TreeArray,seq[i].pos);    //将该节点及父节点的数加1  
    }  
}  
int main(int argc, char* argv[])  
{  
    int array[]={3,5,4,8,2,6,9};  
    Node seq[Length];  
    int TreeArray[Length];  
    memset(TreeArray,0,sizeof(TreeArray));  
    for (int i=1;i<Length+1;i++)  
    {  
        seq[i].data=array[i-1];  
        seq[i].pos=i;  
    }  
    //从大到小排序  
    qsort(seq+1,Length-1,sizeof(Node),cmp);  
    int reverseNum=0;  
    //边插入边计数  
    insertNum(TreeArray,seq,reverseNum);  
    cout<<reverseNum<<endl;  
    return 0;  
}  


你可能感兴趣的:(树状数组求逆序对)