最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

algorithm - how to generate spiral matrix in javascript? - Stack Overflow

programmeradmin8浏览0评论

I am trying to generate sprial matrix in javascript.

question Given an integer A, generate a square matrix filled with elements from 1 to A^2 in spiral order.

input : 3

 [   [ 1, 2, 3 ],
     [ 8, 9, 4 ],
     [ 7, 6, 5 ]   ]

when input is 4

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

my approach is to create 2d array with 0 value and after that they will fill values.

let generateMatrix = function(A) {
  let arr = [], counter = 1;
  for (let i = 0; i < A; i++) {
    let items = []
    for (let j = 0; j < A; j++) {
      items.push(0)
    }
    arr.push(items)
  }

  var spiralMatrix = function(arr) {
    if (arr.length > 1) {
      for (let i = 0; i < arr[0].length; i++) {
        arr[0][i] = counter++;
      }
    }
    return arr
  }
  return spiralMatrix(arr)
}
console.log(generateMatrix(2))

I am trying to generate sprial matrix in javascript.

question Given an integer A, generate a square matrix filled with elements from 1 to A^2 in spiral order.

input : 3

 [   [ 1, 2, 3 ],
     [ 8, 9, 4 ],
     [ 7, 6, 5 ]   ]

when input is 4

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

my approach is to create 2d array with 0 value and after that they will fill values.

let generateMatrix = function(A) {
  let arr = [], counter = 1;
  for (let i = 0; i < A; i++) {
    let items = []
    for (let j = 0; j < A; j++) {
      items.push(0)
    }
    arr.push(items)
  }

  var spiralMatrix = function(arr) {
    if (arr.length > 1) {
      for (let i = 0; i < arr[0].length; i++) {
        arr[0][i] = counter++;
      }
    }
    return arr
  }
  return spiralMatrix(arr)
}
console.log(generateMatrix(2))

Share Improve this question edited May 20, 2021 at 18:28 גלעד ברקן 24k3 gold badges29 silver badges63 bronze badges asked Mar 19, 2020 at 15:57 user12130378user12130378 7
  • This is not a duplicate. The acclaimed original is about reading a matrix in a spiral fashion. The OP is about creating one – yunzen Commented Mar 19, 2020 at 16:06
  • yes this is not duplicate question – user12130378 Commented Mar 19, 2020 at 16:07
  • Sorry, I don't usually read them that quickly. Reopened. – Scott Sauyet Commented Mar 19, 2020 at 16:08
  • There are several possible approaches available here: rosettacode/wiki/Spiral_matrix#C.23 (this link points to the javascript solution). Just shift the indexes accordingly to get the desired result. – briosheje Commented Mar 19, 2020 at 16:31
  • Love the question. May I ask what is the use-case? – IMTheNachoMan Commented Mar 19, 2020 at 17:25
 |  Show 2 more ments

10 Answers 10

Reset to default 2

This is in some ways the reverse of an answer I gave to another question. We can recursively build this up by slicing out the first row and prepending it to the result of rotating the result of a recursive call on the remaining numbers:

const reverse = a => 
  [...a] .reverse ();

const transpose = m => 
  m [0] .map ((c, i) => m .map (r => r [i]))

const rotate = m => 
  transpose (reverse (m))
  
const makeSpiral = (xs, rows) =>
  xs .length < 2
    ? [[... xs]]
    : [
        xs .slice (0, xs .length / rows),
        ... rotate(makeSpiral (xs .slice (xs .length / rows), xs.length / rows))
      ]

const range = (lo, hi) =>
  [...Array (hi - lo + 1)] .map ((_, i) => lo + i)

const generateMatrix = (n) => 
  makeSpiral (range (1, n * n), n)

console .log (generateMatrix (4))

A sharp eye will note that rotate is different here from the older question. transpose (reverse (m)) returns a clockwise rotated version of the input matrix. reverse (transpose (m)) returns a counter-clockwise rotated one. Similarly, here we rotate the result of the recursive call before including it; whereas in the other question we recurse on the rotated version of the matrix. Since we're reversing that process, it should be reasonably clear why.

