【问题标题】:Spiral traversal of an array in JavaScriptJavaScript中数组的螺旋遍历
【发布时间】:2015-09-19 18:42:34
【问题描述】:

我正在解决一个编码问题并编写一个函数,该函数将以类似蜗牛的模式遍历嵌套数组(例如 here)。

例如,给定输入[[1,2,3], [8,9,4], [7,6,5]],函数应该输出[1,2,3,4,5,6,7,8,9]

在我的 main 函数中,我创建了 4 个函数来全方位遍历数组。在每一步中,我都会在当前网格位置推送值并检查标志数组以确定是否已访问下一个位置。

我有两个问题:

  1. 我无法成功克隆作为参数传递的数组。即使我使用了array.slice(0),新的flagsArray 似乎也充当了指向array 的指针,当我修改flagsArray 时,更改会反映在array 中。这是为什么呢?

当我运行这个测试时,我的克隆方法似乎奏效了:

var array = [0, 1];
var array_clone = array.slice(0);
array_clone[2] = 3;
console.log(array); // No changes to array
  1. 我在执行 traverseDown() 时收到 flagsArray[(i + 1)] is undefined 错误。这看起来像是试图读取超出flagsArray 的索引。但是,如果我在while 循环中检查flagsArray[i + 1][j] === false && i < flagsArray.length,那么循环是否应该在尝试读取数组之外时停止?一方面是因为flagsArray[flags.Array.length] 未定义,因此不等于false,另一个原因是我还要检查i 是否在flagsArray 范围内。

这是我的功能:

snail = function(array) {
  console.log(array);
  var output = [];

  var flagsArray = array.slice(0);

  // Populating flagsArray with false values
  // False = index not visited yet
  for (var i = 0; i < flagsArray.length; i++) {
    for (var j = 0; j < flagsArray[i].length; j++) {
      flagsArray[i][j] = false;
    }
  }
  var i = 0;
  var j = 0;

    var traverseRight = function() {
    while (flagsArray[i][j + 1] === false && j < flagsArray[i].length) {
      console.log(flagsArray[i][j].length + 'Traversing right Index traversed: [' + i + '][' + j +'] Flag before: ' + flagsArray[i][j] + 'Element pushed: ' + array[i][j]);
      output.push(array[i][j]);
      flagsArray[i][j] = true;
      j++;
    } 
  }

  var traverseDown = function() {
    while (flagsArray[i + 1][j] === false && i < flagsArray.length) {
      console.log('Traversing Down Index traversed: [' + i + '][' + j +'] Flag before: ' + flagsArray[i][j] + 'Element pushed: ' + array[i][j]);
      output.push(array[i][j]);
      flagsArray[i][j] = true;
      i++;
    }
  }

  var traverseLeft = function() {
    while (array[i][j - 1] === false && j >= 0) {
      console.log('Traversing left Index traversed: [' + i + '][' + j +'] Flag before: ' + flagsArray[i][j] + 'Element pushed: ' + array[i][j]);
      output.push(array[i][j]);
      flagsArray[i][j] = true;
      j--;
    }
  }

  var traverseUp = function() {
    while (array[i][j] === false && j >= 0) {
      console.log('Traversing Up Index traversed: [' + i + '][' + j +'] Flag before: ' + flagsArray[i][j] + 'Element pushed: ' + array[i][j]);
      output.push(array[i][j]);
      flagsArray[i][j] = true;
      i--;
    }
  }

  while (flagsArray[i][j + 1] === false) {
    traverseRight();
    traverseDown();
    traverseLeft();
    traverseUp();
  } 
  return output;
}

console.log(snail([[1,2,3],[8,9,4],[7,6,5]]));

