今天的內容很簡單,我們有一個序列,如何找到該序列中最大或者最小的N個元素?千萬別走開,看到後面會有乾貨分享哦!
一個例子
l = [1,2,4,3,6,0,5,7,9]
該列表中的組大值
>>>print(max(l))
9
該列表中的最小值
>>>print(min(l))
問題來了,我們想知道該列表中最大的3個值或者最小的3個值該怎麼辦呢?
一般方法
# 先對序列進行排序
sorted(l)
# 然後列印輸出
>>>print("前三個:{},後三個:{}".format(l[:3], l[-3:]))
前三個:[1, 2, 4],後三個:[5, 7, 9]
成功實現。
但僅僅這樣就可以了嗎?我們的乾貨還沒出現呢?先給大家推薦一入門書,裡面有很多案例,涉及一些pygame模塊的實戰項目,感興趣的朋友們關注下,喜歡閱讀正版Python書的朋友們可以入手哦。
接著上面的內容。我們繼續……例子擴展
上面的簡單列表我們通過排序可以實現,但是複雜一點的列表呢?
複雜列表如下:
scoreInfo=[
{'name':'Lucy','en_score': 89.6,'math_scroe':94.1},
{'name':'Bob','en_score': 72.4,'math_scroe':84.2},
{'name':'LiLei','en_score': 82.6,'math_scroe':74.1},
{'name':'HanMeimei','en_score': 65.6,'math_scroe':86.9},
{'name':'Lily','en_score': 78.1,'math_scroe':65.8},
{'name':'Tracy','en_score': 72.6,'math_scroe':65.4},
]
按照英語成績'en_score'對學生進行排名,找出前3名和後3名。
解決方案
仍然使用上面的排序思路:先將列表按照每一項中的'en_score'進行排序,然後輸出前後三個即可。對於字典排序,我們前面講過(參見雜亂無章的數據結構如何進行排序,簡明講述Python字典排序那些事)
實現如下:
t_scoreInfo = sorted([item for item in scoreInfo], key=lambda x: x['en_score'], reverse=True)
>>>print('前三個:{}\n後三個:{}'.format(t_scoreInfo[:3], t_scoreInfo[-3:]))
前三個:[{'name': 'Lucy', 'en_score': 89.6, 'math_scroe': 94.1}, {'name': 'LiLei', 'en_score': 82.6, 'math_scroe': 74.1}, {'name': 'Lily', 'en_score': 78.1, 'math_scroe': 65.8}]
後三個:[{'name': 'Tracy', 'en_score': 72.6, 'math_scroe': 65.4}, {'name': 'Bob', 'en_score': 72.4, 'math_scroe': 84.2}, {'name': 'HanMeimei', 'en_score': 65.6, 'math_scroe': 86.9}]
成功完成!
這樣就完了?
上面的方法貌似都可以實現該需求,還有別的方案嗎?我們知道,碰到類似的問題,應該向內置函數求解。沒錯,內置函數提供了這樣的方法。
導入模塊
import heapq
簡單序列
l = [1,2,4,3,6,0,5,7,9]
>>>print('前三:{}'.format(heapq.nlargest(3, l)))
前三:[9, 7, 6]
>>>print('後三:{}'.format(heapq.nsmallest(3, l)))
後三:[0, 1, 2]
複雜序列
還是scoreInfo列表
high =heapq.nlargest(3,scoreInfo,key=lambdas:s['en_score'])
low = heapq.nsmallest(3, scoreInfo, key=lambda s: s['en_score'])
>>>print("前三:{}\n後三:{}".format(high, low))
前三:[{'name': 'Lucy', 'en_score': 89.6, 'math_scroe': 94.1}, {'name': 'LiLei', 'en_score': 82.6, 'math_scroe': 74.1}, {'name': 'Lily', 'en_score': 78.1, 'math_scroe': 65.8}]
後三:[{'name': 'HanMeimei', 'en_score': 65.6, 'math_scroe': 86.9}, {'name': 'Bob', 'en_score': 72.4, 'math_scroe': 84.2}, {'name': 'Tracy', 'en_score': 72.6, 'math_scroe': 65.4}]
完美解決,沒錯,你沒看錯,都是一行代碼!
heapq模塊
上面的方法中nlargest()和nsmallest()函數的底層實現是:先將序列數據進行堆排序後放入一個列表中,本質上來講也是多種方法的封裝。
需要強調的是,當我們使用type查看上面的high和low的類型時,返回的是<class 'list'>。因此,通過傳入序列,此函數使用到了堆結構特性去處理該序列,而返回結果類型依然是該序列本身的類型。
對於一個堆heap的數據結構有以下優點:
heap[0]永遠是最小的元素其餘元素可通過調用 heapq.heappop()方法得到,該方法會先將第一個元素彈出來,然後用下一個最小的元素來取代被彈出元素(這種操作時間複雜度僅僅是O(log N),N是堆大小)通過上面的描述,如果查找最小的三個元素,其實等價於分別從堆結構中使用heappop()方法彈出3個值即可。
我們先來詳細了解下模塊heapq提供的接口有哪些……
【如何創建堆】
方法一:使用heappush()方法
heap = []
data = [2,3,5,7,9,23,14,16,12,10]
for i in data:
heapq.heappush(heap,i)
>>>print(heap)
[2, 3, 5, 7, 9, 23, 14, 16, 12, 10]
此時,其實並沒有進行排序
方法二:使用heapify()方法
data = [2,3,5,7,9,23,14,16,12,10]
heapq.heapify(data)
此時,直接變換data數據為堆結構,並沒有返回新的數據
【堆如何排序】
方法一:
使用heapq.nlargest()和heapq.nsmallest()方法即可實現。
heapq.nXXXest(num, set, key)
num:表示返回數據的個數
set:表示要處理的序列(當然集合最好,沒有重複元素)
key:表示排序規則
方法二:
我們使用heappop()可以彈出heap中的數據。此時,彈出的數據就是排序後的數據。
lst = []
while heap:
lst.append(heapq.heappop(heap))
>>>print(lst)
[2, 3, 5, 7, 9, 10, 12, 14, 16, 23]
怎麼樣,是不是很神奇?
能讀到這裡,說明你真的喜歡Python,想學習點乾貨,看下面……
各種排序場合應用
對heapq使用場合給出幾點建議:
查找(排序)元素個數相對序列較小時,函數 nlargest()和 nsmallest()是最佳選擇;查找序列唯一的最小、最大的元素,推薦使用min()和max()函數最快。查找(排序)元素個數和序列個數接近時,先排序、再切片,這樣會更快。
一個問題
創建一個簡單堆,使用heapify(lst)即可。
如果需要把複雜的列表,如上面的scoreInfo直接傳入heapify(scoreInfo)這樣是會拋出異常的,異常信息為:
「TypeError: '<' not supported between instances of 'dict' and 'dict'」
這個該如何解決呢?小夥伴們有沒有好辦法實現?歡迎下方留下寶貴意見。
好了,今天的內容就到這裡了,我們通過一個簡單的排序案例,講解了利用堆結構排序的方法。小夥伴們Get到這個技能了嗎?上面的問題有沒有小夥伴會的?希望不吝賜教,大家共同學習進步。喜歡Python編程的小夥伴關注我,後續有更加精彩的內容推出。
轉載請註明出處,百家號:Python高手養成