leetcode, python,

[Leetcode] Python刷題黑魔法

Tony Tony Follow Mar 06, 2023 · 4 mins read
[Leetcode] Python刷題黑魔法
Share this

分享一些 Python3 刷 Leetcode 的小技巧,善用這些技巧可以讓code變 得更簡潔,也可以讓解題過程更加舒適潤滑,當然這些技巧有人喜歡,也有人 不喜歡,姑且稱之它為黑魔法吧。

獲取List中第一個滿足條件的元素

next 搭配 generator comprehension 可以獲取 list 中第一個滿足條件的元素,例如以下的例子會拿到 arr 中的第一個正數

# 拿到第一個正數
next(ele for ele in arr if ele > 0)

要注意的是當arr中的元素都不符合條件時,會raise StopIteration,因此在符合條件的元素可能不存在的場景要慎用。

對稱反轉

假如題目有兩個以上的參數,且參數的大小順序會影響解題過程時,可以在函數的開頭先做一次檢查,確保 在解題過程中冗餘且重複的參數判斷。

舉個例子,以下的寫法可以確保解題過程中,一定滿足 a <= b。

def foo(a, b):
    if a > b: return foo(b, a)
    ...

另一個做法是直接在函數的入口處swap參數

def foo(a, b):
    if a > b: a, b = b, a
    ...

flatten dict

在python的dict,key可以使用tuple,因此在巢狀dict的情境,可以用tuple組成composition key的方式,像是 d[k1, k2, k3] 簡化巢狀dict d[k1][k2][k3]

Packing & Unpacking

Packing & Unpacking 可以用來取出需要的參數,例如:

arr = [1, 2, 3, 4, 5]

a, *inter, b = arr
# a == 1
# inter == [2, 3, 4]
# b == 5
可以得到 foo == 1, B == [2, 3, 4], bar == 5

另外還可以用巢狀 unpacking, 例如以下的unpacking在平面座標問題時很常用,

# dirs = [(x1, y1), (x2, y2), ...]
for idx, (x, y) in enumerate(dirs):
    ...

early return

在某些問題,input可能為空值,且當input為空值時會影響函數的執行時,可以利用early return的方式處理。
例如,linked list,tree,matrix等都有可能遇到輸入為空值的情況。當linked list的node為None時,如果繼續進 行以下的node.right或node.left時就會出現exception,因此要先判斷node是否可以操作。


def foo(input):
    if not input:
        return ...

另外,list或是matrx有可能是空列表的情況也可以比照判理。檢查matrix是否為空的小技巧

if not any(matrix):
    return

iterator

遇到sequence或是向後查找的題目,可以使用while加參數自己控制怎麼遍歷,或者是利用iterator的特性。
舉個例子,如果想知道s2是不是s1的subsequence時,可以這麼做:

def is_subsequence(s1, s2):
    return all(c in iter(s1) for c in s2)

上面的例子,假如s1 = “aabbcc”,s2 = “abc”,當c=a時,第一個 c in iter(s1),c=”a”會停在idx=0的地方,第二個 c in iter(s1), c=”b” 從idx=1的位置接續查找,並且停在idx=2的位置,以此類推,最後就能得到我們想要的結果。

這個技巧算是前面的 next 用法進階版。

兩數字是否同正負號

a ^ b > 0

flatten list

想要攤平巢狀 list 可以使用以下工具

list comprehension: A = [ele for sub in arr for ele in sub]
itertools: A = list(itertools.chain.from_iterable(arr))
reduce: A = functools.reduce(operator.iconcat, arr, [])

好用的builtin func

python提供許多好用的builtin func,例如operator.itemgetter可以取代 labmda x: x[1]

from operator import itemgetter
sorted(arr, key=itemgetter(1))
# 等價於 sorted(arr, key=lambda x: x[1])

反向遍歷

因為 python 的 list 提供 negative indexing, 如果要反向遍歷時,可以用 ~i 來獲得對應於 i 的反向 indexing,像是:

for i in range(len(arr)):
    arr[i] += xxx  # arr[0],  arr[1],  arr[2] , ...
    arr[~i] += ooo # arr[-1], A[-2], arr[-3], ...

zip

zip 函數的作用是將 iterables 的元素打包配對,例如

a = "abcd"
b = (1, 2, 3, 4, 5)
c = [6, 7, 8, 9, 0]
d = zip(a, b, c)

# d: [('a', 1, 6), ('b', 2, 7), ('c', 3, 8), ('d', 4, 9), (None, 5, 0)]
# 元素的長度可以不一樣,空值會zip會補None

在matrix的問題裡,用zip會有奇效,例如,旋轉矩陣

/*
 * clockwise rotate
 * first reverse up to down, then swap the symmetry
 * 1 2 3     7 8 9     7 4 1
 * 4 5 6  => 4 5 6  => 8 5 2
 * 7 8 9     1 2 3     9 6 3
*/

正常寫法

def rotate(self, matrix):
    n = len(matrix)
    matrix.reverse()
    for i in xrange(n):
        for j in xrange(i, n):
            matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

但如果用 zip 的話,只需要一行code

def rotate(self, matrix):
    return matrix[:] = zip(*matrix[::-1])
Join Newsletter
Get the latest news right in your inbox. We never spam!
Tony
Written by Tony Follow
Hi, I am Tony, the author of Learning Journey blog. I hope you like what I sharing!