在两点之间对角地迭代2D阵列

时间:2014-04-06 00:50:12

标签: java arrays multidimensional-array

我有一个二维数组,我想在其中对角迭代。我想限制两点之间的范围,创建一个45 *偏移的矩形区域。

一些例子:

| - - - - - - - -     | - - - - - - - -     | - - - - - - - - 
| - - - * - - - -     | - - - * - - - -     | - - - - - * - - 
| - - 1 1 1 - - -     | - - 1 1 1 - - -     | - - - - 1 - - - 
| - - 1 1 1 1 - -     | - 1 1 1 1 1 - -     | - - - 1 - - - - 
| - - - 1 1 1 1 -     | - - 1 1 1 - - -     | - - 1 - - - - - 
| - - - - 1 1 1 -     | - - - * - - - -     | - * - - - - - - 
| - - - - - * - -     | - - - - - - - -     | - - - - - - - - 
| - - - - - - - -     | - - - - - - - -     | - - - - - - - - 

* =输入点

1 =指向迭代

我的问题是:我将如何以干净有效的方式做到(或接近)这样的事情?订单无关紧要。

1 个答案:

答案 0 :(得分:5)

这是一个有趣的问题!以下是我将如何解决它:

让我们确定9种不同的可能情景:

enter image description here

(我把圆圈放在那里作为眼睛的指导。我把这两个点可视化为彼此“轨道”。)

  • A 是两个坐标相同的简单案例
  • B F 是您基本上只需绘制对角线的情况
  • C D E 是点之间的y距离大于x距离的情况
  • G H I 是点之间的x距离大于y距离的情况

从技术上讲, B I 的情况每个都对应两种情况,具体取决于顶部/左侧坐标是第一个还是底部/右侧坐标,但让我们只是定义点的所需顺序,并在需要时切换它们。

需要处理的区域可以分解为平行四边形(绿色)和两个三角形:

enter image description here

C D E 的情况下,可以“垂直”处理平行四边形(即对于每个x位置,你在垂直方向上经过一定数量的点),在 G H 的情况下,它可以“水平”处理

垂直平行四边形中每列的高度和水平平行四边形的每一行的宽度正好是两点的y差值和两点的x差值之间的绝对差值。

我们可以通过仅处理4个案例来涵盖所有情景: C E G B D 可以被认为是 C 的特例,其中平行四边形的高度或宽度分别为0。同样, F 只是 E 的特例,而 H 可以与 I 一起处理。使用 C 也可以使用 A ,但由于它很容易识别,因此我们将其单独处理以提高性能。

为了使程序具有通用性,让我为Processor定义一个与数组交互的接口,并调用所有需要处理的坐标:

public interface Processor {
    public void process(int x, int y);
}

代码有点长,但这并不是特别困难,所以让我发帖吧:

public void process(Processor processor, int x1, int y1, int x2, int y2) {
    int dy = Math.abs(y2 - y1);
    int dx = Math.abs(x2 - x1);
    if (dx<=dy) {
        if (dy==0) {
            // Case A
            processor.process(x1, y1);
            return;
        }
        // Cases B, C, D, E, and F
        if (y2>y1) processVertically  (processor, x1, y1, x2, y2, dy - dx);
              else processVertically  (processor, x2, y2, x1, y1, dy - dx);
    } else {
        // Cases G, H, and I
        if (x2>x1) processHorizontally(processor, x1, y1, x2, y2, dx - dy);
              else processHorizontally(processor, x2, y2, x1, y1, dx - dy);
    }
}

private void processVertically(Processor processor, int x1, int y1, int x2, int y2, int h) {
    if (x2<x1) {
        // Cases E and F
        // Fill in parallelogram 
        int y = y2;
        for (int x=x2; x<=x1; x++) {
            for (int dy=0; dy<=h; dy++)
                processor.process(x, y-dy);
            y--;
        }
        // Fill in triangles
        for (h-=2; h>=0; h-=2) {
            x1++; y1++;
            x2--; y2--;
            for (int dy=0; dy<=h; dy++) {
                processor.process(x1, y1+dy);
                processor.process(x2, y2-dy);
            }
        }
    } else {
        // Cases B, C and D
        // Fill in parallelogram 
        int y = y1;
        for (int x=x1; x<=x2; x++) {
            for (int dy=0; dy<=h; dy++)
                processor.process(x, y+dy);
            y++;
        }
        // Fill in triangles
        for (h-=2; h>=0; h-=2) {
            x1--; y1++;
            x2++; y2--;
            for (int dy=0; dy<=h; dy++) {
                processor.process(x1, y1+dy);
                processor.process(x2, y2-dy);
            }
        }
    }
}

private void processHorizontally(Processor processor, int x1, int y1, int x2, int y2, int w) {
    if (y2<y1) {
        // Case G
        // Fill in parallelogram 
        int x = x2;
        for (int y=y2; y<=y1; y++) {
            for (int dx=0; dx<=w; dx++)
                processor.process(x-dx, y);
            x--;
        }
        // Fill in triangles
        for (w-=2; w>=0; w-=2) {
            x1++; y1++;
            x2--; y2--;
            for (int dx=0; dx<=w; dx++) {
                processor.process(x1+dx, y1);
                processor.process(x2-dx, y2);
            }
        }
    } else {
        // Cases H and I
        // Fill in parallelogram 
        int x = x1;
        for (int y=y1; y<=y2; y++) {
            for (int dx=0; dx<=w; dx++)
                processor.process(x+dx, y);
            x++;
        }
        // Fill in triangles
        for (w-=2; w>=0; w-=2) {
            x1++; y1--;
            x2--; y2++;
            for (int dx=0; dx<=w; dx++) {
                processor.process(x1+dx, y1);
                processor.process(x2-dx, y2);
            }
        }
    }
}

process(...) - 方法只是确定我们拥有的9个案例中的哪一个,并直接处理案例 A 或如上所述调用processHorizontally(...)processVertically(...)以上。然后,这些方法首先贯穿各自的平行四边形,然后填充平行四边形周围的三角形。

有几点需要注意:

  1. 除了 x y 之外,基本上processHorizontally(...)processVertically(...)完全相同。
  2. 循环非常紧,不包含任何繁重的数学,所以这段代码应该相当高效,但仍有改进的余地(例如内联processor.process(...),处理案例 B F 分别采用更优化的方式,...)。
  3. 此代码检查数组边界!例如,可能会使用负坐标调用process(...)
  4. 有人可能会说:案例 G 和案例 E 看起来非常像同一个案例。你不能只计算矩形的另外两个角来将 G 案例 E H 映射到 D 而不是将它们视为单独的案例而不是将它们视为 C ?而且,在理想数学领域,这是一个非常有效的陈述,除非它在其他角落的坐标不是整数的情况下不起作用。例如,OP的第一个例子就是这种情况。在那里,左右两侧有一个“切碎的”角(一列中只有两个1)。如果你要计算并围绕矩形的左右角而不是顶角和底角,你最终会得到顶部和底部的“切碎”角。
  5. 希望这会有所帮助。如果您需要更详细的代码说明,请与我们联系。