【问题讨论】:

    标签: javascript arrays algorithm


    【解决方案1】:

    问题 1

    我无法成功克隆作为参数传递的数组。即使我使用了array.slice(0),新的flagsArray 似乎也充当了指向array 的指针,当我修改flagsArray 时,更改会反映在array 中。这是为什么呢?

    这是因为您有一个二维数组,而您只制作了外部数组的副本。它的每个元素都是一个原始的内部数组。

    要制作array 的深层副本,我们可以在不更改原始文件的情况下对其进行操作,我们必须复制每个内部数组:

    var flags = new Array(array.length);
    for (var i = 0; i < array.length; ++i) {
      flags[i] = array[i].slice();
    }
    

    (注意:我冒昧地将flagsArray 重命名为flags。另请注意,可以不带参数调用slice() 以从索引0 开始复制数组。)

    我们可以使用Array.map 更简洁地做同样的事情:

    var flags = array.map(function (subarray) {
      return subarray.slice();
    });
    

    但是如果我们立即用false 覆盖数组值,那么复制数组值是没有意义的:

    for (var i = 0; i < flags.length; i++) {
      for (var j = 0; j < flags[i].length; j++) {
        flags[i][j] = false;
      }
    }
    

    如果我们在制作时将 false 值放入标志数组中,我们可以删除该双循环:

    var flags = array.map(function (subarray) {
      return subarray.map(function () {
        return false;
      });
    });
    

    问题 2

    我在执行 traverseDown() 时收到 flagsArray[(i + 1)] is undefined 错误。这看起来像是试图读取超出flagsArray 的索引。但是,如果我在while 循环中检查flagsArray[i + 1][j] === false &amp;&amp; i &lt; flagsArray.length,循环不应该在尝试读取数组之外的内容时停止吗?

    不,JavaScript 不会阻止越界数组访问。该行发生了什么

    while (flagsArray[i + 1][j] === false && i < flagsArray.length) {
    

    flagsArray[i + 1] 计算结果为undefined,因为i + 1 超出了数组的最后一个元素。然后你尝试查找[j],程序崩溃了,因为undefined 没有属性。

    你甚至没有时间评估i &lt; flagsArray.length。此外,该测试不会阻止无效索引,因为如果i 是最后一个元素的索引,则flagsArray[i + 1] 超出范围。

    一般观察

    这个测试有两个问题:

    while (flagsArray[i][j + 1] === false && j < flagsArray[i].length) {
    

    首先,您忽略了在访问数组之前检查数组索引。其次,您没有测试正确的值。你应该在访问flagsArray[i][j + 1]之前测试j + 1

    while (j + 1 < flagsArray[i].length && flagsArray[i][j + 1] === false) {
    

    其他三个测试也存在类似问题。

    作为编写四个函数的替代方案,我建议您编写一次遍历逻辑,并使其依赖于具有从03 的值的orientation 变量。唯一与移动方向不同的是您增加网格索引的量。您可以在以orientation 作为数组索引的一对数组中查找这些增量值。

    增量数组如下所示:

    var dr = [ -1, 0, 1, 0 ],  // North, east, south, west.
        dc = [ 0, 1, 0, -1 ];
    

    给定一个网格位置r, c,你可以这样计算下一个位置:

    var R = r + dr[orientation],
        C = c + dc[orientation];
    

    下面的 sn-p 演示了这种方法。

    function print(line) {
      document.getElementById('message').innerHTML += line + '<br><br>';
    }
    
    var snail = function(array) {
      var result = [];
    
      var flags = array.map(function (subarray) {
        return subarray.map(function () {
          return false;
        });
      });
      print(JSON.stringify(array));
    
      var numRows = flags.length,
          numCols = flags[0].length,
          numElements = numRows * numCols,
          dr = [ -1, 0, 1, 0 ],  // North, east, south, west.
          dc = [ 0, 1, 0, -1 ],
          r = 0,
          c = 0,
          orientation = 1;       // Start heading east.
    
      var count = 0;
    
      while (result.length < numElements) {
        result.push(array[r][c]);
        flags[r][c] = true;
        var R = r + dr[orientation],  // Calculate the next grid position.
            C = c + dc[orientation];
        if (R >= 0 && R < numRows && C >= 0 && C < numCols && !flags[R][C]) {
          r = R;                      // If the position is valid, go ahead.
          c = C;
        } else {
          orientation = (orientation + 1) % 4;
          r = r + dr[orientation];    // Otherwise, turn clockwise and recalculate.
          c = c + dc[orientation];
        }
      }
    
      return result;
    }
    
    window.onload = function () {
      print(snail([ [1, 2, 3],
                    [8, 9, 4],
                    [7, 6, 5] ]));
    };
    body {
      font-family: sans-serif;
    }
    &lt;div id="message"&gt;&lt;/div&gt;

    【讨论】:

      【解决方案2】:

      螺旋阵列(ES6)

      试试这个:

      function spiral(array) {
          const matrix = [...array];
          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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-03
        • 2010-12-11
        • 2012-12-26
        • 2012-01-07
        • 2013-06-11
        • 2016-11-27
        相关资源
        最近更新 更多