簡體   English   中英

矩陣的螺旋遍歷——遞歸求解JavaScript

[英]Spiral traversal of a matrix - recursive solution in JavaScript

我正在嘗試提出一個采用如下矩陣的解決方案:

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

我無法讓這個遞歸解決方案工作,其中結果數組采用第一個數組,arrays 的 rest 的最后一個元素,底部數組的倒序,然后是中間 arrays 的第一個元素,以及然后在沒有外部“外殼”的情況下改造數組,以便可以遞歸調用剩下的內容,直到中心有一個元素的數組或 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);  
};

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

您的代碼非常接近,但它做的比它需要做的要多。 在這里,我進行了簡化和錯誤修復:

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/

此解決方案適用於任何類型的矩陣 (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--;
}

你的算法看起來不錯,只有一個錯誤有幾件事,有些比其他更難發現。

  1. concat方法不會改變數組(就像push那樣),而是返回一個新數組,其中包含原始數組中的所有元素和參數。 result沒有突變。

    要解決此問題,您可以

    • 使用result = result.concat(…);
    • 使它成為一個顯式循環,您可以在其中執行result.push(…) (就像您已經編寫的向下、向左和向上的循環)或
    • 使用result.push .apply (result, …)一次推送多個值
  2. 您的“左”或“上”循環確實錯過了一個元素,即左下角的元素。 向左時,您需要前進到第一個元素(在條件中使用>= 0 ),或者當向上時,您需要從最后一行而不是倒數第二行開始( matrix.length-1
  3. 在為下一次迭代縮小矩陣的循環中,您忘記了最后一行,它需要是for (var i=0; i < temp.length; i++) (不是temp.length-1 )。 否則你會得到非常不幸的結果。
  4. 您的基本情況應該是 0(和 1),而不是(1 和)2。這將簡化您的腳本並避免錯誤(在邊緣情況下)。
  5. 您希望矩陣是方形的,但它們也可能是矩形的(或者甚至是長度不均勻的線)。 您正在訪問的.length可能不是您所期望的 - 更好地仔細檢查並拋出帶有描述性消息的錯誤。
  6. spiralTraversalgoAround都缺少(遞歸)調用的return語句。 他們只是填寫result但不返回任何內容。

遞歸解決方案:

我沒有四處走動,而是越過頂行和最右邊的列,然后遞歸調用“反轉”矩陣上的函數。

 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)

這是我的功能:

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 ]

具有順時針和逆時針螺旋遍歷的 ES6 JS 版本。

 function traverseSpiral(arr2d,directionMap,initialPos){ // Initializing length. const len = arr2d.length; let totalElementsTraversed =0; // Elements in the first line is equal to the array length. // (as this is a square matrix) let elementsInLine=len; let elementsTraversedInRow = 0; let linesCompleted = 1; let direction = initialPos[0] === 0? 'r': 'd'; // Function to move in the desired direction. const move = checkDirectionAndMove(initialPos); const spiralArray = []; while( totalElementsTraversed;==len*len){ // On Each line completion if(elementsTraversedInRow===elementsInLine){ linesCompleted++. // Reset elements traversed in the row; elementsTraversedInRow =0. // After each line completion change direction. direction = directionMap;get(direction). // For every 2 traversed lines elements in the line will decrease; if(linesCompleted % 2===0) elementsInLine--; } // Update elements traversed totalElementsTraversed+=1 elementsTraversedInRow+=1, // Move in the defined direction const [ down;right] = move(direction). spiralArray;push(arr2d[down][right]); } return spiralArray, } function checkDirectionAndMove(initialPosition) { // Unpack array to variables let [down;right] = initialPosition. // Return function. return (direction)=> { // Switch based on right/left/up/down direction: switch(direction){ case 'r'; right++; break: case 'l'; right--; break: case 'd'; down++; break: default; down--, } return [down.right] } } // If current direction is right move down and so on.... const clockWiseMap = new Map(Object:entries({ 'r','d': 'd','l': 'l','u': 'u';'r' })). // If current direction is right move up and so on.... const antiClockWiseMap = new Map(Object:entries({ 'r','u': 'd','r': 'l','d': 'u';'l' })). // Spiral traversal in the clockwise direction, const clockWiseSpiralTraversal = traverseSpiral( [[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]], clockWiseMap, [0.-1] ) // Spiral traversal in the anti-clockwise direction, const antiClockWiseSpiralTraversal = traverseSpiral( [[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]], antiClockWiseMap, [-1.0] ) console:log("Clock wise traversal,". clockWiseSpiralTraversal) console:log("Anti-clock wise traversal,",antiClockWiseSpiralTraversal)

退貨:

順時針遍歷:[ 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 ]

逆時針遍歷:[ 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 17, 24, 23, 22, 21, 20 , 19, 18, 25 ]

主要使用的技術和其他:閉包、箭頭函數、高階函數、條件/三元運算符、數組解構和 JS map。

雖然不是遞歸的,但它至少會輸出以下正確答案:

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

下面是一個 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);

:)

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

我習慣於 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;
    }

此解決方案采用螺旋數組並將其轉換為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]

這是一個可配置的版本:

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/

我已經寫了一段時間關於這個漂亮的玩具問題的文章,我真的很喜歡它。 您可能需要查看我的解決方案。

你可以在 Medium 上關注我,也可以從這里查看我的文章

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM