一起刷 leetcode 之旋轉矩陣(頭條/華為/陌陌真題)

2021-02-26 每天曬白牙

微信公眾號:[每天曬白牙]
關注可了解更多的編程知識。問題或建議,請公眾號留言;
如果你覺文章對你有幫助,歡迎關注與

題目描述

給你一幅由 N × N 矩陣表示的圖像,其中每個像素的大小為 4 字節。請你設計一種算法,將圖像旋轉 90 度。

不佔用額外內存空間能否做到?

https://leetcode-cn.com/problems/rotate-matrix-lcci/

示例示例 1

給定 matrix = 
[
  [1,2,3],
  [4,5,6],
  [7,8,9]
],

原地旋轉輸入矩陣,使其變為:
[
  [7,4,1],
  [8,5,2],
  [9,6,3]
]

示例 2

給定 matrix =
[
  [ 5, 1, 9,11],
  [ 2, 4, 8,10],
  [13, 3, 6, 7],
  [15,14,12,16]
], 

原地旋轉輸入矩陣,使其變為:
[
  [15,13, 2, 5],
  [14, 3, 4, 1],
  [12, 6, 8, 9],
  [16, 7,10,11]
]

分析方法 1:藉助額外數組存放旋轉後的元素

按行旋轉,找旋轉前和旋轉後元素的坐標對應關係

原始矩陣

1 2 3
4 5 6
7 8 9

把第一行順時針旋轉 90 度後為:

x x 1
x x 2
x x 3

對應的坐標關係如下所示:

1:(0,0) -> (0,2)  
2:(0,1) -> (1,2)  
3:(0,2) -> (2,2) 

把第二行順時針旋轉 90 度後為:

x 4 1
x 5 2
x 6 3

對應的坐標關係如下所示:

4:(1,0) -> (0,1)
5:(1,1) -> (1,1)
6:(1,2) -> (2,1)

第三行也類似,就不一一列出了,通過旋轉前和旋轉後坐標的對比,我們可以得出一個很重要的規律

旋轉前元素的坐標:(row,col)

旋轉後元素的坐標:(col,n-row-1)

matrix[row] [col] = matrix[col] [n-row-1]

這個規律很重要,下面方法也會用到

方法就很簡單了,我們遍歷矩陣,根據上面得出的旋轉前後的對應關係,把元素放到新的位置上,這裡我們用額外矩陣來存放旋轉後的元素,然後在最後把新矩陣複製到之前的矩陣中

源碼

public static void rotate(int[][] matrix) {
        int n = matrix.length;
        if (n == 0) {
            return;
        }

        // 新建一個矩陣用來存放旋轉後的元素
        int[][] newMatrix = new int[n][matrix[0].length];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                // 根據旋轉前後的對應關係把元素放到要旋轉的位置上
                newMatrix[j][n - i - 1] =  matrix[i][j];
            }
        }
        // 把存放旋轉後元素的矩陣複製到原先的矩陣上
        System.arraycopy(newMatrix, 0, matrix, 0, newMatrix.length);
    }

執行結果

方法1執行結果複雜度分析

時間複雜度:O(n^2)

空間複雜度:O(n^2)

方法 1 佔用了額外的內存空間,其實這道題是可以不額外佔用空間也能實現,我們一起來看下下面的方法吧

方法 2:原地進行旋轉操作

如果不藉助額外數組該怎麼操作?

在方法 1 中我們找到了規律是元素 matrix[row] [col] 旋轉到了 matrix[col] [n-row-1] ,如果不藉助額外數組,matrix[col] [n-row-1] 就會被覆蓋,所以需要先把 matrix[col] [n-row-1] 臨時存起來,下面就一起分析下不藉助額外數組進行原地旋轉吧,分析過程比較枯燥,建議自己嘗試一下,省的跟丟

temp = matrix[col] [n-row-1]

matrix[col] [n-row-1] = matrix[row] [col]

matrix[row] [col] = matrix[col] [n-row-1]

這裡需要注意的是新的 row = col;col = n-row-1,將其代入上面的規律中後為

matrix[n - row -1] [n - col -1] = matrix[col] [n - row -1]

可知,matrix[col] [n - row -1]  旋轉後的位置是 matrix[n - row -1] [n - col -1],這個位置也會被覆蓋,所以還需要用 temp 做中轉

temp = matrix[n - row -1] [n - col -1]

matrix[n - row- 1] [n - col-1] = matrix[col] [n - row -1]

matrix[col] [n-row-1] = matrix[row] [col]

matrix[row] [col] = matrix[col] [n-row-1]

