【第6期】面試BAT前應該知道的二維數組

也許你看過不少面經裏都講到面試會問一些奇奇怪怪的算法題,說實話,手撕紅黑樹作不出來不丟人,很難的動態規劃沒思路也不要緊,可是二維數組相關的題目仍是但願你瞭解一下。前端

不一樣於一些奇怪的算法題讓你面試造核彈,二維數組算法題目很能考察到邏輯能力、程序邊界問題,並且有至關一部分二維數組可使用遞歸來解決,能夠說是程序員居家之必備題。程序員

考察數組下標規律和邊界

此類問題,重點是從數組的下標中發現規律,寫代碼的時候要注意程序的邊界問題,常見於Z字形打印二維數組、蛇形打印二維數組、楊輝三角等等問題。面試

楊輝三角

LeetCode.118,難度簡單算法

題目

給定一個非負整數 numRows,生成楊輝三角的前 numRows 行。
示例:數組

輸入: 5

輸出:

[微信

[1],<br> 
[1,1],<br>

[1,2,1],

[1,3,3,1],

[1,4,6,4,1]
]spa

思路

觀察給出的例子,能夠發現規律,好比對於arr1=[1,2,1]和arr2=[1,3,3,1]來講:
code


能夠看出規律來:遞歸

  1. arr2[0] = 1
  2. arr2[1] = arr1[0] + arr1[1]
  3. arr2[2] = arr1[1] + arr1[2]
  4. arr[3] = 1

反過來思路就是:rem

  • 遍歷arr1,index=0時,arr2.push(1)
  • 若是index=arr1.length-1,arr2.push(1)
  • 不然,arr2.push(arr1[index] + arr1[index+1])

代碼

/**
 * @param {number} numRows
 * @return {number[][]}
 */
var generate = function(n) {
  if(n === 0)
    return [];
  if(n === 1)
    return [ [1] ];
  
  let res = [[1]];
  
  for(let i = 1;i < n;i++) {
    let lastArr = res[i-1];
    let newArr = [];
    for(let j = 0;j < lastArr.length;j++) {
      if(j === 0)
          newArr.push(1);
      if(j === lastArr.length-1)
        newArr.push(1);
      else {
        newArr.push(lastArr[j]+lastArr[j+1]);
      }
    }
    
    res.push(newArr);
  }
  return res;
};

對角線遍歷二維數組

LeetCode.498,難度中等

題目

給定一個含有 M x N 個元素的矩陣(M 行,N 列),請以對角線遍歷的順序返回這個矩陣中的全部元素,對角線遍歷以下圖所示。
示例:

輸入:
[

[ 1, 2, 3 ],

[ 4, 5, 6 ],

[ 7, 8, 9 ]

]

輸出: [1,2,4,7,5,3,6,8,9]

思路

觀察一個例子,對於下面這個二維數組來講,對角線遍歷的順序以下:

  1. (0,0)
  2. (0,1), (1,0)
  3. (2,0), (1,1), (0,2)
  4. (0,3), (1,2), (2,1)
  5. (2,2), (1,3)
  6. (2,3)
[

A, B, C, D,

E, F, G, H,

I, J, K, L

]

能夠看出來一些規律:

  • 按打印一次對角線來算,一共要打印6次,也就是行數+列數-1次
  • 按最開始那一次算第count=0次的話,若是count小於行數,那x=count且y=count-x,不然x=行數-1且y=count-x,對於列數來講也是同樣的道理

代碼

/**
 * @param {number[][]} matrix
 * @return {number[]}
 */
var findDiagonalOrder = function(matrix) {
    if(matrix === null || matrix.length === 0 || matrix[0].length === 0)
        return [];
    
    let rows = matrix.length;
    let cols = matrix[0].length;
    let count = 0;
    let res = [];
    
    while(count < rows+cols-1) {
        let r1 = count < rows ? count : rows-1;
        let c1 = count - r1;
        while(r1 >= 0 && c1 < cols) {
            res.push(matrix[r1][c1]);
            r1--;
            c1++;
        }
        
        count++;
        if(count >= rows+cols-1)
            break;
        
        let c2 = count < cols ? count : cols-1;
        let r2 = count - c2;
        while(c2 >= 0 && r2 < rows) {
            res.push(matrix[r2][c2]);
            r2++;
            c2--;
        }
        count++
    }
    
    return res;
};

螺旋矩陣

LeetCode.54,難度中等

題目

給定一個包含 m x n 個元素的矩陣(m 行, n 列),請按照順時針螺旋順序,返回矩陣中的全部元素。

示例 1:

輸入:
[

[ 1, 2, 3 ],

[ 4, 5, 6 ],

[ 7, 8, 9 ]
]

輸出: [1,2,3,6,9,8,7,4,5]

示例 2:

輸入:
[

[1, 2, 3, 4],

[5, 6, 7, 8],

[9,10,11,12]
]

輸出: [1,2,3,4,8,12,11,10,9,5,6,7]

思路

從題目給出的例子能夠看出,螺旋遍歷矩陣其實就是按照順時針一層一層的遍歷。注意好邊界的劃分,以及特殊狀況好比一行和一列的處理。

代碼

/**
 * @param {number[][]} matrix
 * @return {number[]}
 */
var traverseLayer = function(m, startRow, endRow, startCol, endCol, res) {
    if(startRow === endRow) {
    // 一列的狀況
        for(let i = startCol;i <= endCol;i++) {
            res.push(m[startRow][i]);
        }
    } else if(startCol === endCol) {
        for(let i = startRow;i <= endRow;i++) {
            res.push(m[i][startCol]);
        }
    } else {
        let curR = startRow;
        let curC = startCol;
        
        while(curC < endCol) {
            res.push(m[curR][curC]);
            curC++;
        }
        while(curR < endRow) {
            res.push(m[curR][curC]);
            curR++;
        }
        while(curC > startCol) {
            res.push(m[curR][curC]);
            curC--;
        }
        while(curR > startRow) {
            res.push(m[curR][curC]);
            curR--;
        }
    }
}

