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

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

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

一个排列中所有逆序总数叫做这个排列的逆序数。 所以冒泡排序结束即是所有的逆序数为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;
}

原文地址:https://www.cnblogs.com/Tianwell/p/11490987.html

时间: 2024-07-30 08:10:49

冒泡排序的交换次数 (树状数组)的相关文章

hdu 5775 Bubble Sort 树状数组+冒泡排序

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5775 [题意]按照题目的冒泡排序求每个数在排序过程中到达最右边位置与最左边位置之差. [解题思路]考虑i位置上的数字a[i]在冒泡排序过程的变化情况.a[i]会被其后面比a[i]小的数字各交换一次,之后a[i]就会只向前移动.数组从右向左扫,树状数组维护一下得到每个值右边有多少个比其小的值,加上原位置得到最右位置,最左位置为初始位置和最终位置的最小值. [时间复杂度]O(n lg n)O(n\ lg

POJ 2299 Ultra-QuickSort (树状数组or 归并排序分治求逆序对数)

题目大意就是说帮你给一些(n个)乱序的数,让你求冒泡排序需要交换数的次数(n<=500000) 显然不能直接模拟冒泡排序,其实交换的次数就是序列的逆序对数. 由于数据范围是 0 ≤ a[i] ≤ 999,999,999所以先要离散化,然后用合适的数据结果求出逆序 可以用线段树一步一步添加a[i],每添加前查询前面添加比它的大的有多少个就可以了. 也可用树状数组,由于树状数组求的是(1...x)的数量和所以每次添加前查询i-sum(a[i])即可 树状数组: //5620K 688MS #incl

树状数组求逆序对:POJ 2299、3067

前几天开始看树状数组了,然后开始找题来刷. 首先是 POJ 2299 Ultra-QuickSort: http://poj.org/problem?id=2299 这题是指给你一个无序序列,只能交换相邻的两数使它有序,要你求出交换的次数.实质上就是求逆序对,网上有很多人说它的原理是冒泡排序,可以用归并排序来求出,但我一时间想不出它是如何和归并排序搭上边的(当初排序没学好啊~),只好用刚学过的树状数组来解决了.在POJ 1990中学到了如何在实际中应用上树状数组,没错,就是用个特殊的数组来记录即

codeforce div2 C 树状数组

http://codeforces.com/contest/362 题目大意:给你一个序列,用冒泡排序法让他变为非递减的序列最少需要几次.在冒泡交换之间,你有一个swap操作,该swap操作是交换任意两个数组元素的位置,问在该操作后,所再需要的冒泡交换次数是多少,并输出方案数 思路:树状数组维护一下区间序列,知道该区间内比他大的有几个就行了.然后暴力. //看看会不会爆int!数组会不会少了一维! //取物问题一定要小心先手胜利的条件 #include <bits/stdc++.h> usin

poj 2299 Ultra-QuickSort(树状数组)

题意:求一个数列的冒泡排序的交换次数: 参考:http://blog.csdn.net/suwei19870312/article/details/5293694 思路: 一个数列的冒泡排序交换次数即为每个数对应的逆序对数之和,朴素的思想是两个for,O(N^2)复杂度: 数字范围是0-999999999,数组大小为500000,所以先离散化,用结构体记录原数列的下标和值: 对于第i个数,利用树状数组的结构,将数列中的数逐个插入到树状数组中并统计当前树状数组中在该数之前的数的个数num,i-nu

POJ2299 Ultra-QuickSort【树状数组】【逆序数】

题目链接: http://poj.org/problem?id=2299 题目大意: 给你一个包含N个整数的序列,只能通过交换相邻的数字,最终变为升序顺序,问:最少需要多少次交换. 思路: 其实就是问冒泡排序的交换次数.其实就是求原序列的逆序数.用归并排序.线段树.树状数组都可以做. 但是如果用线段树和树状数组来做的话,因为元素个数是500000,但是元素值范围却是999999999,需 要先离散化.这里用间接排序的方法.用一个数组Arr[]存放原序列的值,另一个数组Id[]存放原序列编号 (1

HDU - 2838 Cow Sorting (树状数组 + 逆序对)

HDU - 2838 Cow Sorting Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status Description Sherlock's N (1 ≤ N ≤ 100,000) cows are lined up to be milked in the evening. Each cow has a unique "grumpiness" lev

POJ 2299 Ultra-QuickSort(树状数组 + 离散)

链接:http://poj.org/problem?id=2299 题意:给出N个数组成的数列A(0 <= A[i] <= 999,999,999),求该数列逆序对的数量. 分析:题目所谓的排序过程其实就是一个冒泡排序的过程.在这里,我们需要知道,冒泡排序所需交换的次数等于该序列逆序对的数量(证明略).这是这道题的一个切入点. 树状数组可以很方便地求出数列的前缀和,对于一个数x,我们使树状数组上第x个元素的值赋为1,这时调用Sum(x)就可以得到一个从第1项到第x项的前缀和.这意味着我们可以通

hdu 2838 Cow Sorting 树状数组求所有比x小的数的个数

Cow Sorting Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4766    Accepted Submission(s): 1727 Problem Description Sherlock's N (1 ≤ N ≤ 100,000) cows are lined up to be milked in the evening.