這裡需要注意的是新的 row = n - row -1;col = n - col -1,將其代入上面的規律中後為

matrix[n - col - 1] [row] = matrix[col] [n - row -1]

可知,matrix[n - row -1] [n - col -1] 旋轉後的位置為   matrix[n - col - 1] [row] ,但這個位置也會被覆蓋,依然需要用 temp 做中轉

temp = matrix[n - col - 1] [row]

matrix[n - col - 1] [row] = matrix[n - row -1] [n - col -1]

matrix[n - row -1] [n - col -1] = matrix[col] [n - row -1]

matrix[col] [n - row -1] = matrix[row] [col]

matrix[row] [col] = matrix[col] [n-row-1]

這裡需要注意的是新的 row = n - col - 1;col =row ,將其代入上面的規律中後為

matrix[row] [col] = matrix[n - col - 1] [row]

這次竟然發現了新大陸,matrix[col] [n - row -1] 旋轉後的位置為 matrix[row] [col],最後又回到了起點

此刻腦子裡飄來了《那些年,我們一起追過的女孩》中的歌詞「又回到最初的起點,呆呆的站在鏡子前……」

綜合上面的推導,我們發現旋轉的重點就是下面的 4 個元素是處於一個循環中的

matrix[row] [col]

matrix[col] [n - row -1]

matrix[n - row - 1] [n - col - 1]

matrix[n - col - 1] [row]

對於上面 4 個元素的交換,我們用一個  temp 就可以實現了。但還有一點需要我們思考,就是要枚舉哪些位置的元素來進行原地交換,能保證不重不漏呢?因為一次原地交換需要 4 個元素參加。我們把 n 分為偶數和奇數來看

當 n 為偶數時,需要枚舉 (n/2)*(n/2) = n^2/4 個位置,所以矩陣左上角的子矩陣就符合我們的要求,可以做到不重不漏

下面用 n = 4 來舉個慄子,* 代表要枚舉的位置

n為偶數枚舉的位置

當 n 為奇數時,矩陣的中心位置是不動的,只需要枚舉 ((n-1)/2)*((n+1)/2) = (n^2 -1/4) 個位置,矩陣的左上角的子矩陣還是符合要求的,可以做到不重不漏

下面以 n = 5 來舉個慄子,* 代表要枚舉的位置,x 代表中心位置

n為奇數枚舉的位置

綜上,我們只需要枚舉矩陣左上角高為 n/2,寬為 (n+1)/2 的小矩陣就好了,就可以做到不重不漏

源碼

public static void rotate2(int[][] matrix) {
        int n = matrix.length;
        if (n == 0) {
            return;
        }

        for (int row = 0; row < n / 2; row++) {
            for (int col = 0; col < (n + 1) / 2; col++) {
                // 把 4 個位置的元素互換,注意順序
                int temp = matrix[n - col -1][row];
                matrix[n - col -1][row] = matrix[n - row -1][n - col -1];
                matrix[n - row -1][n - col -1] = matrix[col][n-row-1];
                matrix[col][n-row-1] = matrix[row][col];
                matrix[row][col] = temp;
            }
        }
    }

執行結果

方法2執行結果複雜度分析

時間複雜度:O(n^2)

空間複雜度:O(1),這個方法沒有額外使用空間

方法 2 的難點是找到 4 個元素位置的坐標關係,然後找到可以枚舉的不重不漏的子矩陣,方法已經很優秀了,但是廣大技術友總是能找到更牛逼的方法,下面的方法是從數學的角度來做到矩陣的原地旋轉

方法 3 :原地雙百

主要思想是用翻轉代替旋轉,先以對角線為軸,進行翻轉。再以每一行的中點為軸,進行翻轉。

我們通過示例圖來看下這個過程

對於原始矩陣,先以對角線為軸,進行翻轉

以對角線為軸進行翻轉

翻轉完後如下所示:

以對角線為軸翻轉後

然後對每一行以中點為軸,進行翻轉,效果如下:

以中點為軸翻轉後

以對角線進行翻轉時,我們只需要枚舉對角線上方的元素,然後和下方元素進行交換,坐標對應關係如下

matrix[row] [col] --> matrix[col] [row]

對每行以中點進行翻轉時,我們只需要枚舉中點左邊的元素,然後和右邊的元素進行交換,坐標對應關係如下

matrix[row] [col] --> matrix[row] [n - col - 1]

源碼

