在2D int数组算法

时间:2017-05-24 11:03:20

标签: java arrays algorithm multidimensional-array

我正在开发老虎机并面临收集结果结果的问题。问题是在2D int数组中收集重复值索引的最快方法是什么?这里的条件是仅收集出现5

的值的标识符

案例1

输入(仅获取 3 值的索引):

int[][] input = new int[][]{
            new int[]{1, 2, 3, 4, 8},
            new int[]{6, 3, 2, 3, 5},
            new int[]{3, 9, 7, 1, 3}
    };

预期产出:

[2, 1, 0, 1, 2]

案例2

输入(仅获取 3 5 值的索引):

int[][] input = new int[][]{
            new int[]{1, 5, 3, 5, 8},
            new int[]{5, 3, 5, 3, 5},
            new int[]{3, 9, 7, 1, 3}
    };

预期产出:

[2, 1, 0, 1, 2] //for 3 value
[1, 0, 1, 0, 1] //for 5 value

我的解决方案(非常糟糕)

1)收集重复项(此项不适用于案例2

Map<Integer, Integer> amountMap = new HashMap<>();
for (int[] row : railSpin) {
    for (int value : row) {
        amountMap.put(value, amountMap.containsKey(value) ? amountMap.get(value) + 1 : 1);
    }
}

2)删除非5场比赛

if (amountMap.containsValue(5)) {
    Iterator<Integer> amountIterator = amountMap.values().iterator();
    while (amountIterator.hasNext()) {
        if (amountIterator.next() != 5) {
            amountIterator.remove();
        }
    }
}

3)颠倒迭代并收集索引

List<Integer> indexes = new ArrayList<>();
for (int row = 0; row < 5; row++) {
    for (int col = 0; col < railSpin.length; col++) {
        int valueToCheck = railSpin[col][row];
        if (amountMap.keySet().contains(valueToCheck)) {
            indexes.add(col);
        }
    }
}

4)如果需要,拆分数组

List<List<Integer>> splitResults = new ArrayList<>();
for (int start = 0; start < indexes.size(); start += 5) {
    int end = Math.min(start + 5, indexes.size());
    List<Integer> sublist = indexes.subList(start, end);
    splitResults.add(new ArrayList<>());
    splitResults.get(start /5).addAll(sublist);
}

你能否提出一个没有那么多迭代的解决方案,哪个适用于 CASE 2 ?我相信stackoverflow的力量

5 个答案:

答案 0 :(得分:2)

我是怎么做到的:

  1. 遍历整个表ONCE以创建索引映射
  2. 删除任何没有5个有效索引的条目
  3. 编辑:我转而使用ArrayList,因为它更好:

    代码:

    public static void main(String t[]) throws IOException {
        int[][] input1 = new int[][] { 
            new int[] { 1, 2, 3, 4, 8 },
            new int[] { 6, 3, 2, 3, 5 },
            new int[] { 3, 9, 7, 1, 3 } };
    
        int[][] input2 = new int[][] { 
            new int[] { 1, 5, 3, 5, 8 }, 
            new int[] { 5, 3, 5, 3, 5 },
            new int[] { 3, 9, 7, 1, 3 } };
    
        System.out.println("case 1");
        doWith(input1);
        System.out.println("case 2");
        doWith(input2);
    }
    
    public static void doWith(int[][] table){
        Map<Integer, List<Integer>> allIndexes = getAllIndexes(table);
        /* Java 8 style
        Map<Integer, List<Integer>> fiveOccurrencesIndexes = allIndexes.entrySet().stream()
                .filter(e ->e.getValue().size() == ROW_SIZE)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
                 */
        Map<Integer, List<Integer>> fiveOccurrencesIndexes = new HashMap<Integer,List<Integer>>();
        for(Map.Entry<Integer,List<Integer>> entry : allIndexes.entrySet()){
            if(entry.getValue().size() == ROW_SIZE){
                fiveOccurrencesIndexes.put(entry.getKey(), entry.getValue());
            }
        }
    
        fiveOccurrencesIndexes.entrySet().forEach(e -> System.out.println(e.getKey()+ " : "+e.getValue()));
    }
    
    // Map of indexes per value
    public static Map<Integer,List<Integer>> getAllIndexes(int[][] table){
        Map<Integer,List<Integer>> result = new HashMap<>();
        // we should force minValue < maxValue
        for(int i=0; i<ROW_SIZE; i++){  
            for(int j=0;j<COL_SIZE; j++){
                Integer value = table[j][i];
                if(!result.containsKey(value)){ 
                    List<Integer> indexes = new ArrayList<>(); // init value
                    result.put(value, indexes);
                }
                result.get(value).add(j);
            }
        }
        return result;
    }
    

    输出:

    case 1
    3 : [2, 1, 0, 1, 2]
    case 2
    3 : [2, 1, 0, 1, 2]
    5 : [1, 0, 1, 0, 1]
    

答案 1 :(得分:1)

首先,查找五次或更多次出现的所有数字:

int[] counts = new int[10];
for (int r = 0 ; r != 3 ; r++)
    for (int c = 0 ; c != 5 ; c++)
        counts[input[r][c]]++;

现在使用count来生成索引数组(这几乎是对算法中组合步骤(3)和(4)的重新安排):