The main function is makeSpiral, which takes an array and the number of rows to spiral it into and returns the spiraled matrix. (If rows is not a factor of the length of the array, the behavior might be crazy.) generateMatrix is just a thin wrapper around that to handle your square case by generating the initial array (using range) and passing it to makeSpiral.

Note how makeSpiral works with rectangles other than squares:

makeSpiral ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 2) //=>
// [
//   [ 1,  2,  3,  4,  5,  6], 
//   [12, 11, 10,  9,  8,  7]
// ]

makeSpiral ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 3) //=>
// [
//   [ 1,  2,  3,  4], 
//   [10, 11, 12,  5], 
//   [ 9,  8,  7,  6]
// ]

makeSpiral ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 4) //=>
// [
//   [ 1,  2,  3], 
//   [10, 11,  4], 
//   [ 9, 12,  5], 
//   [ 8,  7,  6]
// ]

makeSpiral ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 6) //=>
// [
//   [ 1, 2], 
//   [12, 3], 
//   [11, 4], 
//   [10, 5], 
//   [ 9, 6], 
//   [ 8, 7]
// ]

The other functions -- range, reverse, transpose, and rotate -- are general purpose utility functions for working with arrays or matrices.

You could take loops for each edges and loop until no more ranges are avaliable.

function spiral(length) {
    var upper = 0,
        lower = length - 1,
        left = 0,
        right = length - 1,
        i = 0,
        j = 0,
        result = Array.from({ length }, _ => []),
        value = 1;

    while (true) {
        if (upper++ > lower) break;

        for (; j < right; j++) result[i][j] = value++;
        if (right-- < left) break;

        for (; i < lower; i++) result[i][j] = value++;

        if (lower-- < upper) break;

        for (; j > left; j--) result[i][j] = value++;
        if (left++ > right) break;

        for (; i > upper; i--) result[i][j] = value++;
    }

    result[i][j] = value++;
    return result;
}

var target = document.getElementById('out'),
    i = 10;

while (--i) target.innerHTML += spiral(i).map(a => a.map(v => v.toString().padStart(2)).join(' ')).join('\n') + '\n\n';
<pre id="out"></pre>

This bit of code should do what you are trying to.

// This is your Editor pane. Write your JavaScript hem and
// use the mand line to execute mands
let generateMatrix = function(A) {
  let arr = [],
    counter = 1;
  for (let i = 0; i < A; i++) {
    let items = [];
    for (let j = 0; j < A; j++) {
      items.push(0);
    }
    arr.push(items);
  }

  var spiralMatrix = function(arr) {
    let count = 1;
    let k = 0; // starting row
    let m = arr.length; // ending row
    let l = 0; // starting column
    let n = arr[0].length; //ending column

    while (k < m && l < n) {
      // top
      for (var i = l; i < n; i++) {
        arr[k][i] = count;
        count++;
      }
      k++;

      // right
      for (var i = k; i < m; i++) {
        arr[i][n - 1] = count;
        count++;
      }
      n--;

      // bottom
      if (k < m) {
        for (var i = n - 1; i >= l; i--) {
          arr[m - 1][i] = count;
          count++;
        }
        m--;
      }

      // left
      if (l < n) {
        for (var i = m - 1; i >= k; i--) {
          arr[i][l] = count;
          count++;
        }
        l++;
      }
    }
    return arr;
  };
  return spiralMatrix(arr);
};

console.log(generateMatrix(4));

Here's one solution.

I keep the current "moving direction" in dx and dy, such that the next matrix element indices are given by x+dx and y+dy.

If the next item is already filled or is out of bounds, I change this direction clockwise. Otherwise, I fill it with the next value.

const size = 6;
const matrix = Array(size).fill().map(() => Array(size).fill(0));

let x = -1;
let y = 0;
let dx = 1;
let dy = 0;

function changeDirection() {
  if (dx === 1) {
    dx = 0;
    dy = 1;
  } else if (dy === 1) {
    dy = 0;
    dx = -1;
  } else if (dx === -1) {
    dx = 0;
    dy = -1;
  } else {
    dx = 1;
    dy = 0;
  }
}

