【leetcode】排序数组

Solution 1: 归并法

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        # Solution 1: 归并排序
        n = len(nums)
        # 递归结束条件:数字中只有一个元素,返回数组自己
        if n == 1:
            return nums
        # 分成左右两个数组
        mid = int(n/2)
        sorted_left = self.sortArray(nums[0:mid]) # 递归调用给左侧数组排序
        sorted_right = self.sortArray(nums[mid:]) # 递归调用给右侧数组排序
        
        # 合并排序后左侧数组和右侧数组
        i, j, n_left, n_right = 0, 0, len(sorted_left), len(sorted_right)
        sorted_num = []
        while i < n_left and j < n_right:
            if sorted_left[i] <= sorted_right[j]:
                sorted_num.append(sorted_left[i])
                i += 1
            else:
                sorted_num.append(sorted_right[j])
                j += 1
        if i == n_left and j < n_right:
            # 右侧数组没有遍历完
            while j < n_right:
                sorted_num.append(sorted_right[j])
                j += 1
        elif i < n_left and j == n_right:
            # 左侧数组没有遍历完
            while i < n_left:
                sorted_num.append(sorted_left[i])
                i += 1
        return sorted_num

这次手撕代码一次写完,没有bug,顺利通过,太开心了

  • 时间复杂度:O(NlogN)
  • 空间复杂度:O(N)

怎么分析时间复杂度和空间复杂度也是有些讲究的,请见官方题解
https://leetcode-cn.com/problems/sort-an-array/solution/pai-xu-shu-zu-by-leetcode-solution/

合并排序后的左侧数组和右侧数组,定义成一个子函数,修改下代码的写法如下:

class Solution:
	'''
	定义子函数:合并排序后的左侧数组和右侧数组
	'''
    def mergeSortNum(self, sorted_left: List[int], sorted_right: List[int]) -> List[int]:
        # 合并排序后左侧数组和右侧数组
        i, j, n_left, n_right = 0, 0, len(sorted_left), len(sorted_right)
        sorted_num = []
        while i < n_left and j < n_right:
            if sorted_left[i] <= sorted_right[j]:
                sorted_num.append(sorted_left[i])
                i += 1
            else:
                sorted_num.append(sorted_right[j])
                j += 1
        if i == n_left and j < n_right:
            # 右侧数组没有遍历完
            while j < n_right:
                sorted_num.append(sorted_right[j])
                j += 1
        elif i < n_left and j == n_right:
            # 左侧数组没有遍历完
            while i < n_left:
                sorted_num.append(sorted_left[i])
                i += 1
        return sorted_num

    def sortArray(self, nums: List[int]) -> List[int]:
        
        # Solution 1: 归并排序
        n = len(nums)
        # 递归结束条件:数字中只有一个元素,返回数组自己
        if n == 1:
            return nums
        # 分成左右两个数组
        mid = int(n/2)
        sorted_left = self.sortArray(nums[0:mid]) # 递归调用给左侧数组排序
        sorted_right = self.sortArray(nums[mid:]) # 递归调用给右侧数组排序
        sorted_num = self.mergeSortNum(sorted_left, sorted_right) # 合并排序后的左侧数组和右侧数组

        return sorted_num

Solution 2: 快排

class Solution:
	'''
	定义子函数:随机挑选target,快排,返回target的位置
	'''
    def randomPartition(self, nums: List[int]):
        # 随机挑选一个target
        n = len(nums)
        target = random.randint(0, n-1)
        nums[n-1], nums[target] = nums[target], nums[n-1]

        # 从头开始遍历nums
        pos = -1
        for i in range(0, n-1):
            if nums[i] <= nums[n-1]:
                pos += 1
                nums[pos], nums[i] = nums[i], nums[pos]
        pos += 1
        nums[pos], nums[n-1] = nums[n-1], nums[pos]
        return pos

    def sortArray(self, nums: List[int]) -> List[int]:
        n = len(nums)
        if n == 1 or n == 0:
            return nums
            
        pos = self.randomPartition(nums)
        sorted_left = self.sortArray(nums[0:pos])
        sorted_right = self.sortArray(nums[pos+1:])
        
        res = []
        res.extend(sorted_left)
        res.append(nums[pos])
        res.extend(sorted_right)

        return res

快排算法的思想有点巧妙,时间复杂度和空间复杂度请参考题解吧

Solution 3 : 堆排序

  • 关键知识点(1):一般用数组来表示堆,下标为 i 的结点的父结点下标为(i-1)/2;其左右子结点分别为 (2i + 1)、(2i + 2)
    对应代码Note(1)
    参考文献:https://zhuanlan.zhihu.com/p/124885051

  • 关键知识点(2):即节点总数/2(根节点索引为0),这也就是第一个叶子节点,所以第一个非叶子节点的索引就是第一个叶子结点的索引-1。那么对于填不满的二叉树呢?这个计算方式仍然适用
    对应代码Note(2)

参考链接:
https://blog.csdn.net/qq_28063811/article/details/93034625

class Solution:
    def heapify(self, heap: List[int], i: int, n: int):
        # n = len(heap)
        # i -> root

        left = 2*i + 1 # Note(1)关键知识点(1)
        right = 2*i + 2
        swap = i # 默认要交换的节点是i
        if left < n and heap[left] > heap[swap]:
            # 如果存在左节点且左节点数字比swap大,将swap更新成left
            swap = left
        if right < n and heap[right] > heap[swap]:
            # 如果存在右节点且右节点数字比swap大,将swap更新成right
            swap = right
        if swap != i:
            # 如果swap更新,那么互换swap和i的数字
            heap[swap], heap[i] = heap[i], heap[swap]
            # 互换之后,如果swap位置还有子节点,重复heapfity操作
            # print('swap', swap, nums)
            self.heapify(heap, swap, n)

    def buildMaxHeap(self, heap: List[int]):
        n = len(heap)
        # print('build', nums, n)
        # Note(2) 从第一个有子节点的节点开始,调整节点的顺序,参见关键知识点(2)
        for i in range(int(n/2)-1, -1, -1):
            # print('build round', i)
            self.heapify(heap, i, n)

    def sortArray(self, nums: List[int]) -> List[int]:
        # 初始化最大堆
        self.buildMaxHeap(nums)
        for i in range(len(nums)-1, 0, -1):
            nums[0], nums[i] = nums[i], nums[0] # 堆顶元素
            # 调整最大堆
            # print(i, nums, nums[0:i])
            self.heapify(nums, 0, i) # Note(*)
        return nums
  • Note(*)这里很关键,涉及到python函数传参的问题,如果写成
self.heapify(nums[0:i], 0, i)

注意第一个参数是nums[0:i]而不是nums,那么运行函数heapifty不会改变nums
如果调用

self.buildMaxHeap(nums)

则会超出时间限制,没有必要build,只要heapify第0个元素就实现了

基础的排序算法是很重要的,也要清楚它们的时间复杂度和空间复杂度分析~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值