【问题标题】:Multidimensional array fill多维数组填充
【发布时间】:2014-02-26 21:29:14
【问题描述】:

我正在尝试填充多维数组中的一个区域,但不确定该方法。

例如我有以下数组:

var map = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 2, 2, 2, 2, 2, 2, 0, 0],
    [0, 2, 0, 0, 0, 0, 2, 0, 0],
    [0, 2, 0, 2, 0, 0, 2, 0, 0],
    [0, 2, 0, 0, 2, 0, 2, 0, 0],
    [0, 0, 2, 0, 0, 0, 2, 0, 0],
    [0, 0, 0, 2, 2, 2, 2, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0]
];

然后我尝试从 X 和 Y 位置获取数字,并用给定的数字(例如 1)填充所有这些数字(即 0),这将产生以下数组:

var map = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 2, 2, 2, 2, 2, 2, 0, 0],
    [0, 2, 1, 1, 1, 1, 2, 0, 0],
    [0, 2, 1, 2, 1, 1, 2, 0, 0],
    [0, 2, 1, 1, 2, 1, 2, 0, 0],
    [0, 0, 2, 1, 1, 1, 2, 0, 0],
    [0, 0, 0, 2, 2, 2, 2, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0]
];

基本上只是在该区域内将所有相邻的数字 (0) 替换为 (1)。

使用 JavaScript 执行此操作的正确方法是什么?

【问题讨论】:

  • 不清楚你到底想做什么。您如何决定哪些 0 值应该更改为其他值?
  • 看起来有点像绘画程序的“填充”算法:2 形成一个封闭的“形状”,您想用数字 1 填充封闭区域。(只是观察。)
  • 是的,正如 BrettFromLA 所说,我希望用数字 1 填充封闭区域,尽管它可以是任何数字 - 以传递给函数等为准
  • Flood fill算法

标签: javascript arrays algorithm multidimensional-array flood-fill


【解决方案1】:

假设你有一个起始位置,然后你想向上/向下、向左/向右填充所有包含相同值的相邻值,你可以这样做:

var map = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 2, 2, 2, 2, 2, 2, 0, 0],
    [0, 2, 0, 0, 0, 0, 2, 0, 0],
    [0, 2, 0, 2, 0, 0, 2, 0, 0],
    [0, 2, 0, 0, 2, 0, 2, 0, 0],
    [0, 0, 2, 0, 0, 0, 2, 0, 0],
    [0, 0, 0, 2, 2, 2, 2, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0]
];

function fill(data, x, y, newValue) {
    // get target value
    var target = data[x][y];

    function flow(x,y) {
        // bounds check what we were passed
        if (x >= 0 && x < data.length && y >= 0 && y < data[x].length) {
            if (data[x][y] === target) {
                data[x][y] = newValue;
                flow(x-1, y);    // check up
                flow(x+1, y);    // check down
                flow(x, y-1);    // check left
                flow(x, y+1);    // check right
            }
        }
    }

    flow(x,y);
}

fill(map, 2, 2, 1);

工作演示:http://jsfiddle.net/jfriend00/C83AT/


这是一个不使用递归并且似乎可以处理大型数据集的版本。您的大型测试数据集不是一个非常有趣的测试模式,所以我不会说这是最终测试,但它似乎适用于小型和大型数据集:

大数据示例:http://jsfiddle.net/jfriend00/8mrhN/

小数据示例:http://jsfiddle.net/jfriend00/BFTub/(更容易看到结果)

function fill(data, x, y, newValue) {
    // get target value
    var target = data[x][y];
    // maintain list of cells to process
    // put the starting cell in the queue
    var queue = [{x:x, y:y}], item;

    while (queue.length) {
        item = queue.shift();
        x = item.x;
        y = item.y;
        if (data[x][y] === target) {
            data[x][y] = newValue;
            // up
            if (x > 0) {
                queue.push({x:x-1, y:y})
            }
            // down
            if (x + 1 < data.length) {
                queue.push({x:x+1, y:y})
            }
            // left
            if (y > 0) {
                queue.push({x:x, y:y-1});
            }
            // right
            if (y + 1 < data[x].length) {
                queue.push({x:x, y:y+1});
            }
        }
    }
}

