剑指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,x<y输出-1,x>y输出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<j:
                # 从右向左找第一个小于基准的 arr[j]
                # 从左向右找第一个大于基准的 arr[i]
                while i<j and arr[j] >= arr[l]: j -= 1  
                while i<j and arr[i] <= arr[l]: i += 1 
                # 把所有这种情况的pairs进行换位
                arr[i],arr[j] = arr[j],arr[i]

            # 最终i,j相遇,所有小的都在左侧,大的都在右侧
            # 把基准元素arr[l]放到当前的位置arr[i]
            arr[i],arr[l] = arr[l],arr[i]

            # 再分别对左右子序列进行排序 (当前arr[i]不需要考虑了)
            quick_sort(arr,l,i-1)
            quick_sort(arr,i+1,r)


        quick_sort(arr,0,len(arr)-1)
        return arr[:k]
优化
  • 检查基准元素当前的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<j:
                while i<j and arr[j] >= arr[l]: j -= 1  
                while i<j and arr[i] <= arr[l]: i += 1 
                arr[i],arr[j] = arr[j],arr[i]

            # 把基准元素arr[l]放到当前的位置arr[i]
            arr[i],arr[l] = arr[l],arr[i]

            # 判断当前基准元素所在的index,如果刚好是k,直接返回它左边的子序列
            if k == i:
                return(arr[:i])
            if k<i:
                quick_sort(arr,l,i-1)
            if k>i:
                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]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • 1. 二维数组中的查找 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,...
    deactivateuser阅读 1,646评论 0 3
  • 剑指offer 剑指 Offer 15. 二进制中1的个数[https://leetcode-cn.com/pro...
    scuwen阅读 174评论 0 0
  • 剑指offer线性数据结构 剑指offer是找工作开始后刷的第一本书,刷题用牛客网。这本书可以说是已经总结归纳的很...
    锅锅Iris阅读 996评论 0 2
  • 技术交流QQ群:1027579432,欢迎你的加入! 欢迎关注我的微信公众号:CurryCoder的程序人生 1....
    CurryCoder阅读 1,844评论 0 2
  • 剑指Offer系列 [TOC] 数组和字符串 剑指offer 04.二维数组中的查找 从左下角开始查找,二分思想。...
    SwiftGo阅读 422评论 0 1