【问题标题】:Spiral traversal of a matrix - recursive solution in JavaScript矩阵的螺旋遍历 - JavaScript 中的递归解决方案
【发布时间】:2015-09-03 13:27:54
【问题描述】:

我正在尝试提出一个采用这样的矩阵的解决方案:

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

并返回一个以螺旋形式遍历数组的数组,因此在本例中: [1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10]

我无法让这个递归解决方案工作,其中结果数组采用第一个数组,其余数组的最终元素,倒序的底部数组,然后是中间的第一个元素数组,然后在没有外部“外壳”的情况下重新构造数组,以便可以递归地调用剩下的数组,直到中心有一个元素的数组或 2x2 矩阵(我的基本情况,尽管后者可能不是必需的。 ..)

我的解决方案不起作用,如下所示。关于如何完成这项工作的任何建议?

var spiralTraversal = function(matriks){
  var result = [];
    var goAround = function(matrix) {
        var len = matrix[0].length;
        if (len === 1) {
            result.concat(matrix[0]);
            return result;
        }
        if (len === 2) {
            result.concat(matrix[0]);
            result.push(matrix[1][1], matrix[1][0]);
            return result;
        }
        if (len > 2) {
            // right
            result.concat(matrix[0]);
            // down
            for (var j=1; j < matrix.length - 1; j++) {
                result.push(matrix[j][matrix.length -1]);
            }
            // left
            for (var l=matrix.length - 2; l > 0; l--) {
                result.push(matrix[matrix.length - 1][l]);
            }
            // up
            for (var k=matrix.length -2; k > 0; k--) {
                result.push(matrix[k][0]);
            }
        }
        // reset matrix for next loop
        var temp = matrix.slice();
        temp.shift();
        temp.pop();
        for (var i=0; i < temp.length - 1; i++) {
            temp[i] = temp[i].slice(1,-1);
        }
        goAround(temp);
    };
    goAround(matriks);  
};

