Python 元組列表排序:初學者可能忽視的細節

2023-02-16 18:01:06

P1085 [NOIP2004 普及組] 不高興的津津

題目描述

津津上初中了。媽媽認為津津應該更加用功學習,所以津津除了上學之外,還要參加媽媽為她報名的各科複習班。另外每週媽媽還會送她去學習朗誦、舞蹈和鋼琴。但是津津如果一天上課超過八個小時就會不高興,而且上得越久就會越不高興。假設津津不會因為其它事不高興,並且她的不高興不會持續到第二天。請你幫忙檢查一下津津下週的日程安排,看看下週她會不會不高興;如果會的話,哪天最不高興。

輸入格式

輸入包括 \(7\) 行資料,分別表示週一到週日的日程安排。每行包括兩個小於 \(10\) 的非負整數,用空格隔開,分別表示津津在學校上課的時間和媽媽安排她上課的時間。

輸出格式

一個數位。如果不會不高興則輸出 \(0\),如果會則輸出最不高興的是周幾(用 \(1, 2, 3, 4, 5, 6, 7\) 分別表示週一,週二,週三,週四,週五,週六,週日)。如果有兩天或兩天以上不高興的程度相當,則輸出時間最靠前的一天。

樣例輸入

5 3
6 2
7 2
5 3
5 4
0 4
0 6

樣例輸出

3

解題思路

將資料記錄為元組列表,例如:[(1, 2), (3, 4), ...] ,每個元組記錄當天的資料,元組第一位為當天的上課時間,第二位為當天的日期(星期幾)。

儲存資料的方法相同,最後比較輸出結果的方法有兩種。

方法 1

li = []

for i in range(7):
    a, b = map(int, input().split())
    li.append((a+b, i+1))

li.sort()

if li[6][0] > 8:
    for i in range(6,-1,-1):
        if li[i-1][0] != li[i][0]:
            print(li[i][1])
            break
else:
    print(0)

對列表進行排序,list.sort() 預設升序排列,所以只需要判斷最後一個元組內的上課時間是否大於 8 小時,決定是否要輸出 0 ;

當上課時間的最大值大於 8 時,倒序迴圈查詢目標日期。存在上課時間相同的多個元組時,剛剛的排序操作會將日期較小的放在前面,所以需要在迴圈內去判斷 i-1 位置是否等於 i 位置。

如果不等,輸出當前元組的日期並跳出迴圈即可;如果相等,進入下一輪迴圈繼續比較。

方法 2

li = []

for i in range(7):
    a, b = map(int, input().split())
    li.append((a+b, i+1))

if max(li)[0] > 8:
    print(max(li, key=lambda li:li[0])[1])
else:
    print(0)

該方法使用 max 函數 進行最大值選取。

與剛剛的排序方法不同,max() 只輸出 元組內元素之和最大 的元組,也就是說如果存在:[(9, 3), (9, 5)] ,它會返回 (9, 5) 這個元組,我們就無法得到真正的答案 (9,3)。

這時候就需要使用指定選取最大值的方法(函數),通過設定引數 key=函數() 來實現,函數部分實現 返回元組內第一個元素 即可,此處使用了匿名函數 lambda ,對應普通函數為:

def takeFirst(li):
    return li[0]

對於 key 的理解,我最初走入了一個誤區:li[0] 不是列表 li 的第一個元素嗎,應該是一個元組, 這怎麼能用來排序啊?

抱著這個疑問我查閱了一些資料,大多說的比較籠統,直到看到這篇文章: 理解sort()函數中的key ,我終於是理解了。

key 關鍵字指向的函數,會在排序前先執行。換句話說,它會在遍歷迭代物件時,對迭代物件中的 每一個元素 進行一次操作。

假如有列表 li = [(1,2), (5,6), (3,4)] ,對其進行排序(sort函數 和 max函數 都可以設定 key ),設定 key為上方的 takeFirst() 函數。

當對 li 進行排序時,首先遍歷列表中的元素(元組),對每個元素均執行 takeFirst() 函數,也就是說選出 [1, 5, 3] 來,對其進行排序得到 [1, 3, 5] ,再將這個順序對應到 li 上,就可以得到排序後的列表 li = [(1,2), (3,4), (5,6)] ,至此排序完成。

寫在最後

第一次遇到 key 引數時(刷題筆記 - 1043.[程式設計入門]三個數位的排序),只是簡單的記錄了一下,並沒有過多深究。但知識就是這樣,今天無視它,改天它就掐住了你的脖子。

順便摘抄一段最近看到的話:

「我非常喜歡的一個東西,是一個人十三四歲的夏天,在路上撿到一支真槍。因為年少無知,天不怕地不怕,他扣下扳機。沒有人死,也沒有人受傷。他認為自己開了空槍。後來他三十歲或者更老,走在路上聽到背後有隱隱約約的風聲。他停下來,回過身去,子彈正中眉心。」

「果然,在那明媚的陽光中傳來了那一聲槍響。那槍聲沉悶之極。」

——《我與地壇》史鐵生