冒泡排序的交换次数 (树状数组)

计算冒泡排序的交换次数:

逆序数概念:在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序

一个排列中所有逆序总数叫做这个排列的逆序数。 所以冒泡排序结束即是所有的逆序数为0

思路

暴力:我们可以开一个vis[]数组记录 在遍历到第 i 位时,已经出现的值有哪些。然后遍历到 arr[i] (第i位的值),得到小于arr[i] 的出现个数(即 i<j && arr[i]<= arr[j]的个数)

然后再用当前长度 i 减去符合条件个数即得打 arr[i]的逆序个数。

优化:由于每次都要对小于 arr[i]的出现元素遍历,我们就希望能够开一个数组 记录arr[i]之前出现的符合条件个数(这样就不用每次都遍历一次取求)

那么对于单点更新,求区间和 ,我们就可以借助 树状数组 这个数据结构。所以就利用 树状数组进行优化。 

操作:

求出数列种每一个数的逆序数求和
遍历a[i],a[i]再bit[i]中对应的值加一
记录每个值出现前小于自身,自身出现次数
遍历到j时a[j]前有j个数,即小于自身个数有bit[j]个
逆序数为 j - bit[j]

//计算冒泡排序的交换次数:
//逆序数概念,冒泡排序结束即是所有的逆序数为0
//思路:求出数列种每一个数的逆序数求和
//遍历a[i],a[i]再bit[i]中对应的值加一
//记录每个值出现前小于自身,自身出现次数
//遍历到j时a[j]前有j个数,即小于自身个数有bit[j]个
//逆序数为 j - bit[j]
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define mp make_pair
#define Accept 0
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double Pi = acos(-1.0);
const double esp = 1e-9;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5+7;
const int maxm = 1e6+7;
const int mod = 1e9+7;
const int MAXL = 1e6+7;

int n;
int bit[maxn];
int arr[maxn];
int vis[maxn]; 
//求和即是从i开始,将其二进制最低位的1去掉,直到变为0
//前i项和 
int sum(int i){
    int s = 0;
    while(i>0){
        s += bit[i];
        i -= i & -i;
    }
    return s;
}
//单点跟新,将第i位增加x,将最低为的非0幂加到对应的幂上
void add(int i,int x){
    //bit[i] = x; 单点修改
    //i += i & -i;
    while(i<=n){
        bit[i] += x;
        i += i & -i;
    }
}
void solve(){
    int ans = 0;
    for(int j = 0;j<n;j++){
        ans += j - sum(arr[j]);
        add(arr[j],1);
    }
//暴力的解法
// for(int i=0;i<n;i++){ // int cnt = 0; // for(int j=0;j<arr[i];j++){ // if(vis[j]) cnt++; // } // ans += i - cnt; // vis[arr[i]] =1; // } printf("%d\n",ans); } int main(){ memset(vis,0,sizeof(vis)); scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&arr[i]); } solve(); return 0; }

猜你喜欢

转载自www.cnblogs.com/Tianwell/p/11490987.html