【问题讨论】:

    标签: javascript arrays algorithm recursion matrix


    【解决方案1】:

    您的代码非常接近,但它做的比它需要做的要多。在这里我简化并修复错误:

    var input = [[1,  2,   3,  4],
                 [5,  6,   7,  8],
                 [9,  10, 11, 12],
                 [13, 14, 15, 16]];
    
    var spiralTraversal = function(matriks){
      var result = [];
        var goAround = function(matrix) {
            if (matrix.length == 0) {
                return;
            }
    
            // right
            result = result.concat(matrix.shift());
    
            // down
            for (var j=1; j < matrix.length - 1; j++) {
                result.push(matrix[j].pop());
            }
    
            // bottom
            result = result.concat(matrix.pop().reverse());
    
            // up
            for (var k=matrix.length -2; k > 0; k--) {
                result.push(matrix[k].shift());
            }
    
            return goAround(matrix);
        };
    
        goAround(matriks);
    
        return result;
    };
    var result = spiralTraversal(input);
    
    console.log('result', result);
    

    运行它输出:

    result [1, 2, 3, 4, 12, 16, 15, 14, 13, 5, 6, 7, 8, 11, 10, 9]

    JSFiddle:http://jsfiddle.net/eb34fu5z/

    重要的事情:

    • concat on Array 返回结果——它不会改变调用者,所以你需要像这样保存concat 的结果:result = result.concat(otherArray)
    • 检查递归数组顶部的终止条件
    • 对于每次传递,执行预期的(上、右、下、左)
    • 返回结果

    我会这样做,但我会添加错误检查以验证数组具有相等数量的“行”和“列”。所以假设输入是有效的,我们开始:

    var input = [[1,  2,   3,  4],
                 [5,  6,   7,  8],
                 [9,  10, 11, 12],
                 [13, 14, 15, 16]];
    
    function run(input, result) {
        if (input.length == 0) {
            return result;
        }
    
        // add the first row to result
        result = result.concat(input.shift());
    
        // add the last element of each remaining row
        input.forEach(function(rightEnd) {
            result.push(rightEnd.pop());
        });
    
        // add the last row in reverse order
        result = result.concat(input.pop().reverse());
    
        // add the first element in each remaining row (going upwards)
        var tmp = [];
        input.forEach(function(leftEnd) {    
            tmp.push(leftEnd.shift());
        });
        result = result.concat(tmp.reverse());
    
        return run(input, result);
    }
    
    var result = run(input, []);
    
    console.log('result', result);
    

    哪些输出:

    result [1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10]

    一般的想法是我们知道每次通过我们需要做这些事情:

    1. 在输入中添加第一个数组
    2. 在输入中添加每个剩余数组的最后一项
    3. 在输入中添加最后一个数组
    4. 在输入中添加每个剩余数组中的第一项

    因此,如果我们在每次通过时都进行递归,我们就可以完成螺旋。

    JSFiddle:http://jsfiddle.net/2v6k5uhd/

    【讨论】:

      【解决方案2】:

      你的算法看起来不错,只有一个错误有几件事,有些比其他的更难发现。

      1. concat method 不会改变数组(就像push 一样),但会返回一个新数组,其中包含原始数组中的所有元素和参数。 result 没有发生变异。

        要解决此问题,您可以

        • 使用result = result.concat(…);
        • 使其成为一个显式循环,您可以在其中执行result.push(…)(如您已经编写的向下、向左和向上的循环)或
        • 使用result.push@987654322@(result, …) 一次推送多个值
      2. 您的“左”或“上”循环确实漏掉了一个元素,即左下角的元素。要么向左走,你需要前进到第一个元素(在条件中使用&gt;= 0),或者向上走时,你需要从最后一行而不是倒数第二行开始(matrix.length-1)李>
      3. 在为下一次迭代收缩矩阵的循环中,您忘记了最后一行,它必须是for (var i=0; i &lt; temp.length; i++)(而不是temp.length-1)。否则你会得到非常不幸的结果。
      4. 您的基本情况应该是 0(和 1),而不是(1 和)2。这将简化您的脚本并避免错误(在极端情况下)。
      5. 您希望您的矩阵是正方形的,但它们可能是矩形的(甚至有不均匀长度的线)。您正在访问的 .length 可能不是您所期望的 - 最好仔细检查并抛出带有描述性消息的错误。
      6. spiralTraversalgoAround 都缺少用于(递归)调用的 return 语句。他们只是填写result,但不返回任何东西。

      【讨论】:

      • 谢谢。我进行了第一个建议的更改,但仍然出现错误。 jsfiddle.net/vdn9ygae
      • @zahabba:好的,我仔细看了看,发现了更多东西 :-)
      【解决方案3】:

      虽然不是递归的,但至少输出正确答案:

      result: [ 1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10 ]
      

      我想说唯一奇怪的是必须在每个 while 循环之后“重置”变量 i,j。此外,可能还有更简洁的递归解决方案。

      var array = [ 
        [1,  2,  3,   4],
        [5,  6,  7,   8],
        [9,  10, 11,  12],
        [13, 14, 15,  16]  
      ];
      
      function spiralTraversal(array) {
        let discovered = new Set();
        let result = [];  
        let totalSpots = array.length * array[0].length;
        let direction = 'right';
      
        for (var i = 0; i < array.length; i ++) {
          for (var j = 0; j < array[i].length; j++) {   
      
            while (totalSpots) {
              while (direction === 'right' && !!bounds(array, i, j) && !discovered.has(array[i][j])) {  
                discovered.add(array[i][j]);                        
                result.push(array[i][j]);
                totalSpots--;                            
                j++;                         
      
              }
      
              direction = 'down';  
              i++;
              j--;
      
      
              while (direction === 'down' && !!bounds(array,i, j) && !discovered.has(array[i][i])) {      
                discovered.add(array[i][j]);                    
                result.push(array[i][j]);
                totalSpots--;          
                i++;                                           
              }
      
      
              direction = 'left';  
              j--;
              i--;
      
      
              while (direction === 'left' && !!bounds(array, i, j) && !discovered.has(array[i][j])) {  
                discovered.add(array[i][j]);                    
                result.push(array[i][j]);
                totalSpots--;       
                j--;                         
              }
      
      
              direction = 'up';          
              i--;
              j++
      
      
              while (direction === 'up' && bounds(array, i, j) && !discovered.has(array[i][j])) {
                discovered.add(array[i][j]);          
                result.push(array[i][j]);
                totalSpots--;          
                i--;                                   
              }
      
              direction = 'right';        
              j++;
              i++;
      
            }          
          }
        }
        return result;
      }
      
      function bounds(array, i, j){
        if (i < array.length && i >= 0 && j < array[0].length && j >= 0) {
          return true;
        } else {
          return false;
        }
      };
      

      【讨论】:

        【解决方案4】:

        递归解:

        我没有四处走动,而是越过顶行和最右边的一列,然后递归调用“反转”矩阵上的函数。

        var input = [
                        [ 1, 2, 3, 4], 
                        [ 5, 6, 7, 8], 
                        [ 9,10,11,12], 
                        [13,14,15,16]
                    ];
        
        let spiral = (mat) => {
            if(mat.length && mat[0].length) {
                mat[0].forEach(entry => { console.log(entry)})
                mat.shift();
                mat.forEach(item => {
                    console.log(item.pop())
                });
                spiral(reverseMatrix(mat))
            }
            return;
        }
        
        let reverseMatrix = (mat) => { 
            mat.forEach(item => { 
                item.reverse() 
            }); 
            mat.reverse(); 
            return mat; 
        }
        
        console.log("Clockwise Order is:")
        spiral(input)

        【讨论】:

          【解决方案5】:

          这是我的功能:

          let  array_masalah = [
              [1,2,3,4],
              [5,6,7,8],
              [9, 10, 11, 12],
              [13, 14, 15,16],
          ];
          
          let  array_masalah_2 = [
              [1, 2, 3, 4, 5],
              [6, 7, 8, 9, 10],
              [11, 12, 13, 14, 15],
              [16, 17, 18, 19, 20],
          ];
          
          
          function polaSpiral(array_masalah) {
              function spiral(array) {
                  if (array.length == 1) {
                  return array[0];
                }
          
                  var firstRow    = array[0]
                  , numRows     = array.length
                  , nextMatrix  = []
                  , newRow
                  , rowIdx
                  , colIdx      = array[1].length - 1
          
                  for (colIdx; colIdx >= 0; colIdx--) {
                  newRow = [];
          
                  for (rowIdx = 1; rowIdx < numRows; rowIdx++) {
                    newRow.push(array[rowIdx][colIdx]);
                  }
          
                  nextMatrix.push(newRow);
                }
          
                  firstRow.push.apply(firstRow, spiral(nextMatrix));
                  return firstRow
              }
          
              console.log(spiral(array_masalah));
          }
          
          
          polaSpiral(array_masalah) // [ 1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10 ]
          polaSpiral(array_masalah_2) // [ 1, 2, 3, 4, 5, 10, 15, 20, 19, 18, 17, 16, 11, 6, 7, 8, 9, 14, 13, 12 ]
          

          【讨论】:

            【解决方案6】:

            我习惯了 C#:

                public static IList<int> spiralTraversal (int[,] matrix)
                {
                    IList<int> list = new List<int>();            
            
                    // Get all bounds before looping.
                    int bound0 = matrix.GetUpperBound(0);
                    int bound1 = matrix.GetUpperBound(1);
            
                    int totalElem = (bound0+1) * (bound1+1);
            
                    int auxbound0 = 0;
                    int auxbound1 = 0;
            
                    string direction = "left";
            
                    int leftCtrl = 0;
                    int rightCtrl = 0;
                    int upCtrl = 0;
                    int downCtrl = 0;
            
                    for (int i=0;i< totalElem;i++)
                    {
                        if (direction == "down")
                        {
                            list.Add(matrix[auxbound0, auxbound1]);
                            if (auxbound0 == bound0 - downCtrl)
                            {
                                direction = "right";
                                auxbound1 -= 1;
                                downCtrl += 1;
                                continue;
                            }
                            else
                            {
                                auxbound0 += 1;
                            }
                        }
            
                        if (direction == "left")
                        {
                            list.Add(matrix[auxbound0, auxbound1]);
                            if (auxbound1 == bound1 - leftCtrl)
                            {
                                direction = "down";
                                auxbound0 += 1;
                                leftCtrl += 1;
                                continue;
                            }
                            else
                            {
                                auxbound1 += 1;
                            }
                        }
            
                        if (direction == "up")
                        {
                            list.Add(matrix[auxbound0, auxbound1]);
                            if (auxbound0 == 1 + upCtrl)
                            {
                                direction = "left";
                                auxbound1 += 1;
                                upCtrl += 1;
                                continue;
                            }
                            else
                            {
                                auxbound0 -= 1;
                            }
                        }
            
                        if (direction == "right")
                        {
                            list.Add(matrix[auxbound0, auxbound1]);
                            if (auxbound1 == rightCtrl)
                            {
                                direction = "up";
                                auxbound0 -= 1;
                                rightCtrl += 1;
                                continue;
                            }
                            else
                            {
                                auxbound1 -= 1;
                            }
                        }
                    }
            
                    return list;
                }
            

            【讨论】:

              【解决方案7】:

              此解决方案适用于任何类型的矩阵 (m * n),而不仅仅是正方形 (m * m)。下面的示例采用 5*4 矩阵并以螺旋格式打印。

              var matrix =  [[1,2,3,4], [14,15,16,5], [13,20,17,6], [12,19,18,7], [11,10,9,8]];
              
              var row = currentRow = matrix.length, column = currentColumn = matrix[0].length;
              
              while(currentRow > row/2 ){
              
                // traverse row forward
                for(var i = (column - currentColumn); i < currentColumn ; i++) { console.log(matrix[row - currentRow][i]); }
              
                // traverse column downward
                for(var i = (row - currentRow + 1); i < currentRow ; i++) { console.log(matrix[i][currentColumn - 1]) }
              
                // traverse row backward
                for(var i = currentColumn - 1; i > (column - currentColumn) ; i--) { console.log(matrix[currentRow - 1][i - 1]); }
              
                // traverse column upward
                for(var i = currentRow - 1; i > (row - currentRow + 1) ; i--) { console.log(matrix[i - 1][column - currentColumn]) }
              
                currentRow--;
                currentColumn--;
              }
              

              【讨论】:

                【解决方案8】:

                ? 螺旋阵列 (ES6)

                ES6 让我们保持简单:

                function spiral(matrix) {
                  const arr = [];
                    
                  while (matrix.length) {
                    arr.push(
                      ...matrix.shift(),
                      ...matrix.map(a => a.pop()),
                      ...(matrix.pop() || []).reverse(),
                      ...matrix.map(a => a.shift()).reverse()
                    );
                  }
                  return arr;
                }
                

                【讨论】:

                • 告诉我,是否有可能在 PHP 中实现?
                • @Lior Elrom 代码简洁明了。从未想过传播运算符可以用于此任务。
                • 例如,如果您在矩阵大小 N*M (5*4) 上尝试它,则会出现“未定义”异常。用 try 和 catch 包围循环正在解决这个问题。无论如何,这是一个很好的解决方案。
                • @MatanTubul 好点。不要使用 try catch,而是将 ...matrix.pop().reverse() 替换为 ...(matrix.pop() || []).reverse() 以解决非数组结果。
                • @LiorElrom ...matrix.pop()?.reverse?.() ?? []
                【解决方案9】:

                以下是一个 Javascript 解决方案。我已将 cmets 添加到代码中,以便您可以按照流程进行操作:)

                var array = [ 
                    [1,  2,  3,   4],
                    [5,  6,  7,   8],
                    [9,  10, 11,  12],
                    [13, 14, 15,  16]  
                ];
                
                var n = array.length;
                
                //create empty 2d array
                
                var startRow = 0;
                var endRow = n - 1;
                var startColumn = 0;
                var endColumn = n - 1
                var newArray = [];
                
                // While loop is used to spiral into the 2d array.
                while(startRow <= endRow && startColumn <= endColumn) {
                
                    // Reading top row, from left to right
                    for(var i = startColumn; i <= endColumn; i++) {
                        newArray.push(array[startColumn][i]);
                    }
                    startRow++; // Top row read.
                
                    // Reading right column from top right to bottom right
                    for(var i = startRow; i <= endRow; i++) {
                        newArray.push(array[i][endColumn]);
                    }
                    endColumn--; // Right column read
                
                    // Reading bottom row, from bottom right to bottom left
                    for(var i = endColumn; i >= startColumn; i--) {
                        newArray.push(array[endRow][i]);
                    }
                    endRow--; // Bottom row read
                
                    // Reading left column, from bottom left to top left
                    for(var i = endRow; i >= startRow; i--) {
                        newArray.push(array[i][startColumn]);
                    }
                    startColumn++; // left column now read.
                
                } // While loop will now spiral in the matrix.
                
                console.log(newArray);

                :)

                【讨论】:

                  【解决方案10】:

                  此解决方案采用螺旋阵列并将其转换为有序阵列

                  它以上、右、下、左的格式对螺旋矩阵进行排序。

                  const matrix = [
                    [1, 2, 3, 4, 5],
                    [16, 17, 18, 19, 6],
                    [15, 24, 25, 20, 7],
                    [14, 23, 22, 21, 8],
                    [13, 12, 11, 10, 9],
                  ];
                  
                  function getOrderdMatrix(matrix, OrderdCorner) {
                    // If the Matrix is 0 return the OrderdCorner
                    if (matrix.length > 0) {
                  
                      //Pushes the top of the matrix to OrderdCorner array
                      OrderdCorner.push(...matrix.shift());
                  
                      let left = [];
                      /*Pushes right elements to the Orderdcorner array and
                       Add the left elements to the left array */
                      for (let i = 0; i < matrix.length; i++) {
                        OrderdCorner.push(matrix[i][matrix[i].length - 1])
                        matrix[i].pop(); //Remove Right element
                  
                        if (matrix[i].length > 0) {
                          //Starts from the last element of the left corner 
                          left.push(matrix[(matrix.length - 1) - i][0])
                          matrix[(matrix.length - 1) - i].shift();
                        }
                      }
                  
                      /* If the array length is grater than 0 add the bottom
                      to the OrderdCorner array */
                      if (matrix.length > 0) {
                        OrderdCorner.push(...matrix.pop().reverse());
                      }
                      //Ads the left array to the OrderdCorner array
                      OrderdCorner.push(...left);
                  
                      return getOrderdMatrix(matrix, OrderdCorner);
                    } else {
                      return OrderdCorner
                    }
                  }
                  
                  console.log(getOrderdMatrix(matrix,[]));

                  退货 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]

                  【讨论】:

                    【解决方案11】:

                    这是一个可配置的版本:

                    function spiral(n) {
                    
                    // Create 2D array of size n*n
                    var matrix = new Array(n);
                      for(var i=0; i < matrix.length; i++) {
                         matrix[i] = new Array(n);
                      }
                    
                      for(var i=0; i < n;i++) {
                        for(var j=0; j < n; j++) {
                           matrix[i][j] = 0;
                        }
                      }
                    
                      var startNum = 0;
                      var rowNum = 0;
                    
                      function spin(rowNum) {
                    
                       // right
                       for(var j=rowNum; j < (n-rowNum); j++) {
                          startNum++; 
                          matrix[rowNum][j] = startNum;
                       }
                    
                       if(startNum === (n*n)) {
                          return; // exit if number matches to the size of the matrix. ( 16 = 4*4 )
                       }
                    
                       // down
                       for(var i=(rowNum+1); i < (n-(rowNum+1)); i++) {
                         startNum++; 
                         matrix[i][n-(rowNum+1)] = startNum;
                       }
                    
                       if(startNum === (n*n)) {
                          return; // exit if number matches to the size of the matrix. ( 16 = 4*4 )
                       }
                    
                      // left
                       for(var j=(n-(1+rowNum)); j >= rowNum; j--) {
                         startNum++; 
                         matrix[(n-(1+rowNum))][j] = startNum;
                       }
                    
                    
                       if(startNum === (n*n)) {
                          return; // exit if number matches to the size of the matrix. ( 16 = 4*4 )
                       }
                    
                       //top
                       for(var i=(n-(2+rowNum)); i > rowNum; i--) {
                          startNum++; 
                          matrix[i][rowNum] = startNum;
                       }
                    
                      if(startNum === (n*n)) {
                          return; // exit if number matches to the size of the matrix. ( 16 = 4*4 )
                       }
                    
                      spin(rowNum+1);
                    
                    
                     }  
                    
                      spin(rowNum);
                    
                      console.log(matrix)
                    }
                    
                    spiral(6);
                    

                    例如:https://jsfiddle.net/dino_myte/276ou5kb/1/

                    【讨论】:

                      【解决方案12】:

                      const spiralOrder = matrix => {
                        if (!matrix || matrix.length === 0) {
                          return [];
                        }
                        let startRow = 0;
                        let startCol = 0;
                      
                        let ans = [];
                        let endCol = matrix[0].length - 1;
                        let endRow = matrix.length - 1;
                      
                        while (startRow <= endRow && startCol <= endCol) {
                          for (let i = startCol; i <= endCol; i++) {
                            ans.push(matrix[startRow][i]);
                          }
                      
                          startRow++;
                      
                          for (let i = startRow; i <= endRow; i++) {
                            ans.push(matrix[i][endCol]);
                          }
                          endCol--;
                      
                          if (startRow <= endRow) {
                            for (let i = endCol; i >= startCol; i--) {
                              ans.push(matrix[endRow][i]);
                            }
                            endRow--;
                          }
                      
                          if (startCol <= endCol) {
                            for (let i = endRow; i >= startRow; i--) {
                              ans.push(matrix[i][startCol]);
                            }
                            startCol++;
                          }
                        }
                        return ans;
                      };
                      
                      let input = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
                      //Output: [1, 2, 3, 6, 9, 8, 7, 4, 5];
                      spiralOrder(input);

                      【讨论】:

                        【解决方案13】:

                        关于这个漂亮的玩具问题,我已经写了一段时间的文章,我真的很喜欢。您可能需要查看我的解决方案。

                        你可以在 Medium 上关注我,也可以从here.查看我的文章

                        var spiralTraversal = function (matrix, result = []) {
                          // TODO: Implement me!
                        
                          //  if the length of the matrix ==0 we will return the result
                          if (matrix.length == 0) {
                            return result;
                          }
                          // we need to push the elements inside the first element of the array then delete this element
                          while (matrix[0].length) {
                            result.push(matrix[0].shift());
                          }
                          //top right to bottom right
                          matrix.forEach((row) => {
                            result.push(row.pop());
                          });
                          //bottom right to bottom left
                          while (matrix[matrix.length - 1].length) {
                            result.push(matrix[matrix.length - 1].pop());
                          }
                          //reverse again so we can retraverse on the next iteration
                          matrix.reverse();
                          //filter out any empty arrays
                          matrix = matrix.filter((element) => element.length);
                        
                          //recursive case
                          result = spiralTraversal(matrix, result);
                        
                          //return the result and filter any undefined elements
                          return result.filter((element) => element);
                        };
                        
                        

                        【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2014-02-02
                        • 1970-01-01
                        • 2016-11-27
                        • 1970-01-01
                        • 2021-05-18
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多