一文解决逆序数


计算逆序数有两种思路,一种是统计归并排序过程中交换的次数,另一种是根据rank逐个修改位置计数,当前rank的逆序数等于[rank + 1, n]的区间和
leetcode有一个弱模板题

class Solution:
    def reversePairs(self, record: List[int]) -> int:
        a = record
        if a == []:
            return 0
        global ans
        ans = 0
        def change(nums1, nums2):
            global ans
            p1 = 0
            p2 = 0
            n = len(nums1)
            m = len(nums2)
            res = []
            while p1 < n and p2 < m:
                if nums1[p1] > nums2[p2]:
                    res.append(nums2[p2])
                    p2 += 1
                    ans += (n - p1)
                else:
                    res.append(nums1[p1])
                    p1 += 1
            res.extend(nums1[p1: ])
            res.extend(nums2[p2: ])
            return res
        def merge(nums):
            n = len(nums)
            if n == 1:
                return nums
            left = 0
            right = n - 1
            mid = (left + right) // 2
            l = merge(nums[: mid + 1])
            r = merge(nums[mid + 1: ])
            return change(l, r)
        merge(a)
        return ans

一般来说面试像上面那么写就行了,但是这种归并写法会不断复制列表导致开销是很大,如果是竞赛题这么写就不行,需要用索引归并,最后复制一次
这种做法也是我试过python唯一能过洛谷模板题的写法,洛谷很多题给的时间都把python卡掉了,这种专注竞赛的平台几乎都是c++专场

class Solution:
    def reversePairs(self, record: List[int]) -> int:
        a = record
        if a == []:
            return 0
        global ans
        ans = 0
        res = [0] * len(a)
        def change(l, mid, r):
            global ans
            p1 = l
            p2 = mid + 1
            n = mid - l + 1
            k = l
            while p1 <= mid and p2 <= r:
                if a[p1] > a[p2]:
                    res[k] = a[p2]
                    p2 += 1
                    ans += (n - p1 + l)
                else:
                    res[k] = a[p1]
                    p1 += 1
                k += 1
            while p1 != mid + 1:
                res[k] = a[p1]
                k += 1
                p1 += 1
            while p2 != r + 1:
                res[k] = a[p2]
                k += 1
                p2 += 1
            for i in range(l, r + 1):
                a[i] = res[i]
        def merge(left, right):
            if left >= right:
                return
            mid = (left + right) // 2
            merge(left, mid)
            merge(mid + 1, right)
            change(left, mid, right)
        merge(0, len(a) - 1)
        return ans

第二种做法需要单点修改和区间求和,写了线段树和树状数组两种写法
线段树:

class Solution:
    def reversePairs(self, record: List[int]) -> int:
        a = record
        class segment_tree:
            def __init__(self, nums):
                n = len(nums)
                self.nums = nums
                self.tree = [0] * 4 * n

            def update(self, root):
                self.tree[root] = self.tree[root * 2] + self.tree[root * 2 + 1]

            def build(self, left, right, root):
                if left > right:
                    return
                if left == right:
                    self.tree[root] = self.nums[left - 1]
                    return
                mid = (left + right) // 2
                self.build(left, mid, root * 2)
                self.build(mid + 1, right, root * 2 + 1)
                self.update(root)

            def change(self, A, v, left, right, root):
                if left > right:
                    return
                if left == right:
                    self.tree[root] = v
                    return 
                mid = (left + right) // 2
                if left <= A <= mid:
                    self.change(A, v, left, mid, root * 2)
                if mid + 1 <= A <= right:
                    self.change(A, v, mid + 1, right, root * 2 + 1)
                self.update(root)

            def query(self, A, B, left, right, root):
                if A <= left <= right <= B:
                    return self.tree[root]
                mid = (left + right) // 2
                res = 0
                if A <= mid:
                    res += self.query(A, B, left, mid, root * 2)
                if B >= mid + 1:
                    res += self.query(A, B, mid + 1, right, root * 2 + 1)
                return res
        def get_ranks(nums):
            new_nums = sorted(list(set(nums)))
            hp = {v: r + 1 for r, v in enumerate(new_nums)}
            return hp
        hp = get_ranks(a)
        new_a = [0] * len(hp)
        tree = segment_tree(new_a)
        tree.build(1, len(hp), 1)
        ans = 0
        for i in a:
            p = hp[i]
            if p + 1 <= len(hp):
                ans += tree.query(p + 1, len(hp), 1, len(hp), 1)
            temp = tree.query(p, p, 1, len(hp), 1)
            tree.change(p, temp + 1, 1, len(hp), 1)
        return ans

树状数组:

class Solution:
    def reversePairs(self, record: List[int]) -> int:
        a = record
        class binary_indexed_tree:
            def __init__(self, nums):
                self.n = len(nums)
                self.nums = nums
                self.tree = [0] * (self.n + 1)

            def lowbit(self, x):
                return x & -x

            def change(self, pos, v):
                while pos <= self.n:
                    self.tree[pos] += v
                    pos += self.lowbit(pos)

            def query(self, pos):
                res = 0
                while pos > 0:
                    res += self.tree[pos]
                    pos -= self.lowbit(pos)
                return res

        def get_ranks(nums):
            new_nums = sorted(list(set(nums)))
            hp = {v: r + 1 for r, v in enumerate(new_nums)}
            return hp

        hp = get_ranks(a)
        new_a = [0] * len(hp)
        tree = binary_indexed_tree(new_a)
        ans = 0
        for i in a:
            p = hp[i]
            ans += tree.query(len(hp)) - tree.query(p)
            temp = tree.query(p) - tree.query(p - 1)
            tree.change(p, 1)
        return ans

树状数组单点查询还有优化的写法,暂且不表,按前缀和处理比较容易懂