for (let i = 0; i < size * size; i++) {
  const yNext = y + dy;
  const xNext = x + dx;
  const nextRow = matrix[yNext] || [];
  const nextItemContent = nextRow[xNext];
  if (nextItemContent === undefined || nextItemContent > 0) {
    changeDirection();
    i--;
    continue;
  }
  y = yNext;
  x = xNext;
  matrix[y][x] = i + 1;
}

const result = document.getElementById('result');
matrix.forEach(row => {
  row.forEach(value => {
    result.innerHTML += value.toString().padStart(3);
  });
  result.innerHTML += '\n';
});
<pre id="result"></pre>

I'm calculating the index, each number should go in a linear array

console.clear();

Array.prototype.range = function(a, b, step) {
    step = !step ? 1 : step;
    b = b / step;
    for(var i = a; i <= b; i++) {
        this.push(i*step);
    }
    return this;
};

const spiral = function(dimen) {
  "use strict";
  const dim = dimen;
  const dimw = dim;
  const dimh = dim;

  var steps = [1, dimh, -1, -dimh];
  var stepIndex = 0;
  var count = 1;
  var countMax = dimw
  var dec = 0
  var index = 0;
  
  var arr = []; 
  arr = arr.range(1, dimh * dimw)
  const newArr = arr.reduce((coll, x, idx) => {
    index += steps[stepIndex]
    coll[index-1] = idx+1;

    if (count === countMax) {count = 0; stepIndex++; dec++;}
    if (dec === 1) {dec = -1; countMax--}
    if (stepIndex == steps.length) {stepIndex = 0}
    
    count++;
                   
    return coll;
  }, []);
  
  var ret = []
  while (newArr.length) {
    ret.push(newArr.splice(0,dimw))
  }
  return ret
}

console.log(spiral(3))
console.log(spiral(4))
console.log(spiral(5))

var n=14; // size of spiral 
var s=[]; // empty instruction string
function emp() {} // no move
function xpp() {xp++;} // go right
function xpm() {xp--;} // go left
function ypp() {yp++;} // go down
function ypm() {yp--;} // go up
var r=[xpp,ypp,xpm,ypm]; // instruction set
s.push(emp); // push 'no move' (used for starting point)
var c=n-1;
while (c-->0) s.push(r[0]); // push first line - uses a different rule
for (var i=1;i<2*n-1;i++) {  // push each leg
    c=Math.floor((2*n-i)/2);
    while (c-->0) s.push(r[i%4]);
}
var sp=new Array(n); // spiral array
for (var i=0;i<n;i++) sp[i]=new Array(n);
var xp=0; // starting position
var yp=0;
for (var i=0;i<n*n;i++) {
    s[i](); // execute next instruction
    sp[yp][xp]=i+1; // update array
}
for (var i=0;i<n;i++) console.log(sp[i].toString()); // log to console

This code makes a macro of functions to generate a run sequence, for example:

'right4, down4, left4, up3, right3, down2, left2, up1, right1

and then implements it.

Here is a solution to Spiral Matrix from leetcode, maybe this can help

https://leetcode./problems/spiral-matrix/

var spiralOrder = function(matrix) {
  if (matrix.length == 0) {
    return [];
  }
  let result = [];
  let rowStart = 0;
  let rowEnd = matrix.length - 1;
  let colStart = 0;
  let colEnd = matrix[0].length - 1;
  while (true) {
    // top
    for (let i = colStart; i <= colEnd; i++) {
      result.push(matrix[rowStart][i]);
    }
      
    rowStart++;
      
    if (rowStart > rowEnd) {
      return result;
    }
      
    // right
    for (let i = rowStart; i <= rowEnd; i++) {
      result.push(matrix[i][colEnd]);
    }
      
    colEnd--;
      
    if (colEnd < colStart) {
      return result;
    }
      
    // bottom
    for (let i = colEnd; i >= colStart; i--) {
      result.push(matrix[rowEnd][i]);
    }
      
    rowEnd--;
      
    if (rowEnd < rowStart) {
      return result;
    }
      
    // left
    for (let i = rowEnd; i >= rowStart; i--) {
      result.push(matrix[i][colStart]);
    }
      
    colStart++;
      
    if (colStart > colEnd) {
      return result;
    }
      
  }
  return result;
};

