剑指offer【排序】

把数组排成最小的数

  • 自定义排序:
  • 如果x+y > y+x, 那么x>y -> x放在y的后面
快速排序

https://www.runoob.com/python3/python-quicksort.html

  • 对于 i ··· l ··· j,如果 strs[j] + strs[l] >= strs[l] + strs[j],不需要换位,j 指针前移;
  • 对于 I ··· l ··· j,
class Solution:
    def minNumber(self, nums: List[int]) -> str:

        def quick_sort(l , r):
            if l >= r: return
            i, j = l, r
            while i < j:
                while strs[j] + strs[l] >= strs[l] + strs[j] and i < j: 
                    j -= 1 # 不需要换位
                while strs[i] + strs[l] <= strs[l] + strs[i] and i < j: 
                    i += 1 # 不需要换位
                # 需要互换位置
                strs[i], strs[j] = strs[j], strs[i]

            strs[i], strs[l] = strs[l], strs[i] # piovt归位

            quick_sort(l, i - 1) # 左子序列
            quick_sort(i + 1, r) # 右子序列
        
        strs = [str(num) for num in nums]
        quick_sort(0, len(strs) - 1) # 快速排序
        return ''.join(strs) # 拼接输出
内置排序函数
  • 通过定义一个sort_rule函数,对于两个输入参数xy,xy输出1
  • 常用 b = sorted(a, key=f),但是该方法只能用于function输入一个参数,入f(x) = len(x)
  • 本题的function需要两个输入,即x,y;并且需要比较他俩组成的数字大小关系,所以使用老版本方法 b = sorted(a, key=functools.cmp_to_key(f) )
class Solution:
    def minNumber(self, nums: List[int]) -> str:

        def sort_rule(x,y):
            xy = x+y
            yx = y+x
            if xy > yx: #应该输出yx,也就是x比较大,sorted给放到后面去
                return 1
            if xy< yx: # 应该输出xy,排序结果是x小,放到前面
                return -1
            else:
                return 0
        strs = [str(num) for num in nums]
        sorted_strs = sorted(strs, key=functools.cmp_to_key(sort_rule))
        return ''.join(sorted_strs)

扑克牌中的顺子

从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

  • 不考虑大小王,顺子牌应该满足的条件:(如[1,2,3,4,5])
  • 无重复的牌;最大和最小之差<5(最大只能为4)
class Solution:
    def isStraight(self, nums: List[int]) -> bool:
        joker = 0
        nums.sort() # 数组排序
        for i in range(4):
            if nums[i] == 0: joker += 1 # 统计大小王数量
            # 最终返回的最小牌为nums[joker],因为要剔除前面大小王的数量
            elif nums[i] == nums[i + 1]: return False # 若有重复,提前返回 false
        return nums[4] - nums[joker] < 5 # 最大牌 - 最小牌 < 5 则可构成顺子

最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

快速排序,返回有序数组的切片[:k]即可
class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        # 快速排序,返回数组的切片
        def quick_sort(arr,l,r):
            # l: left index,实际上当前的基准元素
            # r: right index,界
            if l >= r :return 
            # 初始化两个指针 i,j
            i,j = l,r
            while i= arr[l]: j -= 1  
                while i
优化
  • 检查基准元素当前的index,判断接下来需要排序左边还是右边
  • 如果k=i,直接返回它的左子序列就可
class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        # 快速排序,返回数组的切片
        def quick_sort(arr,l,r):
            if l >= r :return 
            i,j = l,r
            while i= arr[l]: j -= 1  
                while ii:
                quick_sort(arr,i+1,r)

        quick_sort(arr,0,len(arr)-1)
        return arr[:k]

数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]

有序列表

给定一长度为 N 的无序数组,其中位数的计算方法:首先对数组执行排序(使用 O(Nlog⁡N)时间),然后返回中间元素即可(使用 O(1) 时间)。
针对本题,根据以上思路,可以将数据流保存在一个列表中,并在添加元素时 保持数组有序 。此方法的时间复杂度为 O(N),其中包括: 查找元素插入位置 O(log⁡N)(二分查找)、向数组某位置插入元素 O(N)(插入位置之后的元素都需要向后移动一位)。

双堆定义
  • 堆A存储比较大的一半元素,堆顶是其中最小的一个元素
  • 堆B存储比较小的一半元素,堆顶是其中最大的一个元素
  • 如果N为奇数,保证堆A里面元素多一个,这样返回中位数只返回A的堆顶即可
  • 如果N为偶数,中位数 = A顶+B顶/2
插入元素
  • 为了保证上面说的AB两个堆的元素个数:
  • 如果这是第奇数个(A本身比B多一个,现在让他们一样多),向A插,再把A顶推给B,这样A个数不变,B增加了
  • 如果这是第偶数个(两边一样多,现在要让A多一个),向B插,再把B堆顶推给A,这样B个数不变,A增加了一个
复杂度
  • 查找中位数 O(1) : 获取堆顶元素使用O(1) 时间;
  • 添加数字 O(log⁡N) 堆的插入和弹出操作使用 O(log⁡N)时间。
  • 空间复杂度 O(N):元素数量
    通过自带的heapq库完成
    由于默认堆顶最大,实现A堆顶为最小的元素,需要全部取负值
class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.a = [] # 放大的一半
        self.b = [] # 放小的一半

    def addNum(self, num: int) -> None:
        if len(self.a) == len(self.b):
            # AB等长,往B加,堆顶推给A
            heappush(self.b, num)
            heappush(self.a, -heappop(self.b))
        else:
            # A比B多1,往A加,堆顶推给B
            heappush(self.a, -num)
            heappush(self.b, -heappop(self.a))
        

    def findMedian(self) -> float:
        if len(self.a) == len(self.b):
            return (-self.a[0]+self.b[0])/2.0
        return -self.a[0]

你可能感兴趣的:(剑指offer【排序】)