List<List<Integer>> splitResults = new ArrayList<>();
for (int num = 0 ; num != 10 ; num++) {
    if (counts[num] < 5) {
        continue;
    }
    List<Integer> toAdd = new ArrayList<>();
    for (int c = 0 ; c != 5 ; c++) {
        for (int r = 0 ; r != 3 ; r++) {
            if (input[r][c] == num) {
                toAdd.add(r);
                break;
            }
        }
    }
    splitResults.add(toAdd);
}

Demo.

答案 2 :(得分:1)

我认为很难摆脱这些循环,但你可以让它适用于案例2,如下所示:

// Gather duplicates
Map<Integer, Integer> amountMap = new HashMap<>();
for (int[] row : railSpin) {
    for (int value : row) {
        amountMap.put(value, amountMap.containsKey(value) ? amountMap.get(value) + 1 : 1);
    }
}

// Create index list for 5 matches
HashMap<Integer,List<Integer>> resultsMap = new HashMap<Integer,List<Integer>>();
if (amountMap.containsValue(5)) {
    for( Integer key : amountMap.keySet()) {
        if (amountMap.get( key) == 5) {
            resultsMap.put( key, new ArrayList<Integer>());
        }
    }
}

// For all 5 matches, collect indices
List<Integer> indexList;
for( Integer index : resultsMap.keySet())
{
    indexList = resultsMap.get( index);

    for (int row = 0; row < 5; row++) {
        for (int col = 0; col < railSpin.length; col++) {
            if (railSpin[col][row] == index) {
                indexList.add(col);
            }
        }
    }
}

// Print
StringBuilder sb;
for( Integer index : resultsMap.keySet())
{
    sb = new StringBuilder();

    indexList = resultsMap.get( index);
    for( Integer i : indexList) {
        if( sb.length() == 0) {
            sb.append( "[");
        } else {
            sb.append( ", ");
        }
        sb.append( i.intValue());
    }

    sb.append( "]");
    System.out.println( sb.toString());
}

答案 3 :(得分:1)

更新:删除了选项2,因为它存在缺陷并将两个选项合并为一个解决方案。

在2D数组上,始终有一个选项可以避免至少一个嵌套迭代,通过将它们缩减为1D数组并特定行的(隐式)宽度:

int[] input = new int[]{1, 2, 3, 4, 8, 6, 3, 2, 3, 5, 3, 9, 7, 1, 3};
int gridWidth = 5;

然后,您只需要对所有值进行一次迭代。但是你还需要支持函数来获得当前值的2D索引(x,y):

public static int get_y(int index, int gridWidth) {
    return floor((index / gridWidth));
}

public static int get_x(int index, int gridWidth){
    return index % gridWidth;
}

然后,您可以一次浏览所有值,并将它们添加到索引HashMap中,并在计数器哈希映射上计算它们。

HashMap<Integer, int[]> counts = new HashMap<Integer, int[]>();
HashMap<Integer, Integer> times = new HashMap<Integer, Integer>();

//iterate once through all values
for (int i = 0; i < input.length; i++) {

    // get position in input
    int x = get_x(i, gridWidth);
    int y = get_y(i, gridWidth);
    int value = input[i];

    // add indices for this number
    // or create new indices array
    int[] indices = counts.get(value);
    if (indices == null) {
      indices = new int[gridWidth];
      for (int j = 0; j< gridWidth; j++) {
         //initialze indices with -1 as default
         indices[j] = -1;
         times.put(value, 0); //count counter
      }
    }

    // assign values
    // +1 because 0 means not present
    indices[x] = y;
    counts.put(value, indices);

    // counting up
    int counter = times.get(value);
    times.put(value, counter +1);
 }

使用具有固定宽度和初始条目-1的不同的索引数组,这些位置是否发生,而不会混淆0位置。输入的HashMap内容为:

 1 count: 2
[0] 0
[1] -1
[2] -1
[3] 2
[4] -1
2 count: 2
[0] -1
[1] 0
[2] 1
[3] -1
[4] -1
3 count: 5
[0] 2
[1] 1
[2] 0
[3] 1
[4] 2
4 count: 1
[0] -1
[1] -1
[2] -1
[3] 0
[4] -1
5 count: 1
[0] -1
[1] -1
[2] -1
[3] -1
[4] 1
6 count: 1
[0] 1
[1] -1
[2] -1
[3] -1
[4] -1
7 count: 1
[0] -1
[1] -1
[2] 2
[3] -1
[4] -1
8 count: 1
[0] -1
[1] -1
[2] -1
[3] -1
[4] 0
9 count: 1
[0] -1
[1] 2
[2] -1
[3] -1
[4] -1

从这里,您可以进一步处理有关您所有需求的数据。这两种方法仍需要一些额外的迭代(选项1:for循环用于新索引,选项2:底层计算ArrayList添加操作)但我希望这可以满足您的需求。

这种方法的优点:

  • 所有索引都是在一次迭代中生成的(符合&#34; wheel&#34;行为)
  • 可扩展到一定程度(车轮数量等)
  • 将计数和出现次数拆分为单独的地图,用于计算轻量级请求

答案 4 :(得分:1)

以下是一个简短的解决方案:

final Map<Integer, List<Integer>> res = new HashMap<>();
final Map<Integer, Long> occursMap = Stream.of(input).flatMapToInt(e -> IntStream.of(e)).boxed()
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

for (int j = 0; j < input[0].length; j++) {
    for (int i = 0; i < input.length; i++) {
        if (occursMap.getOrDefault(input[i][j], 0L) == 5) {
            res.computeIfAbsent(input[i][j], ArrayList::new).add(i);
        }
    }
}