console.log(
  spiralOrder([[2, 3, 4], [5, 6, 7], [8, 9, 10], [11, 12, 13], [14, 15, 16]])
);
console.log(spiralOrder([[7], [9], [6]]));
console.log(spiralOrder([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]));
console.log(spiralOrder([[1, 2, 3], [4, 5, 6], [7, 8, 9]]));

Here's my answer using only one for loop -

function matrix(n) {
  const arr = [];
  let row = 0;
  let column = 0;
  let counter = 1;
  let edge = n - 1;

  let leftToRightRow = false;
  let topToBottomCol = false;
  let rightToLeftRow = false;
  let bottomToTopCol = false;

  for (i = 0; i < n * n; i++) {
    if (column <= edge && !leftToRightRow) {
      if (!Array.isArray(arr[row])) {
        arr[row] = []; // if array is not present at this index, then insert one
      }
      arr[row][column] = counter;
      if (column == edge) {
        row = row + 1;
        leftToRightRow = true;
      } else {
        column = column + 1;
      }
      counter = counter + 1;
    } else if (column === edge && !topToBottomCol) {
      if (!Array.isArray(arr[row])) {
        arr[row] = []; // if array is not present at this index, then insert one
      }
      arr[row][column] = counter;
      if (row === edge) {
        column = column - 1;
        topToBottomCol = true;
      } else {
        row = row + 1;
      }
      counter = counter + 1;
    } else if (column >= 0 && !rightToLeftRow) {
      arr[row][column] = counter;
      if (column === 0) {
        row = row - 1;
        rightToLeftRow = true;
      } else {
        column = column - 1;
      }
      counter = counter + 1;
    } else if (row >= n - edge && !bottomToTopCol) {
      arr[row][column] = counter;
      if (row === n - edge) {
        column = column + 1;
        bottomToTopCol = true;
        //setting these to false for next set of iteration
        leftToRightRow = false;
        topToBottomCol = false;
        rightToLeftRow = false;
        edge = edge - 1;
      } else {
        row = row - 1;
      }
      counter = counter + 1;
    }
  }
  return arr;
}

Solution is implemented in C++, but only logic matter then you can do it in any language:

vector<vector<int> > Solution::generateMatrix(int A) {
    vector<vector<int>> result(A,vector<int>(A));
    int xBeg=0,xEnd=A-1;
    int yBeg=0,yEnd=A-1;
    int cur=1;
    while(true){
        for(int i=yBeg;i<=yEnd;i++) 
            result[xBeg][i]=cur++;

        if(++xBeg>xEnd) break;

        for(int i=xBeg;i<=xEnd;i++) 
            result[i][yEnd]=cur++;

        if(--yEnd<yBeg) break;

        for(int i=yEnd;i>=yBeg;i--) 
            result[xEnd][i]=cur++;

        if(--xEnd<xBeg) break;

        for(int i=xEnd;i>=xBeg;i--) 
            result[i][yBeg]=cur++;
            
        if(++yBeg>yEnd) break;
    }
    return result; 
}

Solition in c#:

For solving this problem we use loops for each moving directions

     public IList<int> SpiralOrder(int[][] matrix) {
        var result = new List<int>();
            var n = matrix[0].Length;
            var m = matrix.Length;
            var i = 0;
            var j = 0;
            var x = 0;
            var y = 0;

            while (true)
            {
                //left to right moving:
                while (x <= n - 1 - i)
                {
                    result.Add(matrix[y][x]);
                    x++;
                }
                if (result.Count == n * m)
                    return result;
                x--;y++;

                //up to down moving:
                while (y <= m - 1 - j)
                {
                    result.Add(matrix[y][x]);
                    y++;
                }
                if (result.Count == n * m)
                    return result;
                y--;x--;

                //right to left moving:
                while (x >= j)
                {
                    result.Add(matrix[y][x]);
                    x--;
                }
                if (result.Count == n * m)
                    return result;
                x++;y--;

                //down to up moving:
                while (y > j)
                {
                    result.Add(matrix[y][x]);
                    y--;
                }
                if (result.Count == n * m)
                    return result;
                y++;x++;
                i++;
                j++;                
            }
    }
发布评论

评论列表(0)

  1. 暂无评论