算法:分治/归并排序

    思路

        利用分治的思想,归并排序时当前下标i后有多少个元素要把位置提到i之前就是其逆序数,

        在排序过程中记录逆序数的个数。⬅️其实比较难发现这个特征我觉着

 

        比如左右已经排序好的两个序列left = [-7,1,5,9],right = [-2,1,3,5]

            观察左右两个序列,以下描述中left中的值用i索引,right中的值用j索引

            对left和right中的元素,每个元素在当前位置的逆序数都是0,因为在本列表中,已经是

        从小到大排列的了,意味着右边不会有比自身小的数,但是二者进行归并排序的时候,两个数组

        之间会有大小的比较,进而产生逆序数

            在归并排序中,举例进行比较,

            当排序运行到过程i,j位置时

            如果left[i] <= right[j]:

                1. 在res中插入较小的元素left[i],res.append(left[i])

                2. left[i]在left中的逆序数是0,因为left已排序,i后面的肯定比它大,所以此时

                看j在right中的情况,当left[i] <= right[j]时,因为归并排序是谁小谁就

                在前面,第j个位置的right比righ中的前j个元素大,即在前面的多次比较中,轮到

                第j个元素时,left[i] 才小于等于right[j],说明之前left[i]都是大于right[0..j-1]的,

                否则在j之前left[i]就插入了,所以此时left[i]的逆序数就是right[j]的下标j

                3. 这里把等于的情况也放在这里是因为,对于两个元素相等时,逆序数的计算是和小于<时

                一样,所以把它放在<=这里,>的情况单独是else的另一类

                    其实以等于=的情况来看更清晰,在第i,j个位置处二者才相等,说明在right中该值

                    大于j个元素,那么left[i]的逆序数自然就是j了,而把相等的left[i]放进来后,后面right[j]

                    再进其逆序数也就自然而然是0了(res插入left[i]后,i++1,left和right中都至少大于

                    等于right[j]了)

            否则left[i] > right[j]:

                1. 在res中插入较小的元素right[i],res.append(right[j])

                2. 由于right已经排序,right[j]必然小于j以后的元素,在right中逆序数是0,

                又因为right[j]比left[i]还要小,left中后面还会进行比较的元素一定大于left[i],

                也就是说j这个位置的话,日后不论right还是left,都比当前j要大,逆序数是0

 

            当排序后left还有剩余的话,说明剩余的left元素每个元素都大于整个j的长度,

                如left=[5,6,7,8,9],right=[1,2,3,4,5],排序过程中一直插入right的值,最后left剩下的元素

                逆序数值+= right最后的j值,这个例子中最后j加到了5(所以才超出while的条件)

 

        注意

                1. 排序的过程中会打乱下标,而最后返回的是原下标对应的逆序数计数值,所以在排序中行程原nums的pair

            对(nums[i],i)

                2. 逆序数的添加是+=,而不是单纯的 = ,上面的例子听起来似乎是用=,是因为这只是一次归并排序,实际上

            归并排序会迭代很多层,每一次迭代都是会让left和right变成一个新的排序后的组,而刚才也分析过了,对已经排序

            后的组,组内每个元素的逆序数是0,所以它们的逆序数值事实上是上一轮归并前计算的,所以每一层迭代下来逆序数

            的统计是+=

 

    复杂度分析

        时间:ONlogN,归并排序的时间

        空间:ON,存储逆序数数组的大小

✨✨✨LeetCode 315 Hard 逆序数计算 Python

✨✨✨LeetCode 315 Hard 逆序数计算 Python

✨✨✨LeetCode 315 Hard 逆序数计算 Python

✨✨✨LeetCode 315 Hard 逆序数计算 Python

✨✨✨LeetCode 315 Hard 逆序数计算 Python

def countSmaller( nums):
    def mergesort(left, right, count):
        result = []
        i = 0
        j = 0
        while i < len(left) and j < len(right):
            if left[i][0] <= right[j][0]:
                result.append(left[i])
                count[left[i][1]] += j
                i += 1
            else:
                result.append(right[j])
                j += 1
        for ii in range(i,len(left)):
            count[left[ii][1]] += j
            result.append(left[ii])
        if j < len(right):
            result.extend(right[j:])
        return result

    def merge(nums, count):
        if len(nums) < 2:
            return nums
        half = len(nums) // 2
        left = merge(nums[:half],count)
        right = merge(nums[half:],count)
        return mergesort(left, right, count)

    pairs = [(nums[i], i) for i in range(len(nums))]
    count = [0]*len(nums)
    merge(pairs, count)
    return count

 

相关文章:

  • 2022-12-23
  • 2021-11-17
  • 2021-04-15
  • 2021-08-01
  • 2022-12-23
  • 2022-12-23
  • 2021-07-05
  • 2021-10-15
猜你喜欢
  • 2021-12-31
  • 2021-11-30
  • 2022-01-01
  • 2021-11-18
  • 2022-12-23
  • 2022-01-03
  • 2022-12-23
相关资源
相似解决方案