python 螺旋数组_LeetCode54,螺旋矩阵,一题学会一个重要技巧
今天是LeetCode專題的第32篇文章,我們一起看的是LeetCode的第54題——Spiral Matrix。
首先解釋一下題意,這個Spiral是螺旋的意思,據說英文版的漫畫里,把鳴人的螺旋丸就翻譯成Spiral Sphere...
走遠了,回歸正傳。通過螺旋丸我們都知道螺旋形是什么意思,所以所謂的螺旋矩陣,就是按照螺旋形的順序來遍歷一個數組,或者說矩陣。我們可以看下下圖:
箭頭標注的順序就是螺旋的順序,這道題讓我們求的就是按照螺旋的順序遍歷之后的結果。
背景
這道題的題意非常簡單,我想大家肯定都能看明白,但是真的要上手去做會發現還是蠻困難的。主要的難點就是我們遍歷的順序一直在變化,并且變化的速度也是在變化的。雖然從某種程度上來說,這些變化都是有跡可循的,但是即便如此,把這些規律抽象出來寫成簡單易懂沒有bug的代碼也并不是一件容易的事情。
如果我沒有記錯的話,當年我大二的時候學校里的acm校賽有一題就是這個,一模一樣的原題。雖然非常簡單,每個人都知道怎么做,但是最后的通過率并不高。
這并不完全是出題人的惡意,其實這類問題在acm的比賽當中還是很常見的。經常會有一些題目很清晰明確,你只需要照著實現就可以了的題目。考察的就是選手的抽象和編碼能力,雖然題意不難,但是在比賽高壓的場景下想要快速寫出一個幾百行包含一系列復雜邏輯的程序并且沒有bug,還是非常困難的一件事。由于這類題目的關鍵就是讓我們模擬出來題目的意思,所以也被稱為模擬題。
想要比較順手地寫出這道題,需要一個很常用的技巧或者說慣例,這也是這篇文章的關鍵。
方向數組
在許多問題當中我們經常會遇到這樣一個問題,比如我們需要遍歷一個迷宮,需要沿著現實世界當中上下左右或者是東南西北的方向進行移動。在移動的過程當中自然就涉及各種各樣的方向,于是我們需要用代碼來表示方向。
比如我們畫一個小人,它所在的坐標是(x, y),它有東南西北四個方向可以選。我們假設他每次移動一個單位的距離,我們很容易就得出它往各個方向移動之后的新坐標。
根據數學上向量的定義,我們可以寫出這四個方向的方向單位向量:[0, 1], [0, -1], [1, 0], [-1, 0]。
我們把這些向量存放進一個常量數組當中,就得到了方向數組。當我們需要遍歷所有方向的時候,我們只需要遍歷這個數組即可。
不僅如此,如果我們對方向的遍歷順序有要求,它也完全可以實現。比如在這題當中,我們可以發現,螺旋遍歷每一次改變順序其實都是向左轉了90度。
原本朝東的旋轉之后變成了朝南,朝南的變成了朝西,朝西的成了朝北,而朝北的成了朝東。那么我們只需要把方向按照東南西北的順序擺放,我們每次把方向數組的下標增加一位并對4取模,就得到了轉向之后的下一個方向。
這個技巧并不難理解,但是可以大大簡化我們的代碼。
解答
理解了方向數組之后剩下的就容易多了,我們觀察一下上面螺旋遍歷的過程,每一次改變方向遍歷的長度雖然不同,但是轉向的原因是一致的,就是這個方向上已經遍歷到頭了,所以我們需要變更方向。明白了這點其實就很容易了,我們只需要維護每個方向上的終點,每次到終點則進行變向。由于矩陣當中元素的數量是固定的,我們遍歷的次數也就知道了,所以只要把變更方向的事情處理好,這道題也就解決了。
我們來看下代碼:
class?Solution:????def?spiralOrder(self,?matrix:?List[List[int]])?->?List[int]:????????#?定義方向數組????????fx?=?[[0,?1],?[1,?0],?[0,?-1],?[-1,?0]]????????n?=?len(matrix)????????if?n?==?0:????????????return?[]????????m?=?len(matrix[0])????????????????ret?=?[]????????#?定義邊界數組????????#?邊界數組和旋轉的順序也是對應的????????#?第一次旋轉之后上邊界+1,所以第0位是上邊界????????#?第二次旋轉之后,右邊界-1????????#?以此類推????????condition?=?[0,?m-1,?n-1,?0]????????x,?y,?dt?=?0,?0,?0????????for?i?in?range(n?*?m):????????????ret.append(matrix[x][y])????????????x_,?y_?=?x?+?fx[dt][0],?y?+?fx[dt][1]????????????#?如果已經越過邊界了,則需要轉向????????????if?x_??condition[2]?or?y_??condition[1]:????????????????#?更新邊界????????????????if?dt?in?(1,?2):????????????????????condition[dt]?-=?1????????????????else:????????????????????condition[dt]?+=?1????????????????????????????????????#?轉向,并且重新移動????????????????dt?=?(dt?+?1)?%?4????????????????x_,?y_?=?x+fx[dt][0],?y+fx[dt][1]????????????????#?print(x_,?y_)????????????x,?y?=?x_,?y_????????????????????return?ret我們觀察一下代碼,還有一個我們剛才沒有提到的細節。我們在移動的時候,并不是直接在原變量上進行變更,因為如果一旦移動超界或者觸發其他非法的情況,那么我們就無法得知之前的位置了。所以我們會創建新的x和y的變量來表示移動之后的位置,即使移動到了非法位置,也不會影響之前的結果。這也是一個常用的技巧,在Python當中,我們在變量末尾加上下劃線表示這是一個影子(克隆)變量。
總結
我個人認為今天的題目出得不錯,初學者很有必要親自動手做一下。因為在做題的過程當中我們可以很具體地學到方向數組這個很有用的解題技巧,它在搜索問題當中廣泛使用,可以說是做算法題必須會的技巧之一。
可能你會覺得我們通過邊界判斷是否需要轉向的邏輯看起來有些復雜,這并不是必須的。我們也可以使用一些其他方法來代替,比如我們可以開辟一個數組記錄已經遍歷過的位置來代替邊界的判定,和使用邊界判定的方法相比,這樣做消耗的空間要更大一些,并且通過邊界判定的方法更加考驗思維一些,因此我個人比較推薦。當然,這題還有一些其他的方法,比如找規律什么的,不是特別巧妙,就不占用大家時間了。
今天的文章就是這些,如果覺得有所收獲,請順手點個關注或者轉發吧,你們的舉手之勞對我來說很重要。
總結
以上是生活随笔為你收集整理的python 螺旋数组_LeetCode54,螺旋矩阵,一题学会一个重要技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 猜一猜清明美食青团的绿色最早取自?蚂蚁庄
- 下一篇: 青岛发布大风橙色预警!98斤女生差点被9