相关题目:
912. 排序数组
315. 计算右侧小于当前元素的个数
493. 翻转对
327. 区间和的个数

归并排序其实就是二叉树的后序遍历,左右子树分别排序(sort),然后再后序遍历位置合并(merge)两个左右子树。

利用左右子树有序的特征,在merge阶段可以适当改进来做一些拓展应用,这里罗列的 leetcode 315题、leetcode 493题、leetcode 327题,均是利用该框架,详细见代码:

# 912. 排序数组
class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        # 辅助数组,开辟内存空间
        self.temp = [None] * len(nums)
        # 归并排序对数组进行原地排序
        self.sort(nums, 0, len(nums)-1)
        return nums

    # 归并排序 
    def sort(self, arr, lo, hi):
        if lo == hi:
            return 
        mid = lo + (hi-lo)//2
        self.sort(arr, lo, mid)
        self.sort(arr, mid+1, hi)
        self.merge(arr, lo, mid, hi)

    # 合并两个有序数组 arr[lo..mid] 和 arr[mid+1..hi]
    def merge(self, arr, lo, mid, hi):
        # 将两个有序数组复制到 辅助数组上
        for i in range(lo, hi+1):
            self.temp[i] = arr[i]
        
        i, j = lo, mid+1
        for p in range(lo, hi+1):
            if i == mid+1:
                # 左半边数组已全部被合并
                arr[p] = self.temp[j]
                j += 1
            elif j == hi+1:
                # 右半边数组已全部被合并
                arr[p] = self.temp[i]
                i += 1

            elif self.temp[i] > self.temp[j]:
                arr[p] = self.temp[j]
                j += 1
            else:
                arr[p] = self.temp[i]
                i += 1

# 315. 计算右侧小于当前元素的个数
class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        class Pair:
            def __init__(self, val, idx):
                self.val = val
                self.idx = idx

        # 归并排序 
        def sort(arr, lo, hi):
            if lo == hi:
                return 
            mid = lo + (hi-lo)//2
            sort(arr, lo, mid)
            sort(arr, mid+1, hi)
            merge(arr, lo, mid, hi)
        
        # 合并两个有序数组 arr[lo..mid] 和 arr[mid+1..hi]
        def merge(arr, lo, mid, hi):
            # 将两个有序数组复制到 辅助数组上
            for i in range(lo, hi+1):
                temp[i] = arr[i]
            
            i, j = lo, mid+1
            for p in range(lo, hi+1):
                if i == mid+1:
                    # 左半边数组已全部被合并
                    arr[p] = temp[j]
                    j += 1
                elif j == hi+1:
                    # 右半边数组已全部被合并
                    arr[p] = temp[i]
                    i += 1
                    # 更新count数组
                    count[arr[p].idx] += hi-mid
                elif temp[i].val > temp[j].val:
                    arr[p] = temp[j]
                    j += 1
                else:
                    arr[p] = temp[i]
                    i += 1
                    # 更新count数组
                    count[arr[p].idx] += j-mid-1
        
        # 归并排序要用到的辅助数组
        temp = [Pair(0, 0) for _ in range(len(nums))]
        # 记录每个元素后面比自己小的元素个数
        count = [0 for _ in range(len(nums))]

        arr = [Pair(nums[idx], idx) for idx in range(len(nums))]
        # 执行归并排序
        sort(arr, 0, len(nums)-1)

        return count 


# 493. 翻转对
class Solution:
    
    def reversePairs(self, nums: List[int]) -> int:
        self.res = 0

        # 归并排序 
        def sort(arr, lo, hi):
            if lo == hi:
                return 
            mid = lo + (hi-lo)//2
            sort(arr, lo, mid)
            sort(arr, mid+1, hi)
            merge(arr, lo, mid, hi)
        
        # 合并两个有序数组 arr[lo..mid] 和 arr[mid+1..hi]
        def merge(arr, lo, mid, hi):
            
            # 将两个有序数组复制到 辅助数组上
            for i in range(lo, hi+1):
                temp[i] = arr[i]
            
            # 在合并有序数组之前,加点私货
            end = mid + 1
            for i in range(lo, mid+1):
                # 对于左半边的每个 nums[i],都去右半边寻找符合条件的元素
                while end<=hi and nums[i] > 2*nums[end]:
                    end += 1
                
                # 更新全局变量
                self.res += (end - (mid+1))


            i, j = lo, mid+1
            for p in range(lo, hi+1):
                if i == mid+1:
                    # 左半边数组已全部被合并
                    arr[p] = temp[j]
                    j += 1
                elif j == hi+1:
                    # 右半边数组已全部被合并
                    arr[p] = temp[i]
                    i += 1

                elif temp[i] > temp[j]:
                    arr[p] = temp[j]
                    j += 1
                else:
                    arr[p] = temp[i]
                    i += 1

        
        # 归并排序要用到的辅助数组
        temp = [None for _ in range(len(nums))]
        
        # 执行归并排序
        sort(nums, 0, len(nums)-1)

        return self.res 

# 327. 区间和的个数
class Solution:
    def countRangeSum(self, nums: List[int], lower: int, upper: int) -> int:

        self.res = 0
        self.lower = lower
        self.upper = upper

        preSum = [0] * (len(nums) + 1)
        for i in range(len(nums)):
            preSum[i + 1] = nums[i] + preSum[i]
        

        # 归并排序 
        def sort(arr, lo, hi):
            if lo == hi:
                return 
            mid = lo + (hi-lo)//2
            sort(arr, lo, mid)
            sort(arr, mid+1, hi)
            merge(arr, lo, mid, hi)
        
        # 合并两个有序数组 arr[lo..mid] 和 arr[mid+1..hi]
        def merge(arr, lo, mid, hi):
            
            # 将两个有序数组复制到 辅助数组上
            for i in range(lo, hi+1):
                temp[i] = arr[i]
            
            # 在合并有序数组之前,加点私货
            start = end = mid + 1
            for i in range(lo, mid+1):
                # 维护左闭右开区间 [start, end) 中的元素和 nums[i] 的差在 [lower, upper] 中
                while start<=hi and arr[start]-arr[i] < self.lower:
                    start += 1
                
                while end<=hi and arr[end]-arr[i] <= self.upper:
                    end += 1
                
                # 更新全局变量
                self.res += (end - start)


            i, j = lo, mid+1
            for p in range(lo, hi+1):
                if i == mid+1:
                    # 左半边数组已全部被合并
                    arr[p] = temp[j]
                    j += 1
                elif j == hi+1:
                    # 右半边数组已全部被合并
                    arr[p] = temp[i]
                    i += 1

                elif temp[i] > temp[j]:
                    arr[p] = temp[j]
                    j += 1
                else:
                    arr[p] = temp[i]
                    i += 1

        
        # 归并排序要用到的辅助数组
        temp = [None for _ in range(len(preSum))]
        
        # 执行归并排序
        sort(preSum, 0, len(preSum)-1)

        return self.res 


10-03 09:19