二维数组的花式遍历技巧盘点
學算法認準?labuladong
后臺回復?進群?進刷題
讀完本文,可以去力扣解決如下題目:
48. 旋轉圖像(中等)
54. 螺旋矩陣(中等)
59. 螺旋矩陣 II(中等)
有不少讀者說,看過很多公眾號歷史文章之后掌握了框架思維,可以解決大部分有套路框架可循的題目。
但是框架思維也不是萬能的,有一些特定技巧呢,屬于會者不難,難者不會的類型,只能通過多刷題進行總結和積累。
那么本文我分享一些巧妙的二維數組的花式操作,你只要有個印象,以后遇到類似題目就不會懵圈了。
順/逆時針旋轉矩陣
對二維數組進行旋轉是常見的筆試題,力扣第 48 題「旋轉圖像」就是很經典的一道:
題目很好理解,就是讓你將一個二維矩陣順時針旋轉 90 度,難點在于要「原地」修改,函數簽名如下:
void?rotate(int[][]?matrix)如何「原地」旋轉二維矩陣?稍想一下,感覺操作起來非常復雜,可能要設置巧妙的算法機制來「一圈一圈」旋轉矩陣:
但實際上,這道題不能走尋常路,在講巧妙解法之前,我們先看另一道谷歌曾經考過的算法題熱熱身:
給你一個包含若干單詞和空格的字符串s,請你寫一個算法,原地反轉所有單詞的順序。
比如說,給你輸入這樣一個字符串:
s?=?"hello?world?labuladong"你的算法需要原地反轉這個字符串中的單詞順序:
s?=?"labuladong?world?hello"常規的方式是把s按空格split成若干單詞,然后reverse這些單詞的順序,最后把這些單詞join成句子。但這種方式使用了額外的空間,并不是「原地反轉」單詞。
正確的做法是,先將整個字符串s反轉:
s?=?"gnodalubal?dlrow?olleh"然后將每個單詞分別反轉:
s?=?"labuladong?world?hello"這樣,就實現了原地反轉所有單詞順序的目的。
我講這道題的目的是什么呢?
旨在說明,有時候咱們拍腦袋的常規思維,在計算機看來可能并不是最優雅的;但是計算機覺得最優雅的思維,對咱們來說卻不那么直觀。也許這就是算法的魅力所在吧。
回到之前說的順時針旋轉二維矩陣的問題,常規的思路就是去尋找原始坐標和旋轉后坐標的映射規律,但我們是否可以讓思維跳躍跳躍,嘗試把矩陣進行反轉、鏡像對稱等操作,可能會出現新的突破口。
我們可以先將n x n矩陣matrix按照左上到右下的對角線進行鏡像對稱:
然后再對矩陣的每一行進行反轉:
發現結果就是matrix順時針旋轉 90 度的結果:
將上述思路翻譯成代碼,即可解決本題:
//?將二維矩陣原地順時針旋轉?90?度 public?void?rotate(int[][]?matrix)?{int?n?=?matrix.length;//?先沿對角線鏡像對稱二維矩陣for?(int?i?=?0;?i?<?n;?i++)?{for?(int?j?=?i;?j?<?n;?j++)?{//?swap(matrix[i][j],?matrix[j][i]);int?temp?=?matrix[i][j];matrix[i][j]?=?matrix[j][i];matrix[j][i]?=?temp;}}//?然后反轉二維矩陣的每一行for?(int[]?row?:?matrix)?{reverse(row);} }//?反轉一維數組 void?reverse(int[]?arr)?{int?i?=?0,?j?=?arr.length?-?1;while?(j?>?i)?{//?swap(arr[i],?arr[j]);int?temp?=?arr[i];arr[i]?=?arr[j];arr[j]?=?temp;i++;j--;} }肯定有讀者會問,如果沒有做過這道題,怎么可能想到這種思路呢?
其實我覺得這個思路還是挺容易想出來的,如果學過線性代數,這道算法題的思路本質就是矩陣變換,肯定可以想出來。
即便沒學過線性代數,旋轉二維矩陣的難點在于將「行」變成「列」,將「列」變成「行」,而只有按照對角線的對稱操作是可以輕松完成這一點的,對稱操作之后就很容易發現規律了。
既然說道這里,我們可以發散一下,如何將矩陣逆時針旋轉 90 度呢?
思路是類似的,只要通過另一條對角線鏡像對稱矩陣,然后再反轉每一行,就得到了逆時針旋轉矩陣的結果:
翻譯成代碼如下:
//?將二維矩陣原地逆時針旋轉?90?度 void?rotate2(int[][]?matrix)?{int?n?=?matrix.length;//?沿左下到右上的對角線鏡像對稱二維矩陣for?(int?i?=?0;?i?<?n;?i++)?{for?(int?j?=?0;?j?<?n?-?i;?j++)?{//?swap(matrix[i][j],?matrix[n-j-1][n-i-1])int?temp?=?matrix[i][j];matrix[i][j]?=?matrix[n?-?j?-?1][n?-?i?-?1];matrix[n?-?j?-?1][n?-?i?-?1]?=?temp;}}//?然后反轉二維矩陣的每一行for?(int[]?row?:?matrix)?{reverse(row);} }void?reverse(int[]?arr)?{?/*?見上文?*/}至此,旋轉矩陣的問題就解決了。
矩陣的螺旋遍歷
我的公眾號 動態規劃系列文章 經常需要遍歷二維dp數組,但難點在于狀態轉移方程而不是數組的遍歷,頂多就是倒序遍歷。
今天我們講一下力扣第 54 題「螺旋矩陣」,看一看二維矩陣可以如何花式遍歷:
函數簽名如下:
List<Integer>?spiralOrder(int[][]?matrix)解題的核心思路是按照右、下、左、上的順序遍歷數組,并使用四個變量圈定未遍歷元素的邊界:
隨著螺旋遍歷,相應的邊界會收縮,直到螺旋遍歷完整個數組:
只要有了這個思路,翻譯出代碼就很容易了:
List<Integer>?spiralOrder(int[][]?matrix)?{int?m?=?matrix.length,?n?=?matrix[0].length;int?upper_bound?=?0,?lower_bound?=?m?-?1;int?left_bound?=?0,?right_bound?=?n?-?1;List<Integer>?res?=?new?LinkedList<>();//?res.size()?==?m?*?n?則遍歷完整個數組while?(res.size()?<?m?*?n)?{if?(upper_bound?<=?lower_bound)?{//?在頂部從左向右遍歷for?(int?j?=?left_bound;?j?<=?right_bound;?j++)?{res.add(matrix[upper_bound][j]);}//?上邊界下移upper_bound++;}if?(left_bound?<=?right_bound)?{//?在右側從上向下遍歷for?(int?i?=?upper_bound;?i?<=?lower_bound;?i++)?{res.add(matrix[i][right_bound]);}//?右邊界左移right_bound--;}if?(upper_bound?<=?lower_bound)?{//?在底部從右向左遍歷for?(int?j?=?right_bound;?j?>=?left_bound;?j--)?{res.add(matrix[lower_bound][j]);}//?下邊界上移lower_bound--;}if?(left_bound?<=?right_bound)?{//?在左側從下向上遍歷for?(int?i?=?lower_bound;?i?>=?upper_bound;?i--)?{res.add(matrix[i][left_bound]);}//?左邊界右移left_bound++;}}return?res; }力扣第 59 題「螺旋矩陣 II」也是類似的題目,只不過是反過來,讓你按照螺旋的順序生成矩陣:
函數簽名如下:
int[][]?generateMatrix(int?n)有了上面的鋪墊,稍微改一下代碼即可完成這道題:
int[][]?generateMatrix(int?n)?{int[][]?matrix?=?new?int[n][n];int?upper_bound?=?0,?lower_bound?=?n?-?1;int?left_bound?=?0,?right_bound?=?n?-?1;//?需要填入矩陣的數字int?num?=?1;while?(num?<=?n?*?n)?{if?(upper_bound?<=?lower_bound)?{//?在頂部從左向右遍歷for?(int?j?=?left_bound;?j?<=?right_bound;?j++)?{matrix[upper_bound][j]?=?num++;}//?上邊界下移upper_bound++;}if?(left_bound?<=?right_bound)?{//?在右側從上向下遍歷for?(int?i?=?upper_bound;?i?<=?lower_bound;?i++)?{matrix[i][right_bound]?=?num++;}//?右邊界左移right_bound--;}if?(upper_bound?<=?lower_bound)?{//?在底部從右向左遍歷for?(int?j?=?right_bound;?j?>=?left_bound;?j--)?{matrix[lower_bound][j]?=?num++;}//?下邊界上移lower_bound--;}if?(left_bound?<=?right_bound)?{//?在左側從下向上遍歷for?(int?i?=?lower_bound;?i?>=?upper_bound;?i--)?{matrix[i][left_bound]?=?num++;}//?左邊界右移left_bound++;}}return?matrix; }至此,兩道螺旋矩陣的題目也解決了。
以上就是遍歷二維數組的一些技巧,其他數組技巧可參見之前的文章 前綴和數組,差分數組,數組雙指針算法集合;鏈表相關技巧可參見 單鏈表六大算法技巧匯總。?
_____________
公眾號后臺回復關鍵詞「目錄」查看精選歷史文章,另外沒關注我視頻號的讀者趕緊關注下,每周末有空直播:
總結
以上是生活随笔為你收集整理的二维数组的花式遍历技巧盘点的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 米6 / MIUI10 自带电子邮件应用
- 下一篇: 计算机二级培训ppt,计算机二级PPT真