矩阵的螺旋遍历 - JavaScript中的递归解决方案

时间:2015-06-18 04:19:21

标签: javascript arrays algorithm recursion matrix

我试图想出一个像这样的矩阵的解决方案:

[[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]

我无法使这个递归解决方案工作,其中结果数组采用第一个数组,其余数组的最终元素,反向顺序的底部数组,然后是第一个元素中间阵列,然后改造阵列没有外部" shell"这样就可以递归地调用剩下的东西,直到中心有一个元素的数组或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);  
};

13 个答案:

答案 0 :(得分:12)

螺旋阵列(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;
}

答案 1 :(得分:9)

你的代码非常接近,但它的功能超出了它的需要。在这里,我简化并修复错误:

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/

重要的事情:

    Array上的
  • concat返回结果 - 它不会改变调用者,因此您需要保存concat的结果,如下所示:result = result.concat(otherArray)
  • 检查递归数组顶部的终止条件
  • 每次通过,执行预期(顶部,右侧,底部,左侧)
  • 返回结果

以下是我将如何做,但我会添加错误检查以验证数组具有相同数量的&#34;行&#34;和#34;列&#34;。所以假设输入有效,我们在这里:

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. 在输入中添加最后一个数组
  5. 从输入
  6. 中的每个剩余数组中添加第一项

    因此,如果我们在每次传递中执行递归操作,我们就可以完成螺旋式连接。

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

答案 2 :(得分:5)

此解决方案适用于任何类型的矩阵(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--;
}

答案 3 :(得分:4)

你的算法似乎很好,只有一个错误有一些东西,有些东西比其他东西更难发现。

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

    要解决此问题,您可以

    • 使用result = result.concat(…);
    • 将其设为result.push(…)的显式循环(如您已编写的向下,向左和向上)或
    • 使用result.push.apply(result, …)一次推送多个值
  2. 你的&#34;离开&#34;或&#34; up&#34;循环确实错过了一个元素,左下角的元素。在向左移动时,您需要前进到第一个元素(在条件中使用>= 0),或者在上升时您需要从最后一个开始而不是从倒数第二行开始({{1} })
  3. 在缩小下一次迭代矩阵的循环中,您忘记了最后一行,它必须是matrix.length-1(不是for (var i=0; i < temp.length; i++))。否则你会得到非常不幸的结果。
  4. 你的基本情况应该是0(和1),而不是(1和)2。这将简化你的脚本并避免错误(在边缘情况下)。
  5. 您希望矩阵是方形的,但它们可以是矩形(甚至有不均匀长度的线)。您正在访问的temp.length-1可能不是您期望的那个 - 更好的双重检查并使用描述性消息抛出错误。
  6. .lengthspiralTraversal都缺少(递归)调用的goAround语句。他们只填写return,但不会退货。

答案 4 :(得分:2)

递归解决方案:

而不是绕过,我只是越过顶行和最右边的列,然后递归调用&#34;反转&#34;基质

&#13;
&#13;
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)
&#13;
&#13;
&#13;

答案 5 :(得分:2)

这是我的功能:

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 :(得分:1)

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

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;
  }
};

答案 7 :(得分:1)

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);

答案 8 :(得分:0)

我习惯于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;
    }

答案 9 :(得分:0)

以下是Javascript解决方案。我已经在代码中添加了注释,因此您可以按照以下步骤进行操作:)

    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 :(得分:0)

此解决方案采用螺旋阵列,并将其转换为 Ordered Array

  

它按“上”,“右”,“下”,“左”的格式对螺旋矩阵进行排序。

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 :(得分:0)

这是可配置的版本:

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 :(得分:0)

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

您可以在 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);
};