本文中所涉及的**,在未特殊宣告的情況下,都是基於python3程式語言編寫的。
如果您未掌握知識提要中的內容,建議您先掌握這些內容之後再閱讀本文。
知識提要
0、列表乘法:list_obj * n
1、列表生成:[exp for v in seq_obj]
0
問題描述
實現乙個函式,給定乙個m x n的矩陣matrix,按照順時針螺旋順序遍歷,返回遍歷結果。要求不能改變matrix的內容。
例如:
給定 matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
]輸出:[1,2,3,4,8,12,11,10,9,5,6,7]
螺旋遍歷的特點是:從外圈到內圈逐圈遍歷;每乙個圈,頂邊從左到右步進,右邊從上到下步進,底邊從右到左步進,左邊從下到上步進。所以這個問題的關鍵點在於確定每一圈每一條邊的長度和四條邊分別遍歷時,拐點的位置和和方向。
1
邊長步進法
邊長步進法
仔細觀察示圖,我們可以發現如下兩個規律:
a、每次橫向邊走一次,下一次橫向邊的步進數減1;每次縱向走一次,下一次縱向邊的步進數減1。
例如第一圈頂邊的步進數為8,第一圈底邊的步進數為7,第二圈頂邊的步進數為6,第二圈底邊的步進數為5。縱向邊也有同樣的規則,第一圈右邊的步進數為5,第一圈左邊的步進數為4,第二圈右邊的步進數為3,第二圈左邊的步進數為2。
b、頂邊的步進方向為正,即1;右邊的步進方向為正,即1;頂邊的步進方向為負,即-1;左邊的步進方向為負,即-1。
而且當橫座標步進不為0時,縱座標步進為0;當縱座標步進不為0時,橫座標步進為0。每4條邊乙個迴圈。所以我們可以用兩個陣列來表示每條邊橫座標(列)和縱座標(行)每一圈在四條邊的步進方向。
# 列步進方向
# 頂,右,底,左
cds=[1, 0, -1, 0]
# 行步進方向
# 頂,右,底,左
rds=[0, 1, 0, -1]
在**實現中,拐彎的次數和正在遍歷的邊是有對應關係的。拐彎次數為0是正在遍歷頂邊;拐彎次數為1時正在遍歷右邊;拐彎次數為2時正在遍歷底邊;拐彎次數為3時正在遍歷左邊;拐彎次數為4時正在遍歷頂邊;依此迴圈,每4次拐彎一次交替。所以我們記錄了拐彎的次數,就可以推算出正在遍歷的邊,自然也就可以得到當前行和列的步進方向。
def spiralorder(matrix):
if not matrix:
return
cds = [1, 0, -1, 0] # 列座標步進方向
rds = [0, 1, 0, -1] # 行座標步進方向
# 行列當前元素數量(步進數)
rs, cs = len(matrix), len(matrix[0])
turns = 0 # 拐彎次數
r, c = 0, -1 # 當前元素座標
elem_num = rs * cs
result = [0] * elem_num # 結果
index = 0 # 當前結果下標
while index < elem_num:
di = turns % 4 # 步進方向下標
# 每次拐彎,交替使用行和列的步進
steps = cs if turns % 2 == 0 else rs
for i in range(steps):
r += rds[di]
c += cds[di]
result[index] = matrix[r][c]
index += 1
# 每兩次拐彎,行列的步進數遞減
if turns % 2 == 0:
cs -= 1
rs -= 1
turns += 1
return result
事實上,列和行的步進方向cd和rd在每次拐彎中,是符合如下迭代規則的:
cd, rd = -rd, cd
這個規則是很容易推算的,這裡我不作詳細講解,留給各位讀者作為思考題。
另外,上邊的**實現進行了兩次turns == 0 的判定,我們可以把它們合併成乙個。基於這兩個事實,我們可以把上邊的**實現優化成如下的樣子。
def spiralorder(matrix):
if not matrix:
return
cd, rd = 1, 0 # 列和行步進方向
rs, cs = len(matrix), len(matrix[0])
turns = 0
r, c = 0, -1
elem_num = rs * cs
result = [0] * elem_num
index = 0
while index < elem_num:
if turns % 2 == 0:
steps = cs
cs -= 1
rs -= 1
else:
steps = rs
for i in range(steps):
r += rd
c += cd
result[index] = matrix[r][c]
index += 1
turns += 1
# 每次拐彎,重新決定行列步進方向
cd, rd = -rd, cd
return result
2邊界標記法我們可以用乙個額外的矩陣來標記每乙個元素是否已經被遍歷。我們在遍歷某一條邊的時候,總是朝著乙個固定的方向走,如果遇到了邊界,或者發現下乙個元素已經被遍歷,那麼就是該拐彎的時候了。示圖給出了**中visited矩陣的示例圖,元素t表示已經被訪問過,f表示未被訪問。
visited矩陣
每次拐彎,行列方向的規則與「邊長步進法」的一致。
def spiralorder(matrix):
if not matrix:
return
cd, rd = 1, 0 # 列和行的步進方向
rows, cols = len(matrix), len(matrix[0])
# 用於記錄每個元素的訪問狀態
visited = [[false] * cols for _ in range(rows)]
r, c = 0, 0
elem_num = rows * cols
result = [0] * elem_num
for i in range(elem_num):
result[i] = matrix[r][c]
visited[r][c] = true
# 下一步的座標nr和nc
# 如果不越界,或者未被訪問,則下個座標就是nr和nc
# 否則拐彎
nr, nc = r + rd, c + cd
if 0 <= nr < rows and 0 <= nc < cols and not visited[nr][nc]:
r, c = nr, nc
else:
cd, rd = -rd, cd # 重算拐彎後的步進方向
r, c = r + rd, c + cd
return result
雖然這個方法比較容易理解和直觀,但它需要額外的空間複雜度為o(mn),用於儲存visited矩陣。
python3輸入輸出
模 式 描 述 r以唯讀方式開啟檔案。檔案的指標將會放在檔案的開頭。這是預設模式。rb以二進位制格式開啟乙個檔案用於唯讀。檔案指標將會放在檔案的開頭。這是預設模式。r 開啟乙個檔案用於讀寫。檔案指標將會放在檔案的開頭。rb 以二進位制格式開啟乙個檔案用於讀寫。檔案指標將會放在檔案的開頭。w開啟乙個檔...
排序演算法 python3
a 8,2,7,5,1,9,6,4,3 point 1 dk len a while true dk dk 2 for n in range dk point dk n while point index 1 for i in range n,point,dk if temp a i index i...
Python3 輸入輸出筆記
encoding utf 8 有多組輸入資料,但組數不確定 while true try a,b map int,input split 以空格隔開 print a b map int,list 把每個list的元素轉為int型 except eoferror 捕捉異常 break 先輸入乙個整數,...