• 归并排序及其拓展应用


    参考:
    归并排序详解及应用

    相关题目:
    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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    # 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 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    
    # 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 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    # 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 
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
  • 相关阅读:
    go实现复杂度与简单排序算法
    flink生成水位线记录方式--周期性水位线生成器
    Vue3留言墙项目——主体部分静态、mock
    MySQL项目迁移华为GaussDB PG模式指南
    2022年6月电子学会Python等级考试试卷(三级)答案解析
    使用任务表,实现两个数据库表数据迁移
    RK3566 linux系统硬件复位后无法重启
    FTP服务配置
    SpringCloudGateway--自动路由映射
    【飞桨PaddleSpeech语音技术课程】— 语音识别-定制化识别
  • 原文地址:https://blog.csdn.net/qq_32275289/article/details/133501353