【leetcode】排序问题总结

LeetCode215题 数组中的第K个最大元素

堆排序

把待排序数组视为数组表示的完全二叉树,然后自底向上不断调整,使其成为一个大顶堆(升序排序)/小顶堆(降序排序),之后可以通过不断弹出堆顶元素进行排序

建堆过程时间复杂度为O(n),堆排序过程时间复杂度为O(nlogn)

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        n = len(nums)
        # 从第一个父节点调整堆,进而构建大顶堆
        for i in range((n - 1) // 2, -1, -1):
            self.adjustHeap(nums, i, n)
        # 堆排序
        for i in range(1, k + 1):
            nums[0], nums[-i] = nums[-i], nums[0]
            self.adjustHeap(nums, 0, n - i)
        return nums[-k]

    def adjustHeap(self, nums, idx, n):
        left = 2 * idx + 1
        right = 2 * idx + 2
        max_idx = idx
        if left < n and nums[max_idx] < nums[left]:
            max_idx = left
        if right < n and nums[max_idx] < nums[right]:
            max_idx = right
        if max_idx != idx:
            nums[idx], nums[max_idx] = nums[max_idx], nums[idx]
            self.adjustHeap(nums, max_idx, n)

归并排序

归并排序是在递归的后序位置进行自底向上的排序,会有logn次的“并”,每次“并”的时间复杂度是O(n),所以整体时间复杂度为O(nlogn)

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        return self.mergeSort(nums)[-k]

    def mergeSort(self, nums):
        if len(nums) <= 1:
            return nums
        mid = len(nums) // 2
        left = self.mergeSort(nums[:mid])
        right = self.mergeSort(nums[mid:])
        cur_left = 0
        cur_right = 0
        r = []
        while cur_left < len(left) and cur_right < len(right):
            if left[cur_left] <= right[cur_right]:
                r.append(left[cur_left])
                cur_left += 1
            else:
                r.append(right[cur_right])
                cur_right += 1
        if cur_left < len(left):
            r = r + left[cur_left:]
        if cur_right < len(right):
            r = r + right[cur_right:]
        return r

快速排序

快速排序是在递归的前序位置进行自顶向下的排序,平均时间复杂度为O(nlogn),但由于快速排序在极端情况下复杂度为O(n^2),本题中会超时

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        return self.quickSortK(nums, 0, len(nums) - 1, len(nums) - k)

    def quickSortK(self, nums, left, right, k):
        pivot = nums[right]
        pivot_index = left # 代表比pivot小的最后一个元素索引 +1
        for i in range(left, right):
            if pivot > nums[i]:
                nums[pivot_index], nums[i] = nums[i], nums[pivot_index]
                pivot_index += 1
        nums[pivot_index], nums[right] = nums[right], nums[pivot_index]
        if k == pivot_index:
            return nums[k]
        elif k < pivot_index:
            return self.quickSortK(nums, left, pivot_index - 1, k)
        else:
            return self.quickSortK(nums, pivot_index + 1, right, k)

LeetCode315题 计算右侧小于当前元素的个数

归并排序的应用:归并排序过程中的左右区间附带着元素位置信息,可以在“并”的过程中累加左区间元素的结果

class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        self.res = [0] * len(nums)
        self.mergeSortProcess([(i, j) for i, j in enumerate(nums)])
        return self.res

    def mergeSortProcess(self, nums):
        if len(nums) <= 1:
            return nums
        mid = len(nums) // 2
        left = self.mergeSortProcess(nums[:mid])
        right = self.mergeSortProcess(nums[mid:])
        cur_left = 0
        cur_right = 0
        r = []
        while cur_left < len(left) and cur_right < len(right):
            if left[cur_left][1] <= right[cur_right][1]:
                self.res[left[cur_left][0]] += cur_right
                r.append(left[cur_left])
                cur_left += 1
            else:
                r.append(right[cur_right])
                cur_right += 1
        if cur_left < len(left):
            for i in left[cur_left:]:
                self.res[i[0]] += len(right)
            r = r + left[cur_left:]
        if cur_right < len(right):
            r = r + right[cur_right:]
        return r

LeetCode327题 区间和的个数

前缀和数组 + 归并排序 + 双指针的应用:

  • 构建前缀和数组后,任意的区间和都可以表示为归并排序过程中的right[j] - left[i] / right[j];
  • 且对于有序的left、right,可以用双指针技巧计算right[j] - left[i] / right[j]是否在区间[lower,upper]中,时间复杂度为O(n),这样整体的复杂度就是O(nlogn),与归并排序相同
class Solution:
    def countRangeSum(self, nums: List[int], lower: int, upper: int) -> int:
        # 构建前缀和数组
        for i in range(1, len(nums)):
            nums[i] += nums[i - 1]
        self.res = 0
        self.mergeSort(nums, lower, upper)
        return self.res

    def mergeSort(self, nums, lower, upper):
        if len(nums) <= 1:
            if len(nums) == 1 and lower <= nums[0] <= upper:
                self.res += 1
            return nums
        mid = len(nums) // 2
        left = self.mergeSort(nums[:mid], lower, upper)
        right = self.mergeSort(nums[mid:], lower, upper)
        cur_l = cur_r = 0
        cur_left = cur_right = 0
        r = []
        while cur_left < len(left) and cur_right < len(right):
            if left[cur_left] <= right[cur_right]:
                r.append(left[cur_left])
                cur_left += 1
                while cur_l < len(right) and right[cur_l] - r[-1] < lower:
                    cur_l += 1
                while cur_r < len(right) and right[cur_r] - r[-1] <= upper:
                    cur_r += 1
                self.res += cur_r - cur_l
            else:
                r.append(right[cur_right])
                cur_right += 1
        if cur_left < len(left):
            for v in left[cur_left:]:
                r.append(v)
                while cur_l < len(right) and right[cur_l] - r[-1] < lower:
                    cur_l += 1
                while cur_r < len(right) and right[cur_r] - r[-1] <= upper:
                    cur_r += 1
                self.res += cur_r - cur_l
        if cur_right < len(right):
            r = r + right[cur_right:]
        return r
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值