孤岛计数二维数组算法

Posted

技术标签:

【中文标题】孤岛计数二维数组算法【英文标题】:island count two-dimensional array algorithm 【发布时间】:2017-12-09 10:52:57 【问题描述】:
let arr = [[1, 0, 1],
           [1, 0, 0],
           [1, 1, 1]
     ];

我有数组,其中 1 岛和 0 水。我需要写一个岛屿计数器。这里有 2 岛 1 大和 1 小(单人)。例如这里有 5 个单岛

let arr = [[1, 0, 1],
           [0, 1, 0],
           [1, 0, 1]
     ]; 

我写了双循环来吸引数组中的每个项目,如下所示:

for(let i = 0; i < arr.length; i++)
    for(let x = 0; x < arr[i].length; x++)
         if(...)
    
 

我需要为此编写 сondition。请帮帮我。

【问题讨论】:

想要的结果是什么?岛屿的数量?尺寸? 对不起,我忘了写。我需要岛屿数 到目前为止您尝试过什么?你被困在哪里了?您在寻找哪种“条件”? 【参考方案1】:

您可以使用计数器并检查所有相邻项目并使用实际计数器更新元素。

function check(array) 

    function test(array, i, j, value) 
        if (array[i] && array[i][j] === -1) 
            array[i][j] = value;
            test(array, i -1, j, value);
            test(array, i + 1, j, value);
            test(array, i, j - 1, value);
            test(array, i, j + 1, value);
            return true;
        
    
    var value = 1;

    array.forEach(a=> a.forEach((b, i, bb) => bb[i] = -b));
    array.forEach((a, i, aa) => a.forEach((b, j) => test(aa, i, j, value) && value++));
    document.getElementById('out').innerhtml += array.map(a => a.join(' ')).join('\n') + '<hr>';
    return value - 1;


console.log(check([[1, 0, 1], [1, 0, 0], [1, 1, 1]]));
console.log(check([[1, 0, 1], [0, 1, 0], [1, 0, 1]]));
&lt;pre id="out"&gt;&lt;/pre&gt;

【讨论】:

【参考方案2】:

我喜欢这个问题。这是一个基本的。因此,就我能想到的而言,应该有一个有效的算法。我认为它需要递归。

/**
 * function to resolve islands
 * @param  Array map     - the world map we provide
 * @param  Array islands - 2D array considiting of island arrays each
 *                           holding island coordinates. Defaults to []
 * @return Array islands - "
**/
function resolveIslands(map, islands = [])

  /**
   * function to take a coordinate and split 4 neighboring coordinates into 1s and 0s
   * @param  Number r - 0 indexed row value
   * @param  Number c - 0 indexed column value
   * @return Array    - coordinates of 0s and 1s split in the form of [[0s],[1s]]
  * */
  function splitRoutes(r,c)
    return [[r-1,c],[r,c-1],[r+1,c],[r,c+1]].reduce((p,[r,c]) => map[r]    === void 0 ||
                                                                 map[r][c] === void 0 ? p
                                                                                      : map[r][c] ? (p[1].push([r,c]),p)
                                                                                                  : (p[0].push([r,c]),p), [[],[]]);
  
  
  /**
   * function to take a coordinate and solve the island if the coordinate is 1
   * @param Number r     - 0 index row value
   * @param Number c     - 0 index column value
   * @param Array island - An array of arrays of coordinates representing the
                             currently resolved island
   * @param Number oc    - The number of 1s in da hood
   * @param Array tz     - Gradually becoming all available zeros in the map
                             initially []
   * @return Undefined   - Full of side effects :)
  * */
  function scan(r, c, island = [], oc = 0, tz = [])
    var [toZeros,toOnes] = splitRoutes(r,c);
    tz.push(...toZeros);
    switch (map[r][c]) 
      case 1: !island.length && islands.push(island);
              oc += toOnes.length;
              map[r][c] = void 0;
              island.push([r,c]);
              toOnes.forEach(([r,c]) => scan(r,c,island,--oc,tz));
              !oc && tz.forEach(([r,c]) => map[r][c] === 0 && scan(r,c,island,oc,tz));
              break;
      case 0: map[r][c] = void 0;
              toOnes.forEach(([r,c]) => map[r][c] === 1 && scan(r,c));
              tz.forEach(([r,c]) => map[r][c] === 0 && scan(r,c));
              break;
    
  
  scan(0,0);
  return islands;




let arr = [[1, 0, 1],
           [1, 0, 0],
           [1, 1, 1]
          ],
    brr = [[1, 0, 1, 1, 1],
           [0, 1, 0, 0, 1],
           [1, 0, 1 ,1, 0],
           [0, 0, 0, 0, 1],
           [1, 1, 0, 1, 1]
          ];

console.log(JSON.stringify(resolveIslands(arr)));
console.log(JSON.stringify(resolveIslands(brr)));

primaryRoutes(r,c) 实用函数接受一个坐标,并在数组中返回我们接下来可以访问的路线。包含 1 的路线优先,包含 0 的路线延迟(放在队列的后面),地图外或之前访问过的 0 的路线将被忽略。

每个访问的单元格都变成void 0,这是一个完美的undefined