public static void rotate3(int[][] matrix) {
        int n = matrix.length;
        if (n == 0) {
            return;
        }
        // 以對角線進行翻轉
        for (int row = 0; row < n; row++) {
            for (int col = row + 1; col < n; col++) {
                int temp = matrix[col][row];
                matrix[col][row] = matrix[row][col];
                matrix[row][col] = temp;
            }
        }
        // 對每一行以中點進行翻轉
        for (int row = 0; row < n; row++) {
            for (int col = 0; col < n / 2; col++) {
                int temp = matrix[row][n - col - 1];
                matrix[row][n - col - 1] = matrix[row][col];
                matrix[row][col] = temp;
            }
        }
    }

執行結果

方法3執行結果

這種方法很巧妙,一般很難想到,所以只要你多刷肯定能學到一些比較好的方法,這種方法就是你看到過,你就會,沒看到過,可能就需要好好想想了,最終還不一定能想到

但這個方法還可以優化,可以把按對角線翻轉按中點翻轉放到一個大 for 循環下

源碼

public static void rotate4(int[][] matrix) {
        int n = matrix.length;
        if (n == 0) {
            return;
        }

        for (int row = 0; row < n; row++) {
            // 以對角線進行翻轉
            for (int col = row + 1; col < n; col++) {
                int temp = matrix[col][row];
                matrix[col][row] = matrix[row][col];
                matrix[row][col] = temp;
            }

            // 對每一行以中點進行翻轉
            for (int col = 0; col < n / 2; col++) {
                int temp = matrix[row][n - col - 1];
                matrix[row][n - col - 1] = matrix[row][col];
                matrix[row][col] = temp;
            }
        }
    }

執行結果

方法4執行結果複雜度分析

時間複雜度:O(n^2)

空間複雜度:O(1) 沒有使用額外的空間

用過該題做過面試題的公司

使用該題的公司

其實有技術友分享過陌陌公司去年使用過這道題作為筆試題,不知道今年還有沒有,遇到的小夥伴可以留言說下

陌陌真題-技術友分享

