如何找到在矩阵中只出现一次的数字?

How to find numbers that appear only once in a matrix?

我有一个名为 m 的给定矩阵,它的维度是 n x n,它包含整数。我需要将只出现一次的数字复制到一个名为 a.

的新数组中

我认为逻辑是为矩阵中的每个数字创建一个 for 循环并将其与其他所有数字进行比较,但我不知道如何使用代码实际执行此操作。

我只能使用循环(没有地图等),这就是我想出的:

public static void Page111Ex14(int[][] m) {

    int previous = 0, h = 0;
    
    int[] a = new int[m.length*m[0].length];
    
    for (int i = 0; i < m.length; i++) {
        for (int j = 0; j < m[0].length; j++) {
            previous = m[i][j];
            
            if (m[i][j] != previous) {
                a[h] = m[i][j];
                h++;
            }
        }
    }

虽然可能不正确。

看起来不太好:

previous = m[i][j];

if (m[i][j] != previous) {
    a[h] = m[i][j];
    h++;
}

您将 m[i][j] 分配给了 previous 然后您检查是否 if (m[i][j] != previous)?

这个任务对数字的取值范围有限制吗?

再循环一遍,看有没有重复的。假设您可以使用标签,答案可能看起来有点像这样:

public static int[] getSingleInstanceArrayFromMatrix(int[][] m) {
        int[] a = new int[m.length * m[0].length];

        // Main loop.
        for (int x = 0; x < m.length; x++) {
            for (int y = 0; y < m[0].length; y++) {

                // Gets the current number in the matrix.
                int currentNumber = m[x][y];

                // Boolean to check if the variable appears more than once.
                boolean isSingle = true;

                // Looping again through the array.
                checkLoop:
                for (int i = 0; i < m.length; i++) {
                    for (int j = 0; j < m[0].length; j++) {
                        
                        // Assuring we are not talking about the same number in the same matrix position.
                        if (i != x || j != y) {
                            // If it is equal to our current number, we can update the variable and break.
                            if (m[i][j] == currentNumber) {
                                isSingle = false;
                                break checkLoop;
                            }
                        }
                    }
                }

                if (isSingle) {
                    a[(x * m.length) + y] = currentNumber;
                }
            }
        }

        return a;
    }

不确定它是否最有效,但我认为它会起作用。没有列表等的帮助,很难形成最终数组。由于未分配的值将默认为 0,如果您查找返回的数组,任何 actual 零(即它“应该”基于矩阵)将不会被检测到。但是,如果有这样的限制,我想它并不是至关重要的。

这是您可以直接使用 HashMap 解决的问题之一,它会为您完成工作。您遍历二维数组,使用 HashMap 存储每个元素及其出现次数,然后遍历 HashMap 并将出现次数为 1 的所有元素添加到列表中。然后将此列表转换为数组,这是您需要 return.

复杂度为 O(n*n),其中 n 是方阵 m 的一维。

import java.util.*;
import java.io.*;
    
class GetSingleOccurence
{
    static int[] singleOccurence(int[][] m)
    {
        // work with a list so that we can append to it
        List<Integer> aList = new ArrayList<Integer>();

        HashMap<Integer, Integer> hm = new HashMap<>();

        for (int row = 0; row < m.length; row++) {
            for (int col = 0; col < m[row].length; col++) {
                if (hm.containsKey(m[row][col]))
                    hm.put(m[row][col], 1 + hm.get(m[row][col]));
                else
                    hm.put(m[row][col], 1);
            }
        }

        for (Map.Entry entry : hm.entrySet())
        {
            if (Integer.parseInt(String.valueOf(entry.getValue())) == 1)
                a.add(Integer.parseInt(String.valueOf(entry.getKey())));
        }

        // return a as an array
        return a.toArray(new int[a.size()]);
    }
    
    public static void main(String args[])
    {
            // A 2D may of integers with some duplicates

            int[][] m = { { 1, 2, 3, 4, 5 },
                            { 6, 7, 8, 9, 10 },
                            { 11, 12, 12, 14, 15 },
                            { 16, 17, 18, 18, 20 },
                            { 21, 22, 23, 24, 25 } };
    
            a = singleOccurence(m);
    }
}

最好使用布尔数组 boolean[] dups 来跟踪重复的数字,因此在第一次传递期间填充此中间数组并计算单打的数量。

然后创建适当大小的结果数组,如果此数组不为空,则在 dups 的第二次迭代中将标记为单值的值复制到结果数组。

public static int[] getSingles(int[][] arr) {
    int n = arr.length;
    int m = arr[0].length;
    boolean[] dups = new boolean[n * m];
    int singles = 0;
    for (int i = 0; i < dups.length; i++) {
        if (dups[i]) continue; // skip the value known to be a duplicate
        int curr = arr[i / m][i % m];
        boolean dup = false;
        for (int j = i + 1; j < dups.length; j++) {
            if (curr == arr[j / m][j % m]) {
                dup = true;
                dups[j] = true;
            }
        }
        if (dup) {
            dups[i] = true;
        } else {
            singles++;
        }
    }
    // debugging log
    System.out.println("singles = " + singles + "; " + Arrays.toString(dups));
    int[] res = new int[singles];
    if (singles > 0) {
        for (int i = 0, j = 0; i < dups.length; i++) {
            if (!dups[i]) {
                res[j++] = arr[i / m][i % m];
            }
        }
    }
    return res;
}

测试:

int[][] mat = {
    {2, 2, 3, 3},
    {4, 2, 0, 3},
    {5, 4, 2, 1}
};    
System.out.println(Arrays.toString(getSingles(mat)));

输出(包括调试日志):

singles = 3; [true, true, true, true, true, true, false, true, false, true, true, false]
[0, 5, 1]

您对 previous 的使用只是一个即将出现的想法。删除它,并填充一维a。使用两个嵌套的 for 循环查找重复项需要 n4 个步骤。但是,如果您对数组 a 进行排序 - 对值进行排序 - 成本 n² log n²,您可以更快地找到重复项。

Arrays.sort(a);
int previous = a[0];
for (int h = 1; h < a.length; ++h) {
    if (a[h] == previous)...
    previous = a[h];
 ...

看起来这个解决方案几乎已经在 class 中处理过了。