矩阵的螺旋遍历 - JavaScript 中的递归解决方案
Posted
技术标签:
【中文标题】矩阵的螺旋遍历 - JavaScript 中的递归解决方案【英文标题】:Spiral traversal of a matrix - recursive solution in 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);
;
【问题讨论】:
【参考方案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]
一般的想法是我们知道每次通过我们需要做这些事情:
-
在输入中添加第一个数组
在输入中添加每个剩余数组的最后一项
在输入中添加最后一个数组
在输入中添加每个剩余数组中的第一项
因此,如果我们在每次通过时都进行递归,我们就可以完成螺旋。
JSFiddle:http://jsfiddle.net/2v6k5uhd/
【讨论】:
【参考方案2】:你的算法看起来不错,只有一个错误有几件事,有些比其他的更难发现。
concat
method 不会改变数组(就像push
一样),但会返回一个新数组,其中包含原始数组中的所有元素和参数。 result
没有发生变异。
要解决此问题,您可以
使用result = result.concat(…);
使其成为一个显式循环,您可以在其中执行result.push(…)
(如您已经编写的向下、向左和向上的循环)或
使用result.push@987654322@(result, …)
一次推送多个值
您的“左”或“上”循环确实漏掉了一个元素,即左下角的元素。要么向左走,你需要前进到第一个元素(在条件中使用>= 0
),或者向上走时,你需要从最后一行而不是倒数第二行开始(matrix.length-1
)李>
在为下一次迭代收缩矩阵的循环中,您忘记了最后一行,它必须是for (var i=0; i < temp.length; i++)
(而不是temp.length-1
)。否则你会得到非常不幸的结果。
您的基本情况应该是 0(和 1),而不是(1 和)2。这将简化您的脚本并避免错误(在极端情况下)。
您希望您的矩阵是正方形的,但它们可能是矩形的(甚至有不均匀长度的线)。您正在访问的 .length
可能不是您所期望的 - 最好仔细检查并抛出带有描述性消息的错误。
spiralTraversal
和 goAround
都缺少用于(递归)调用的 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);
;
【讨论】:
请注意我对类似问题的simpler answer 的看法。以上是关于矩阵的螺旋遍历 - JavaScript 中的递归解决方案的主要内容,如果未能解决你的问题,请参考以下文章