【问题标题】:How to loop 2D array in a anti-clock wise fashion?如何以反时钟方式循环二维数组?
【发布时间】:2019-03-16 18:54:05
【问题描述】:

给定一个 m x n 维的二维数组,我如何以逆时针方式循环遍历它们?

例如:

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

1st loop: 1, 5, 9, 13, 14, 15, 16, 12, 8, 4, 3, 2
2nd loop: 6, 10, 11, 7, 6

我真的不介意实现是用 ruby​​ 还是 js 给出的

我目前的解决方案是这样的:

  (1..rotate).each do
    bottom_left_corner = i
    top_right_corner   = j
    start_nth_layer = 1; end_nth_layer = matrix.length - 2
    matrix.reverse_each do
      matrix[bottom_left_corner].unshift(matrix[bottom_left_corner - 1].shift) if bottom_left_corner > 0
      matrix[top_right_corner] << matrix[top_right_corner + 1].pop if top_right_corner < matrix.length - 1

      bottom_left_corner -= 1; top_right_corner += 1
    end

    nth_layer(matrix, start_nth_layer, end_nth_layer)

  end

更新

输出不格式化没关系,只要输出顺序正确。

问题的目的

这个问题的目的是逆时针遍历这些数组,一层一层,直到没有更多的层。对于每次遍历,我们逆时针移动值。例如:

 Iteration 1:        Iteration 2:       Iteration 3:
 ==============      =============      ==============
 1    2   3   4      2   3   4   8      3   4   8  12
 5    6   7   8      1   7  11  12      2  11  10  16
 9   10  11  12  =>  5   6  10  16  =>  1   7   6  15
 13  14  15  16      9  13  14  15      5   9  13  14

【问题讨论】:

  • 你有没有尝试过?
  • 这就像一个面试问题。
  • @TonyDong 我在hackerrank中尝试这个有趣的算法问题
  • @choz 否,因此 (M x N)。其中 N 和 M 可以是任意维度
  • 您的“第二个循环”应以7 结尾。注意6已经被遍历过了。

标签: javascript arrays ruby loops multidimensional-array


【解决方案1】:

这是矩阵层旋转问题。这是我的完整解决方案:

function matrixRotation(matrix, rotate) {
    var r = matrix.length, c = matrix[0].length;
    var depth = Math.min(r,c)/2;
    for(var i=0;i<depth;i++) {
        var x = i, y = i, n = (r-i*2 - 2)*2 + (c-i*2-2)*2+4, dir = 'down', index=0;
        var buf = [];
        while(index < n) {
            buf.push(matrix[x][y]);
            if(dir == 'down') {
                if(x+1 >= r-i) {
                    dir = 'right';
                    y++;
                } else {
                    x++;
                }
            } else if(dir == 'right') {
                if(y+1 >= c-i) {
                    dir = 'up';
                    x--;
                } else {
                    y++;
                }
            } else if(dir == 'up') {
                if(x-1 <= i-1) {
                    dir = 'left';
                    y--;
                } else {
                    x--;
                }
            } else if(dir == 'left') {
                y--;
            }
            index++;
        }
        var move = rotate%n;
        buf = [...buf.slice(buf.length-move), ...buf.slice(0, buf.length-move)]
        x = i, y = i, dir = 'down', index = 0;
        while(index < n) {
            matrix[x][y] = buf[index];
            if(dir == 'down') {
                if(x+1 >= r-i) {
                    dir = 'right';
                    y++;
                } else {
                    x++;
                }
            } else if(dir == 'right') {
                if(y+1 >= c-i) {
                    dir = 'up';
                    x--;
                } else {
                    y++;
                }
            } else if(dir == 'up') {
                if(x-1 <= i-1) {
                    dir = 'left';
                    y--;
                } else {
                    x--;
                }
            } else if(dir == 'left') {
                y--;
            }
            index++;
        }
    }
    matrix.map(row => console.log(row.join(' ')));
}

const matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
// rotate count
const r = 3

matrixRotation(matrix, r);