如果需要,可以通过在将值放入队列之前测试值并按照给定方向直到找到不匹配的值(如果需要)来进一步优化性能。

【讨论】:

  • 嗨,感谢您的发帖,这适用于小型阵列,但大型阵列呢?它将得到“超出最大调用堆栈大小”错误。假设宽度为 200,高度为 200。
  • @user2909323 - 发布一个你有这个问题的数组,我会调查它。
  • @Xotic750 - 我知道有非递归实现。我已要求 OP 发布大量样本数据。我倾向于寻找解决手头问题的最简单的解决方案,这符合 OP 的初始要求,所以我选择了简单的实现。
  • 我同意。我真的将 OP 指向伪代码而不是你。 :)
【解决方案2】:

这是一个alternative implementation(基于队列)粗略翻译,没有进行优化。还有其他的。

Javascript

var map = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 2, 2, 2, 2, 2, 2, 0, 0],
    [0, 2, 0, 0, 0, 0, 2, 0, 0],
    [0, 2, 0, 2, 0, 0, 2, 0, 0],
    [0, 2, 0, 0, 2, 0, 2, 0, 0],
    [0, 0, 2, 0, 0, 0, 2, 0, 0],
    [0, 0, 0, 2, 2, 2, 2, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0]
];

/*
 1. Set Q to the empty queue.
 2. If the color of node is not equal to target-color, return.
 3. Add node to Q.
 4. For each element N of Q:
 5.     If the color of N is equal to target-color:
 6.         Set w and e equal to N.
 7.         Move w to the west until the color of the node to the west of w no longer matches target-color.
 8.         Move e to the east until the color of the node to the east of e no longer matches target-color.
 9.         For each node n between w and e:
10.             Set the color of n to replacement-color.
11.             If the color of the node to the north of n is target-color, add that node to Q.
12.             If the color of the node to the south of n is target-color, add that node to Q.
13. Continue looping until Q is exhausted.
14. Return.
*/
function floodFill(data, node, targetValue, replacementValue) {
    var Q;

    if (data[node[0]][node[1]] === targetValue) {
        Q = [node];
        while (Q.length) {
            var N = Q.shift(),
                value,
                index,
                n,
                e,
                s,
                w;

            if (data.hasOwnProperty([N[0]]) && data[N[0]][N[1]] === targetValue) {
                w = e = N[0];
                do {
                    w -= 1;
                } while (data.hasOwnProperty(w) && data[w][N[1]] === targetValue);

                do {
                    e += 1;
                } while (data.hasOwnProperty(e) && data[e][N[1]] === targetValue);

                n = N[1] - 1;
                s = N[1] + 1;
                for (index = w + 1; index < e; index += 1) {
                    data[index][N[1]] = replacementValue;
                    if (data[index].hasOwnProperty(n) && data[index][n] === targetValue) {
                        Q.push([index, n]);
                    }

                    if (data[index].hasOwnProperty(s) && data[index][s] === targetValue) {
                        Q.push([index, s]);
                    }
                }
            }
        }
    }
}

floodFill(map, [2, 2], 0, 1);
map.forEach(function (m) {
    console.log(JSON.stringify(m));
});

输出

[0,0,0,0,0,0,0,0,0] [0,2,2,2,2,2,2,0,0] [0,2,1,1,1,1,2,0,0] [0,2,1,2,1,1,2,0,0] [0,2,1,1,2,1,2,0,0] [0,0,2,1,1,1,2,0,0] [0,0,0,2,2,2,2,0,0] [0,0,0,0,0,0,0,0,0]

开启jsFiddle

【讨论】:

    猜你喜欢
    • 2011-01-11
    • 2013-02-20
    • 1970-01-01
    • 2018-01-27
    • 2015-10-23
    • 2011-06-29
    • 1970-01-01
    相关资源
    最近更新 更多