求解逆序对的三种方法

剑指 Offer 51. 数组中的逆序对
此题数据范围为 [ 0 , 50000 ] [0, 50000] [0,50000],理论上说最多可能存在 50000 × 49999 2 = 1 , 249 , 975 , 000 \frac{50000\times 49999}{2}=1,249,975,000 250000×49999=1,249,975,000 个逆序对,因此用答案用 int 存储绰绰有余。

解法一:归并排序

归并排序的交换次数即为逆序对数目

class Solution {
    int ans;
    vector<int> tmp;
    void mergeSort(vector<int> &a, int L, int R) {
        if (L >= R) return;
        int m = L+R >> 1;
        mergeSort(a, L, m);
        mergeSort(a, m+1, R);
        int i = L, j = m+1, t = L;
        while (i <= m && j <= R) {
            if (a[j] < a[i]) {
                tmp[t++] = a[j++];
                ans += m+1-i;
            }
            else tmp[t++] = a[i++];
        }
        while (i <= m) tmp[t++] = a[i++];
        while (--t >= L) a[t] = tmp[t];
    }
public:
    int reversePairs(vector<int>& nums) {
        ans = 0;
        int n = nums.size();
        tmp.resize(n);
        mergeSort(nums, 0, n-1);
        return ans;
    }
};
解法二:离散化+树状数组(或线段树)

不断将第 i 个数字按顺序放入树状数组中,每次查询前 i 个数字中大于该数的数字个数。

之所以要离散化,是因为数组中的数字大小可能是 1e9 级别的。

class BinaryIndexedTree {
    vector<int> C;
public:
    BinaryIndexedTree(int n) : C(n+1) {
    }
    void add(int x, int val) {
        while (x < C.size()) {
            C[x] += val;
            x += x&-x;
        }
    }
    int getSum(int x) {
        int sum = 0;
        while (x) {
            sum += C[x];
            x -= x&-x;
        }
        return sum;
    }
};

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        vector<int> mp(n+1);
        int t = 0;
        for (int &num : nums) {
            mp[++t] = num;
        }
        sort(mp.begin()+1, mp.begin()+1+t);
        t = unique(mp.begin()+1, mp.begin()+1+t)-mp.begin()-1;
        int ans = 0;
        BinaryIndexedTree bit(n);
        for (int &num : nums) {
            num = lower_bound(mp.begin()+1, mp.begin()+1+t, num)-mp.begin();
            ans += bit.getSum(n)-bit.getSum(num);
            bit.add(num, 1);
        }
        return ans;
    }
};
解法三:红黑树(插入和查询)

维护树的 s i z e size size 信息可快速查询比 n u m num num 大的数字的个数.
n n n 次查询,每次查询时间复杂度 O ( log ⁡ n ) O(\log n) O(logn) ,总时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

enum color_t {red, black};
class RedBlackTree {
    struct node {
        node *left, *right, *parent;
        color_t color;
        int val;
        int size;
    };
    node *nil;
    node *root;

    // 创建一个size为1的红色节点
    node* createNode(int x, node *parent) {
        node *t = new node;
        t->color = red;
        t->parent = parent;
        t->left = t->right = nil;
        t->val = x;

        t->size = 1;
        return t;
    }
    void rotateLeft(node *z) {
        node *p = z->parent;
        node *y = z->right;

        // 处理p的指针域
        if (p == nil) root = y;
        else if (z == p->left) p->left = y;
        else p->right = y;

        // 处理z的指针域
        z->parent = y;
        z->right = y->left;
        y->left->parent = z;
        
        // 处理y的指针域
        y->parent = p;
        y->left = z;

        // 维护节点数据
        y->size = z->size;
        z->size -= y->right->size + 1;
    }
    void rotateRight(node *z) {
        node *p = z->parent;
        node *x = z->left;

        // 处理p的指针域
        if (p == nil) root = x;
        else if (z == p->left) p->left = x;
        else p->right = x;
        
        // 处理z的指针域
        z->parent = x;
        z->left = x->right;
        x->right->parent = z;

        // 处理x的指针域
        x->parent = p;
        x->right = z;

        // 维护节点数据
        x->size = z->size;
        z->size -= x->left->size + 1;
    }

    void fixup(node *z) {
        node *p, *q;
        while (z->parent->color != black) {  // z的父节点是红色才会导致冲突
            p = z->parent;
            if (p == p->parent->left) {
                q = p->parent->right;
                if (q->color == red) {  // case1: parent and uncle both red
                    p->color = q->color = black;
                    p->parent->color = red;
                    z = z->parent->parent;
                    continue;
                }
                if (z == z->parent->right) {  // case2: uncle is black and z is right
                    z = z->parent;
                    rotateLeft(z);
                }
                z = z->parent->parent;  // case3: uncle is black and z is left
                rotateRight(z);
                swap(z->color, z->parent->color);
                break;
            }
            else {
                q = p->parent->left;
                if (q->color == red) {  // symmetric case1: parent and uncle both red
                    p->color = q->color = black;
                    p->parent->color = red;
                    z = z->parent->parent;
                    continue;
                }
                if (z == z->parent->left) {  // symmetric case2: uncle is black and z is left
                    z = z->parent;
                    rotateRight(z);
                }
                z = z->parent->parent;  // symmetric case3: uncle is black and z is right
                rotateLeft(z);
                swap(z->color, z->parent->color);
                break;
            }
        }
        root->color = black;
    }
public:
    RedBlackTree() {
        nil = new node;
        nil->color = black;
        nil->size = 0;
        root = nil;
    }
    ~RedBlackTree() {
        // 后序遍历析构红黑树
        node *z = root;
        node *tmp = nullptr;  // 记录最后析构的节点
        while (z != nil) {
            while (z->left != nil) z = z->left;  // 有左孩子就遍历左子树,递归返回时遍历该节点
            if (z->right != nil) z = z->right;  // 没有左孩子但有右孩子
            else {  // 叶节点开始回溯
                do {
                    // cout << z->val;
                    tmp = z;
                    z = z->parent;
                    delete tmp;
                    if (z == nil) break;
                } while (z->right == tmp || z->right == nil);
                if (z != nil) z = z->right;
            }
        }
        delete nil;
    }
    void insert(int x) {
        node *p = nil, *t = root;
        while (t != nil) {
            p = t;
            ++t->size;
            if (x < t->val) t = t->left;
            else t = t->right;
        }

        node *px = createNode(x, p);
        if (p == nil) root = px;
        else if (x < p->val) p->left = px;
        else p->right = px;
        
        fixup(px);
    }
    int countBigger(int x) {
        node *p = root;
        int tot = 0;
        while (p != nil) {
            if (p->val <= x) p = p->right;
            else {
                tot += p->right->size + 1;
                p = p->left;
            }
        }
        return tot;
    }
};


class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int ans = 0;
        RedBlackTree rbt;
        for (int num : nums) {
            ans += rbt.countBigger(num);
            rbt.insert(num);
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值