[LeetCode] Reverse Pairs 翻转对

Reverse Pairs 翻转对

题意

计算数组里面下标i小于j,但是i的值要大于j的值的两倍的搭配的个数(也就是可能会有多种搭配);网址

做法

这道题显然是不允许使用最简单的方法:两次循环,逐次进行判断,这样做的时间复杂度就是O(n^2),OJ无法通过,需要考虑另外的实现方式;

class Solution(object):
    def reversePairs(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        count = 0
        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                if nums[i] > 2 * nums[j]:
                    count += 1
        return count

分治法

利用分治法,将这个问题分成多个问题,每个小问题求出解后,再求出大问题的解,类似归并排序的做法。

可以设想一个数组分成两边的数组,而且两个数组还是有序的,分别记录两个下标i和j,因为i必然小于j,如果当前i的值已经大于当前j的值的*2,那么当前的i和j之前的值显然也必然满足情况,所以直接计算j从中间的位置移动的距离,这就是当前i求出来的个数,然后移动i,只要满足条件的i,就计算他们的求出来的个数,最后都累加起来,就是最终的结果。

class Solution(object):
    res = 0
    def reversePairs(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        self.merge_sort(nums, 0, len(nums)-1)
        return self.res
    def merge_sort(self, nums, left, right):
        if right <= left:
            return
        mid = (left + right) // 2
        self.merge_sort(nums, left, mid)
        self.merge_sort(nums, mid+1, right)
        # 统计个数
        count = 0
        i, j = left, mid+1
        while i <= mid:
            if j > right or nums[i] <= 2 * nums[j]:
                i += 1
                self.res += count
            else:
                j += 1
                count += 1
        nums[left:right + 1] = sorted(nums[left:right+1])

或者:

class Solution(object):
    def reversePairs(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        return self.merge_sort(nums, 0, len(nums) - 1)
    def merge_sort(self, nums, left, right):
        if left >= right:
            return 0
        mid = (left + right) // 2
        res = self.merge_sort(nums, left, mid) + self.merge_sort(nums, mid+1, right)
        i, j = left, mid+1
        while i <= mid:
            while j <= right and nums[i] > nums[j] * 2:
                j += 1
            res += j - (mid + 1)
            i += 1
        nums[left:right + 1] = sorted(nums[left:right + 1])
        return res

树状数组

树状数组的原理可以参考我的这篇博客,使用树状数组是因为其能够保证较少的时间复杂度来计算前面存在多少个符合规定的数字。

class FenwickTree(object):
    def __init__(self, n):
        self.n = n
        self.sums = [0] * (n + 1)
    def low_bit(self, x):
        return x & -x
    def add(self, x, val):
        while x <= self.n:
            self.sums[x] += val
            x += self.low_bit(x)
    def get_sum(self, x):
        result = 0
        while x > 0:
            result += self.sums[x]
            x -= self.low_bit(x)
        return result
class Solution(object):
    def reversePairs(self, nums):
        """
        :param nums: [int]
        :return: int
        """
        nums2 = [x * 2 for x in nums]
        dmap = {v: k for k, v in enumerate(sorted(set(nums + nums2)), start=1)}
        fenwick = FenwickTree(len(dmap))
        ans = 0
        for n in nums2[::-1]:
            ans += fenwick.get_sum(dmap[n/2]-1)
            fenwick.add(dmap[n], 1)
        return ans

它的做法是将数组里乘以2的值对应的下标计算出来,注意需要进行排序,这样才能保证后面BIT获取sum的时候是正确的,然后循环从后面开始是因为求的是逆序对,这样设置的时候则从后面开始设置,然后获取的sum函数意为计算nums中的值所在树中的位置前面存在多少个比n*2要大的值,然后设置的时候要设置nums2中的值,因为题目要求的就是比nums2的值要大的个数。

又或者都统一起来,将获取的index和设置的index都为nums中的值或许更容易理解一些,如下:

import bisect
class Solution(object):
    def reversePairs(self, nums):
        """
        :param nums: [int]
        :return: int
        """
        nums2 = sorted([x * 2 for x in nums])
        n = len(nums2)
        fenwick = FenwickTree(n)
        order, binary_order = {}, {}
        for i in range(n):
            binary_order[i] = bisect.bisect_left(nums2, nums[i])  # num中的值在nums2中的位置
            order[nums2[i]/2] = i+1  # nums中的值对应的位置
        ans = 0
        for i in range(n-1, -1, -1):
            ans += fenwick.get_sum(binary_order[i])  # 获取前面存在多少个数
            fenwick.add(order[nums[i]], 1)  # 设置num的位置
        return ans
posted @ 2017-10-29 12:22  banananana  阅读(838)  评论(0)    收藏  举报