这比公认的答案稍慢,但考虑到您也获得了岛屿坐标,我仍然认为它是有效的。因此,为了获得找到的岛屿的数量,您需要检查结果数组的 length 属性。

【讨论】:

【参考方案3】:

一个更详细的答案,更容易理解:

function gridToList(grid, rows, column)
  var lg = [];
  var gridString = "<p>";
  for(var z = 0; z < grid.length; z++)
    gridString = gridString.concat("[");
    gridString = gridString.concat(grid[z]);
    lg = lg.concat(grid[z]);
    gridString = gridString.concat("]<br/>");
    gridString = gridString.concat("</p>");
  
  var el = document.getElementById("initialData");
  if(el)
      el.innerHTML = "rows:" + rows + "<br>" +
    "column:" + column + "<br>" +
    "grid:" + gridString + "<br>";
  
  return lg


function numberAmazonGoStores(rows, column, grid)

  var idm = ;

  lg = gridToList(grid, rows, column);

  for(var z = 0; z < lg.length; z++)
    if(lg[z])
      idm[z] = 1;
    
  

  var findNeighbors = function(b) 

    var currentBuilding = parseInt(b);
    var currRow = Math.floor(currentBuilding/column);

    //remove value from map so we dont re-traverse it.
    delete idm[currentBuilding];

    var u,d,l,r;

    // u = - column if > 0
    u = currentBuilding - column;
    if(idm[u]) 
      findNeighbors(u);
    

    // d = + column if < column*rows
    d = currentBuilding + column;
    if(idm[d]) 
      findNeighbors(d);
    

    // l = - 1 if > 0 && same row;
    l = currentBuilding - 1;
    var lRow = Math.floor(l/column);
    if(lRow === currRow && idm[l]) 
      findNeighbors(l);
    

    // r = + 1 if < row && same row;
    r = currentBuilding + 1;

    var rRow = Math.floor(r/column);
    if(rRow === currRow && idm[r]) 
      findNeighbors(r);
    
  

  var clusters = 0;
  // loop over non traversed values in map
  for(p in idm)
    if(idm[p])
      findNeighbors(p);
    
    clusters += 1;
  
  console.log("grid size = " + lg.length);
  console.log("total clusters found: " + clusters)
  return clusters;


g = [
[1,1,1,1],
[1,1,1,1],
[0,0,0,0],
[0,1,0,1]
]
numberAmazonGoStores(4,4,g);

g2 = [
[1,1,0,0,0],
[0,0,0,1,1],
[0,1,0,0,0],
[1,0,1,1,0],
[0,1,0,0,1],
[1,0,1,0,1],
[0,1,0,1,0]
];
numberAmazonGoStores(7,5,g2);

【讨论】:

解决问题的具体算法是什么? 只有在浏览器中才会更加冗长 @NinaScholz 我称之为递归部门优先搜索。这是来自互联网的一个小sn-p:遍历图表。 ...您可以通过遍历图形的所有顶点轻松地做到这一点,对检查时仍未访问的每个顶点执行算法。图的遍历通常使用两种算法:深度优先搜索 (DFS) 和广度优先搜索 (BFS)。 @LucaKiebel 我说它更冗长主要是因为我没有嵌套循环或链式操作。取而代之的是,我确保每个逻辑部分都分列在不同的行中,以便读者可以更轻松地通过逻辑。对于那些想要快速复制、粘贴和运行的人来说,日志记录和 ui 更新只是节食。【参考方案4】:
const map = [
  [1, 0, 0, 0, 1],
  [0, 0, 1, 0, 0],
  [0, 1, 0, 0, 1],
  [0, 1, 1, 1, 1],
  [0, 0, 0, 1, 1]
];

function findIslands(arr) 
  let rows = arr.length,
    cols = arr[0].length; // matrix dimentions

  let islands = 0;
  let eaten = []; // connected islands on the latest iterations

  let left = 0, // island on the left
    up = 0; // island on the right

  for (let row = 0; row < rows; row++) 
    for (let col = 0; col < cols; col++) 
      if (!arr[row][col]) 
        continue; // skip zero (water)
      
      left = col > 0 ?
        arr[row][col - 1] :
        0;

      up = row > 0 ?
        arr[row - 1][col] :
        0;
      if (!left && !up)  // new island starts if there is water on the left and up
        islands++;
        arr[row][col] = islands; // give a number to island 
       else if (left && up && left !== up)  //upper island is not seperate
        arr[row][col] = left;
        eaten.push(up)
       else if (left) 
        arr[row][col] = left; // island continues previous island to the right
       else if (up) 
        arr[row][col] = up; // island continues previous island
      
    
  
  console.table(arr)
  return islands - eaten.length;


console.log('найдено островов ', findIslands(map))

【讨论】:

请在您的答案中添加一些解释,以便其他人可以从中学习

以上是关于孤岛计数二维数组算法的主要内容,如果未能解决你的问题,请参考以下文章

孤岛问题算法题

根据一维计数器数组填充二维数组列

获取每个二维数组的累积计数

请教一个关于二维数组的算法

二维数组转换算法,最好是js,或者其他语言也行。

二维数组元素个数的算法