【问题标题】:How to determine number of same neighbours in matrix?如何确定矩阵中相同邻居的数量?
【发布时间】:2014-02-01 21:02:18
【问题描述】:

我有一个枚举为 1 或 0 的对象数组。 它可能看起来像这样:

0 0 1 0
0 1 1 0
0 0 0 1

我希望包含 1 的组的所有成员,以显示整个组中的成员数量:

0 0 3 0
0 3 3 0
0 0 0 1

有什么已知的方法可以找到这个吗?我的意思是,我可能会为我需要的每个案例编写条件,因为我想将数字更改为 5 以下,但这会很长。

【问题讨论】:

  • 为什么第一行是0 0 3 0?它不应该是0 0 2 0,因为只有两个邻居是1?第二行不应该是0 2 3 0,第三行是0 0 0 1吗?
  • 你能更详细地解释你的输出吗?你是如何计算出这些行是0,0,3,0 而不是1,3,2,2neighbour 是什么意思?对角线元素也是邻居吗?您正在检查的元素是否也是其自身的邻居?
  • 如果不计入对角线,那么第一行的 1 如何有两个邻居和它自己?
  • 所以你想让一个组的所有成员都显示整个组的成员数量?
  • 这确实是一个不难但严肃而有趣的算法。你会找到所有的邻居,记住他们,然后增加他们的所有值。

标签: java matrix


【解决方案1】:

这个问题可以用联合查找算法来解决。您的矩阵被解释为具有双向垂直和水平连接的非零节点图。然后问题是在跟踪分区大小的同时找到图的连接分区。这是一个独立的解决方案:

public class So21503628 {
    private List<List<Integer>> matrix;
    private int h, w;
    private Map<Integer, Node> nodes = new HashMap<>();
    So21503628() {
        matrix = new ArrayList<>();
        matrix.add(Arrays.asList(0,0,1,0));
        matrix.add(Arrays.asList(0,1,1,0));
        matrix.add(Arrays.asList(0,0,0,1));
        h = matrix.size(); w = matrix.get(0).size();
    }
    void run() {
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                Node xy = nodeAt(x, y);
                if (xy == null) continue;
                Node x1y = nodeAt(x+1, y);
                if (x1y != null) union(xy, x1y);
                Node xy1 = nodeAt(x, y+1);
                if (xy1 != null) union(xy, xy1);
            }
        }
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                Node n = nodeAt(x, y);
                if (n == null) continue;
                matrix.get(y).set(x, find(n).count);
            }
        }
        System.out.println(matrix);
    }
    Node nodeAt(int x, int y) {
        if (x >= w || y >= h || matrix.get(y).get(x) == 0) return null;
        int xy = y * w + x;
        Node node = nodes.get(xy); 
        if (node == null) { node = new Node(); nodes.put(xy, node); }
        return node;
    }
    void union(Node n1, Node n2) { // unite areas if separate
        Node r1 = find(n1), r2 = find(n2);
        if (r1 != r2) { r2.parent = r1; r1.count += r2.count; }
    }
    Node find(Node n) { // find representative + compress path
        Node r = n; while (r.parent != null) r = r.parent;
        if (r != n) while (n.parent != r) { Node h = n.parent; n.parent = r; n = h; }
        return r;
    }
    static class Node { Node parent; int count = 1; }
    public static void main(String[] args) { new So21503628().run(); }
}

Union-Find 可以很好地扩展(接近 O(n))。 有关 Union-Find 的详细信息,请参阅 Wikipedia 上的 Disjoint-set data structure

【讨论】:

  • 对于[[0, 0, 1, 0], [1, 1, 0, 1], [0, 0, 1, 1]],输出为[[0, 0, 1 , 0], [2, 2, 0, 1], [0, 0, 1, 1]];这是不正确的。我还没有研究过代码,可能只是一些小错误。
  • 谢谢!我修复了它:在第一次遍历中使用y&lt;hx&lt;w 而不是y&lt;h-1x&lt;w-1,并在nodeAt() 添加了对x 和y 的检查。 -1 旨在优化,但导致不尊重右端的垂直链接和底部的水平链接。
  • 你的回答比较好,但我没有完全理解。我正在为学校制作项目,我必须知道我的代码。感谢您抽出时间来帮助我。谢谢。
【解决方案2】:

不知何故让我想起了扫雷。在这里可以很容易地使用递归......

public class MatrixNeighborCount
{
    public static void main(String[] args)
    {
        int array[][] = new int[][]{
            { 0, 0, 1, 0 },
            { 0, 1, 1, 0 },
            { 0, 0, 0, 1 },
        };
        int result[][] = count(array);
        print(result);
    }

    private static void print(int array[][])
    {
        for (int r=0; r<array.length; r++)
        {
            for (int c=0; c<array[r].length; c++)
            {
                System.out.printf("%3d", array[r][c]);
            }
            System.out.println("");
        }
    }

    private static int[][] copy(int array[][])
    {
        int result[][] = new int[array.length][];
        for (int i=0; i<array.length; i++)
        {
            result[i] = array[i].clone();
        }
        return result;
    }

    private static int[][] count(int inputArray[][])
    {
        int result[][] = new int[inputArray.length][];
        for (int i=0; i<inputArray.length; i++)
        {
            result[i] = new int[inputArray[i].length];
        }

        int array[][] = copy(inputArray);
        for (int r=0; r<array.length; r++)
        {
            for (int c=0; c<array[r].length; c++)
            {
                if (array[r][c] == 1)
                {
                    int count = count(array, r, c);
                    distribute(inputArray, result, r, c, count);
                }
            }
        }
        return result;
    }

    private static int count(int array[][], int r, int c)
    {
        if (!valid(array, r, c)) return 0;
        if (array[r][c] == 0) return 0;

        array[r][c] = 0;
        return 1 + 
            count(array, r-1, c) +
            count(array, r+1, c) +
            count(array, r, c-1) +
            count(array, r, c+1);
    }

    private static void distribute(
        int inputArray[][], int result[][], int r, int c, int value)
    {
        if (!valid(inputArray, r, c)) return;
        if (inputArray[r][c] == 0) return;
        if (result[r][c] != 0) return;
        result[r][c] = value;
        distribute(inputArray, result, r-1, c, value);
        distribute(inputArray, result, r+1, c, value);
        distribute(inputArray, result, r, c-1, value);
        distribute(inputArray, result, r, c+1, value);
    }

    private static boolean valid(int array[][], int r, int c)
    {
        if (r < 0) return false;
        if (r >= array.length) return false;
        if (c < 0) return false;
        if (c >= array[r].length) return false;
        return true;
    }
}

但是

  1. 对于“大”数组,这将导致 StackOverflowError
  2. 包含一些集合的解决方案可能更优雅

如果您描述了您正在处理的实际数据结构,以及该程序的最终目的是什么,这可能会有所帮助....

【讨论】:

  • 数据结构 - ArrayList。它将是程序生成地图的框架。地图可以大到 500x500 或更大...
  • 顺便感谢您的代码。已经是晚上了,但我明天会检查并测试它。再次感谢您。
猜你喜欢
  • 2016-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多