var spiralOrder = function(matrix) {
    if(matrix === void 0 || matrix.length === 0 || matrix[0].length === 0)
        return [];
    
    let res = [];
    let startRow = 0;
    let endRow = matrix.length-1;
    let startCol = 0;
    let endCol = matrix[0].length-1;
    
    
    while(startRow <= endRow && startCol <= endCol) {
        traverseLayer(matrix, startRow, endRow, startCol, endCol, res);
        startRow++;
        endRow--;
        startCol++;
        endCol--;
    }
    
    return res;
};

考察遞歸思路

對於此類問題,最重要的是要注意到不是簡單地找數組下標的規律就能解決的,要把問題抽象成遞歸的思路,遞納入口、遞歸調用、遞歸結束條件,這三點缺一不可。

朋友圈

LeetCode.547,難度中等

題目

班上有 N 名學生。其中有些人是朋友,有些則不是。他們的友誼具備是傳遞性。若是已知 A 是 B 的朋友,B 是 C 的朋友,那麼咱們能夠認爲 A 也是 C 的朋友。所謂的朋友圈,是指全部朋友的集合。

給定一個 N * N 的矩陣 M,表示班級中學生之間的朋友關係。若是Mi = 1,表示已知第 i 個和 j 個學生互爲朋友關係,不然爲不知道。你必須輸出全部學生中的已知的朋友圈總數。

示例 1:

輸入:

[[1,1,0],

[1,1,0],

[0,0,1]]

輸出: 2

說明:已知學生0和學生1互爲朋友,他們在一個朋友圈。
第2個學生本身在一個朋友圈。因此返回2。

示例 2:

輸入:

[[1,1,0],

[1,1,1],

[0,1,1]]

輸出: 1

說明:已知學生0和學生1互爲朋友,學生1和學生2互爲朋友,因此學生0和學生2也是朋友,因此他們三個在一個朋友圈,返回1。

注意:
N 在[1,200]的範圍內。
對於全部學生,有Mi = 1。
若是有Mi = 1,則有Mj = 1。

思路

看一下這道題的示例,能夠看出來若是arr[0,1]=1,0和1是朋友,那接下來就要去檢查1和其餘的人是不是朋友,再依次檢查下去,說到這裏就能夠考慮深度優先的搜索方法了。

代碼

/**
 * @param {number[][]} M
 * @return {number}
 */
var findCircleNum = function(M) {
    // 深度優先
    let visited = [];
    for(let i = 0;i < M.length;i++) {
        visited.push(false);    
    }
    
    let res = 0;
    for(let i = 0;i < visited.length;i++) {
        if(visited[i]) 
            continue;
        traverse(M, i, visited);
        res++;
    }
    
    return res;
};

function traverse(M, cur, visited) {
    visited[cur] = true;
    for(let i = 0;i < M.length;i++) {
        if(visited[i] || !M[cur][i])
            continue;
        traverse(M, i, visited);
    }
}

島嶼的最大面積

LeetCode.695,難度中等

題目

給定一個包含了一些 0 和 1的非空二維數組 grid , 一個 島嶼 是由四個方向 (水平或垂直) 的 1 (表明土地) 構成的組合。你能夠假設二維矩陣的四個邊緣都被水包圍着。

找到給定的二維數組中最大的島嶼面積。(若是沒有島嶼,則返回面積爲0。)

示例 1:

[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]

對於上面這個給定矩陣應返回6。

注意答案不該該是11,由於島嶼只能包含水平或垂直的四個方向的‘1’。

示例 2:

[[0,0,0,0,0,0,0,0]]

對於上面這個給定的矩陣, 返回 0。

注意: 給定的矩陣grid 的長度和寬度都不超過 50。

思路

對於[[1,1,0,0,0],[1,1,0,0,0],[0,0,0,1,1],[0,0,0,1,1]]這樣的矩陣來講,從最左上角的1出發,分別再考察它的四周(上下左右)是否爲1,它的上下左右再考察本身的四周是否爲1,遇到是1則能夠加1,遇到是0則不處理便可,說到這裏基本就能夠肯定可使用遞歸了,即有一個本身調用本身的過程;這裏面有個細節,處理過的1須要置爲0,不然會有重複計算的問題。

代碼以下:

/**
 * @param {number[][]} grid
 * @return {number}
 */
var maxAreaOfIsland = function(grid) {
    if(grid === null || grid.length === 0)
        return 0;
    
    let res = 0;
    for(let i = 0;i < grid.length;i++) {
        for(let j = 0;j < grid[0].length;j++) {
            if(grid[i][j] === 1)
                res = Math.max(res, help(grid, i, j));
        }
    }
    
    return res;
};

var help = function(grid, i, j) {
    grid[i][j] = 0;
    
    let sum = 1;
    if(i > 0 && grid[i-1][j] === 1)
        sum += help(grid, i-1, j);
    if(i < grid.length-1 && grid[i+1][j] === 1) 
        sum += help(grid, i+1, j);
    if(j > 0 && grid[i][j-1] === 1)
        sum += help(grid, i, j-1);
    if(j < grid[0].length-1 && grid[i][j+1] === 1)
        sum += help(grid, i, j+1);
    
    return sum;
}

歡迎關注微信公衆號——前端亞古獸(fe-yagushou)

相關文章
相關標籤/搜索