【讨论】:

    【解决方案2】:

    这种方法应该支持matrixmatrix[n] 的任何大小。如果这对您有用,请尝试使用各种测试用例。

    请注意,我在这里忽略了任何性能缺陷。

    var arr = [
      [   1,    2,   3,    4  ],
      [   5,    6,   7,    8  ],
      [   9,   10,  11,   12  ],
      [  13,   14,  15,   16  ]
    ],
    result = [];
    
    while ( arr.flat().length ) {
      var res = [];
    
      // 1. Left side
      arr.forEach(x => res = res.concat( x.splice(0, 1) ));
    
      // 2. Bottom side
      if (arr.length) {
        res = res.concat( ...arr.splice(arr.length - 1, 1) );
      }
    
      // 3. Right side
      var tmp = [];
      if (arr.flat().length) {
        arr.forEach(x => tmp.push( x.splice(arr.length - 1, 1) ));
        res = res.concat( ...tmp.reverse() );
      }
    
      // 4. Top side
      if (arr.length) {
        tmp = [].concat( ...arr.splice(0, 1) );
        res = res.concat( tmp.reverse() );
      }
    
      result.push(res);
    }
    
    console.log(result);

    【讨论】:

      【解决方案3】:

      我会以分层的方式编写这个,编写矩阵转置逻辑(即,在西北-东南对角线上翻转矩阵),使用它和一个简单的reverse 来构建矩阵的逆时针旋转,使用该旋转构建顺时针螺旋,最后再次使用transpose 沿着顺时针螺旋构建逆时针螺旋。

      我选择这个顺序是因为更常见的是顺时针螺旋,但直接构建逆时针螺旋很容易。 提示rotateClockwise = m =&gt; transpose(reverse(m)).

      const reverse = a => [...a].reverse();
      const transpose = m => m[0].map((c, i) => m.map(r => r[i]))
      const rotateCounter = m => reverse(transpose(m))
      const spiralClockwise = m => m.length < 2
        ? m[0]
        : m[0].concat(spiralClockwise(rotateCounter(m.slice(1))))
      const spiralCounter = m => spiralClockwise(transpose(m))
      
      console.log(spiralCounter([
        [1, 2, 3, 4],
        [5, 6, 7, 8],
        [9, 10, 11, 12]
      ]))

      请注意,这里的中间函数本身很可能很有用。所以这让我觉得这是解决问题的好方法。

      【讨论】:

      • 再看一遍,我不确定您的“第一个循环”/“第二个循环”故障是否只是算法的演示或您的预期输出。这个创建了整个路径的平面列表。如果您希望将其作为单独的列表,那么这可能无济于事。
      【解决方案4】:

      这就是我在 ruby​​ 中的做法:

      def counter_clockwise!(arr)
        return arr if arr.size <= 1
        rotation = [
          arr.map(&:shift)
            .concat(arr.pop,
                    arr.map(&:pop).reverse,
                    arr.shift.reverse).compact
        ]
        rotation.push(*send(__method__,arr)) unless arr.all?(&:empty?)
        rotation 
      end
      
      def counter_clockwise(arr)
        counter_clockwise!(arr.map(&:dup))
      end
      

      例子:

      arr = [
        [   1,    2,   3,    4  ],
        [   5,    6,   7,    8  ],
        [   9,   10,  11,   12  ],
        [  13,   14,  15,   16  ],
        [  17,   18,  19,   20  ],
        [  21,   22,  23,   24  ]
      ]
      counter_clockwise(arr)
      #=> [[1, 5, 9, 13, 17, 21, 22, 23, 24, 20, 16, 12, 8, 4, 2, 3], 
      #    [6, 10, 14, 18, 19, 15, 11, 7]]
      

      此方法将外部边缘递归处理成组(原始帖子中的“循环”)。显然,如果您只想要没有组的逆时针螺旋,您可以使返回值变平。

      这也将处理非 M x N 矩阵,例如:

      arr = [
        [   1,    2,   3,    4  , 72 ],
        [   5,    6,   7,    8  , 73 ],
        [   9,   10,        11  , 12 ],
        [  13,   14,        16  , 75 ],
        [  17,   18,  19,   20  , 76 ],
        [  21,   22,  23,   24  , 77 ]
      ]
      
      counter_clockwise(arr) 
      #=> [[1, 5, 9, 13, 17, 21, 22, 23, 24, 77, 76, 75, 12, 73, 72, 4, 3, 2],       
      #   [6, 10, 14, 18, 19, 20, 16, 11, 8, 7]]
      

      如果你想要每条边并且可以保证 M x N 那么这也可以工作

      def counter_clockwise_edges(arr)
        return arr if arr.size == 1
        arr = arr.transpose
        [arr.shift].push(*send(__method__,arr.map(&:reverse))) 
      end
      counter_clockwise_edges(arr)
      #=> [[1, 5, 9, 13, 17, 21], 
           [22, 23, 24], 
           [20, 16, 12, 8, 4], 
           [3, 2], 
           [6, 10, 14, 18], 
           [19, 15, 11, 7]]
      

      【讨论】:

        【解决方案5】:

        以下是我对this SO question的回答的修改,与这个的不同之处仅在于数组以相反的方向遍历。

        arr = matrix.map(&:dup).transpose
        out = []
        loop do
          out = out + arr.shift
          break out if arr.empty?
          arr = arr.transpose.reverse
        end
          #=> [1, 5, 9, 13, 14, 15, 16, 12, 8, 4, 3, 2, 6, 10, 11, 7]
        

        步骤如下。

        arr = matrix.map(&:dup).transpose
          #=> [[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]
        

        我已经欺骗了matrix 的元素,这样后者就不会发生变异。

        out = []
        
        out = out + arr.shift
          #=> [1, 5, 9, 13]
        arr
          #=> [[2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]    
        
        arr = arr.transpose.reverse
          #=> [[14, 15, 16], [10, 11, 12], [6, 7, 8], [2, 3, 4]]
        out = out + arr.shift
          #=> [1, 5, 9, 13, 14, 15, 16]
        arr
          #=> [[10, 11, 12], [6, 7, 8], [2, 3, 4]]
        
        arr = arr.transpose.reverse
          #=> [[12, 8, 4], [11, 7, 3], [10, 6, 2]]
        out = out + arr.shift
          #=> [1, 5, 9, 13, 14, 15, 16, 12, 8, 4]
        arr
          #=> [[11, 7, 3], [10, 6, 2]]
        
        arr = arr.transpose.reverse
          #=> [[3, 2], [7, 6], [11, 10]]
        out = out + arr.shift
          #=> [1, 5, 9, 13, 14, 15, 16, 12, 8, 4, 3, 2]
        arr
          #=> [[7, 6], [11, 10]]
        

        arr = arr.transpose.reverse
          #=> [[6, 10], [7, 11]]
        out = out + arr.shift
          #=> [1, 5, 9, 13, 14, 15, 16, 12, 8, 4, 3, 2, 6, 10]
        arr
          #=> [[7, 11]]
        
        arr = arr.transpose.reverse
          #=> [[11], [7]]
        out = out + arr.shift
          #=> [1, 5, 9, 13, 14, 15, 16, 12, 8, 4, 3, 2, 6, 10, 11]
        arr
          #=> [[7]]
        
        arr = arr.transpose.reverse
          #=> [[7]]
        out = out + arr.shift
          #=> [1, 5, 9, 13, 14, 15, 16, 12, 8, 4, 3, 2, 6, 10, 11, 7]
        arr
          #=> []
        

        由于arr 现在是空的,我们break 并返回out

        arr
        arr = arr.transpose.reverse
        
        out = out + arr.shift
        arr
        arr = arr.transpose.reverse
        
        out = out + arr.shift
        arr
        arr = arr.transpose.reverse
        
        out = out + arr.shift
        arr
        arr = arr.transpose.reverse
        
        out = out + arr.shift
        arr
        arr = arr.transpose.reverse
        
        out = out + arr.shift
        arr
        arr = arr.transpose.reverse
        

        【讨论】:

          【解决方案6】:

          给定数组:

          array = 
            [
              [ '00', '01', '02', '03', '04', '05'],
              [ '10', '11', '12', '13', '14', '15'],
              [ '20', '21', '22', '23', '24', '25'],
              [ '30', '31', '32', '33', '34', '35']
            ]
          

          这会返回外部循环:

          external_ccw_round = [array.map(&:shift), array.pop, array.map(&:pop).reverse, array.shift.reverse].flatten
          #=> ["00", "10", "20", "30", "31", "32", "33", "34", "35", "25", "15", "05", "04", "03", "02", "01"]
          

          只留下核心

          array #=> [["11", "12", "13", "14"], ["21", "22", "23", "24"]]
          

          对于多于一行的矩阵。

          这是一个递归实现的方法

          适用于任何二维矩阵。

          def ccw_scan(array, result=[])
            return array if array.size <= 1
            result << [array.map(&:shift), array.pop, array.map(&:pop).reverse, array.shift.reverse]
            if array.size >= 2
              then ccw_scan(array, result)
            else result << array
            end
            result.flatten.compact
          end
          

          调用数组上的方法:

          ccw_scan(array)
          #=> ["00", "10", "20", "30", "31", "32", "33", "34", "35", "25", "15", "05", "04", "03", "02", "01", "11", "21", "22", "23", "24", "14", "13", "12"]
          

          【讨论】:

          • 更新了递归方法以获取螺旋中的所有元素。
          • @CarySwoveland,是的,谢谢,我在编辑时错过了.compact。你的嫌疑人是正确的。
          猜你喜欢
          • 2018-03-09
          • 1970-01-01
          • 2017-03-27
          • 2021-05-02
          • 1970-01-01
          • 1970-01-01
          • 2019-06-02
          • 1970-01-01
          • 2011-08-21
          相关资源
          最近更新 更多