【问题标题】:Determine whether rows in 2D array are unique确定二维数组中的行是否唯一
【发布时间】:2015-02-07 22:54:09
【问题描述】:

我正在尝试创建一个算法来确定二维整数数组中的所有行是否都是唯一的(即不相交的)。目前,我有一个蛮力算法,它检查每一行中的每个值与其他行中的每个值,但我想加快速度。是否有某种分而治之的方法来处理这个问题?我在单个数组和列表中找到了一些半解决方案,但在二维数组中没有。

【问题讨论】:

  • 你想用什么语言来做这个?
  • 这个问题不就等同于检查二维数组中的所有数字是否唯一吗?
  • @CandiedOrange:最好是Java。
  • 一行中的数字位置有什么意义吗?我所知道的唯一不相交的定义是关于集合。如果您的行不是集合,是否打算将它们配对成集合?
  • @CandiedOrange:我想我不知道您所说的“成组配对”是什么意思。二维数组的行中不能有重复项,但行只能包含一种整数,例如,A 行可能全为 2,B 行可能全为 3,它们将被视为不相交。

标签: java arrays algorithm multidimensional-array


【解决方案1】:

如果允许使用 java 集合,请考虑使用 java 的 HashSet。它是为这样的事情而设计的:

areDisjoint() 灵感来自http://www.geeksforgeeks.org/check-two-given-sets-disjoint/

import java.util.*;

public static void main (String[] args)
{        

    int[][] twoD = {{3,3,3},{4,4,4},{5,5,5}};
    if ( disjointRows(twoD) )
    {
        System.out.println("Mutually disjoint rows");
    }
    else
    {
        System.out.println("Some element duplicated in other row");
    }
}

public static boolean disjointRows(int[][] twoD)
{

    // -- Copy 2D array into data structure -- //

    ArrayList<HashSet<Integer>> rows = new ArrayList<HashSet<Integer>>();

    for (int[] row : twoD)
    {            
        HashSet<Integer> hs = new HashSet<Integer>();
        for (int elem : row)
        {
            hs.add(elem);                
        }
        rows.add( hs );
    }

    // Above is O = r*c time just as it would be to copy the 2D array. 
    // or you can express it as O(n^2)


    // -- Mutual disjoint rows test -- //

    // Compare every combination of rows
    for (int y=1; y< rows.size(); y++)
    {
        for (int x=0; x<y; x++)
        {
          //TODO remove debugging code                
            System.out.print(areDisjoint( rows.get(x), rows.get(y) ) );
            System.out.print("=");
            System.out.print("(" + x + "," + y + ")  ");

            if (! areDisjoint( rows.get(x), rows.get(y) )  )
            {
                return false;
            }
        }
        System.out.println("");
    }
    return true;
    //Above is O = r(r-1)/2 * c 
    //or you can express it as O(n^3)
}

static boolean areDisjoint(Set<Integer> set1, Set<Integer> set2)
{
    //set1 must not contain anything in set2 
    for (int i : set2)
    {
        if ( set1.contains(i) )
            return false;
    }
    return true;
    // Above is c or O(n) because contains lookup is O(1)
}

输出:

// 真=(0,1)
// 真=(0,2) 真=(1,2)
// 互不相交的行

我不知道这是否比您的“蛮力”解决方案更好,因为您没有发布它。

我可以从一个大的 O 表示法的角度来看,这与 japreiss 的解决方案并驾齐驱。我们都在 O(n^3)。此解决方案使用更多内存,因为它复制 2D 数组而不是对其进行变异。当性能相同时,我强烈建议您根据可读性选择算法。我承认我的更长。我花时间评论它。

另见:Is a Java hashmap really O(1)?

//         If each row is to be disjoint from all other rows then
//         then testing them all against each other would produce
//         this truth table.  
//        
//                0    1    2   
//              |----|----|----|
//            0 | F  | T  | T  |
//              |----|----|----|
//            1 | T  | F  | T  |
//              |----|----|----|
//            2 | T  | T  | F  |
//              |----|----|----|
//        
//         Testing a row against it self can be ignored as can        
//         tests that simply transpose the input rows. Leaving us with
//        
//                0    1    2   
//              |
//            0 | 
//              |----|
//            1 | T  |
//              |----|----|
//            2 | T  | T  |
//              |----|----|
//        
//         So long as that's true, the rows are mutually disjoint

【讨论】:

  • 集合很棒,但它们不允许在其中重复,因此,例如 [3 3 3] [4 4 4] 的二维数组不会这样工作,对吧?
  • @amanda_dennis 如果如您所说,行中的位置并不重要,那么每一行都可以减少到一个集合而不会丢失任何重要信息,因为 [3,3,3] 只传达了 3其他任何地方都不允许。我已经用一个适用于任何大小的二维数组的解决方案更新了我的答案。
  • @amanda_dennis - [3,3,3] 作为集合成为集合 {3} 而 [4,4,4] 成为集合 {4}。这两组是不相交的。但是,例如 [1,2,2] 变为 {1,2} 并且 [2,3,3] 变为 {2,3} (无论顺序如何)。这两组并不相交。我想这就是你的目标。我认为我的答案在不相交的测试中使用计数更优雅一些,但按照 CandiedOrange 的建议遍历它们也应该有效。
【解决方案2】:

编辑:我的回答缓慢而愚蠢。或者说得更亲切一点,它做了不必要的工作,因为它计算的是哪两行相交,而不是是否有任何两行相交。

发布后,我想到了 2 种更快的算法,但结果证明它们是 Danylo Mysak 的答案,并且是 danh 暗示的“运行总集”。

我仍然想对这些不同的方法进行基准测试。

【讨论】:

  • 我敢打赌,我们比你想象的更接近。如果你复制数组,那就是 O(n^2)。对数组进行排序是 O(n*nlogn)。你的最后一个循环块是 O(n^3) 和我的一样。至于拳击int,这是一次性交易,无论如何都会缓存-128到127之间的数字。另一方面,你有一个 if 结构可能会抛出分支预测。但我们正在分裂头发。我敢打赌我们非常接近。不过,如果我必须在 c 中解决这个问题,我宁愿移植你的代码。 :)
【解决方案3】:

要测试任何两行是否不相交**,请创建两个集合(例如拒绝重复的 java 集合,几乎在 O(1) 时间内完成),每行一个并计算每个集合的元素。

两行不相交,如果:

count(rowASet) + count(rowBSet) == count( union(rowASet, rowBSet) )

这建议了一种用于 2D 数组的算法,其中行被连续添加(作为集合)到运行总集合中。总集合的计数是在添加每行集合之前和之后测量的。如果该计数随着刚刚添加的行集的大小而增加,则刚刚添加的行与目前添加的行不相交。

**查看与@CandiedOrange 的对话。我用“不相交的行”来表示一行不包含在任何其他行中找到的元素,但它本身可能在内部包含重复的元素。

【讨论】:

    【解决方案4】:

    如果要检查是否有两行包含相同的数字,可以将所有数字连同它们所属的行数放在一个长列表中。然后对这个列表进行排序,这将使所有相同的数字彼此相邻。你可以很容易地确定每个相同数字的集群是否最初属于同一行。

    如果你的表是n × m,算法会运行在O(nm × (log(n) + log(m)))。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-31
      • 1970-01-01
      • 2021-03-15
      • 2015-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多