相關焦點

  • 一起刷 leetcode 之螺旋矩陣(頭條和美團真題)
    問題或建議,請公眾號留言;如果你覺得文章對你有幫助,歡迎關注與轉發題目描述給定一個包含 m*n 個元素的矩陣(m 行,n 列),請按順時針螺旋順序,返回矩陣中所有元素leetcode 第 54 題https://leetcode-cn.com/problems/spiral-matrix/示例示例 1
  • ​LeetCode刷題實戰54:螺旋矩陣
    今天和大家聊的問題叫做 螺旋矩陣,我們先來看題面:https://leetcode-cn.com/problems/spiral-matrix/Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in
  • 每天一道leetcode61-旋轉鍊表
    正當班主任要繼續發話,只聽到角落默默想起來一個聲音:」K2」前言 2018.11.21號打卡今天的題目leetcode26:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/description/昨天的題解題目 每天一道leetcode61
  • 六十四、開始刷Leetcode之旅(Python版本)
    其實Leetcode上的一部分題我都基本刷過,那麼我就開始吧。註冊Leetcode帳號目前Leetcod有國際和中文的兩個版本,下圖就是我的中文版本,刷的不夠,其實我也挺菜的。這個是中文的網址:https://leetcode-cn.com/problemset/all/下圖就是我的國際版本,其實我主要在國際版混日子,刷了100多題,其實我很菜的。這是它的官網,https://leetcode.com。
  • leetcode刷題指南之RussianDollEnvelopes
    問最多可以幾個套娃放在一起。 比如,[[5,4],[6,4],[6,7],[2,3]]的答案是3,因為[2,3] => [5,4] => [6,7]2.j].second)15                dp[i]=max(dp[i],dp[j]+1);16    int ans=0;17    for(int i=0;i<n;i++)18        ans=max(ans,dp[i]);19    return ans;20}21};推薦閱讀:leetcode
  • 一個月帶你狂刷算法Leetcode題,衝刺最後秋招!
    對書籍重難點內容提供針對性視頻講解,對leetcode刷題進行重難點視頻講解1、以《百面機器學習》為教材,結合leetcode篩選刷題2、利用一線發開中最實際的問題為主,包含熱點和常考問題 從EM算法到kmeans算法的推導【視頻課】機器學習中的概率圖模型hmm的引出和問題的介紹HMM預測問題之維特比算法crf的一些基礎概念crf具體介紹【刷題】LeetCode:字符串、數組和矩陣、位運算、數學【直播】周六晚直播+答疑課Week4
  • leetcode 刷500道題,筆試/面試穩嗎?
    如果我在 leetcode 堅持刷它個 500 道題,以後筆試/面試穩嗎?這裡我說下我的個人看法,我認為不穩。下面說說為啥不穩以及算法題應該如何刷、如何學才比較好,當然,也會推薦自己學過的資料。一、先說說筆試題在刷 leetcode 的時候,你會發現,每道題的題意都很短,你只需要花十幾秒的時間,就知道這道題是要你幹嘛了,並且每道題所用道的算法思想都很明確,動態規劃、遞歸、二分查找等,你可能很快就知道該用哪種方法,只是你不懂如何把代碼寫出來而已。
  • leetcode刷題指南之PatchingArray
    +]; 9        else{10            ans++;11            sum += sum+1;12        }13    }14    return ans;15}16};作者:東大ACM退役隊伍編輯:劉凱旋推薦閱讀:leetcode
  • leetcode刷題指南之shortestPalindrome
    i+1;18        }19    }20    for (int i = len; i < n; i++) {21        s += h[i];22    }23    return s;24}25};作者:東大ACM退役隊伍編輯:劉凱旋推薦閱讀:leetcode
  • leetcode刷題指南之PalindromePairs
    = i)25                    ans.push_back({ma[r], i});26            }27        }28        return ans;29    }30};作者:東大ACM退役隊伍編輯:劉凱旋推薦閱讀:leetcode刷題指南之
  • leetcode 刷500道題,筆試/面試穩嗎?別以為你自己穩了
    來源公眾號:苦逼的碼農作者:帥地想要學習算法、應付筆試或者應付面試手撕算法題,相信大部分人都會去刷 Leetcode,有讀者問?如果我在 leetcode 堅持刷它個 500 道題,以後筆試/面試穩嗎?
  • leetcode刷題指南之findtheduplicatenumber
    if (nums[i] <= m) cnt++;10        if (cnt > m) r = m;11        else l = m + 1;12    }13    return l;14}15};作者:東大ACM退役隊伍編輯:劉凱旋推薦閱讀:leetcode
  • Anki實戰-刷leetcode之「141-環形鍊表」
    有同學說自己刷leetcode的時候每次刷完就忘,忘了再刷。
  • leetcode刷題最強指南(版本1.0)
    為什麼會有這篇刷題指南很多剛開始刷題的同學都有一個困惑:面對leetcode上近兩千道題目,從何刷起
  • 帶你狂刷算法Leetcode題!短時間內快速獲得實戰能力!
    但對於初學者來說,很容易沉迷在刷題的數量中,覺得如果能刷完這1000道題,自己一定能夠有所飛躍。但實際上,低效率的重複對你來說,根本就無法掌握到解題的精髓,一旦題目有所變動,就無法舉一反三。那究竟應該怎麼刷題才高效呢?
  • 春節大禮包|刷題技巧+80道Leetcode
    為了跳槽,我前兩年的春節都是在刷題中度過的,目前為止刷了小四百道leetcode,也算是有一些經驗,今天就跟大家分享下學習方法和我總結的乾貨。後來發現了 Leetbook[1] 這個寶藏,才算是找到了適合自己的刷題方法。
  • leetcode刷對了麼
    今天,逆行君就帶你從世界觀和方法論兩方面走進「leetcode」 leetcode之世界觀 什麼是leetcode 簡單來說,leetcode是一個美國的在線編程網站,它收集了各大公司的經典算法面試題,用戶可以選擇不同的語言進行代碼的在線編寫、編譯和調試。
  • 【每日一題】leetcode刷題指南之Longest Consecutive Sequence
    used.end())    {        used[right]=true;        right++;    }    ans=max(ans,right-left-1);}return ans;}作者:東大ACM退役隊伍編輯:劉凱旋推薦閱讀:leetcode
  • 如何科學的刷 Leetcode
    這是它的官網,https://leetcode.com。Leetcode 官網很久以前,還是在大學的時候,有師兄對我意味深長的說,如果把 Leetcode 上面的題目做上七遍,就有很大概率能夠通過谷歌的面試。雖然有點誇張,這句話還是對我幼小的內心,產生了不小的震撼。
  • 隱私風險高懸 陌陌Zao難逃火一把就死的命運?
    今天各大門戶網站、科技網站頭條、自媒體等都在報導這款換臉應用神器ZAO。據悉用戶只需上傳一張正臉圖片,便可以在ZAO上把視頻裡的明星臉替換成用戶自己的臉,一夜之間無數用戶滿足了自己擁有一張明星臉的欲望。以#Zao AI 換臉# 話題曾一度登上微博熱搜第7位,App在ios商店下載量攀升至娛樂類免費排行第一位,朋友圈也是有不少用戶在刷頻換臉。