简体   繁体   English

Java通过特定值存储int矩阵中的值的更好方法

[英]Java better way to store values from matrix of int by specific values

I have an matrix of int[][] and I want to store coords in different arrays according to it's values in the matrix. 我有一个int [] []矩阵,我想根据它在矩阵中的值将坐标存储在不同的数组中。 I think that my solution works but it's not very performant... 我认为我的解决方案有效,但是效果不是很好...

    public ArrayList<ArrayList<Coords>> storeValues(int[][] labels) {
    int max = getMaxValue(labels);
    ArrayList<ArrayList<Coords>> collection = new ArrayList<ArrayList<Coords>>();
    ArrayList<Coords> coords;

    while (max > 0) {
        coords = new ArrayList<Coords>();
        for (int x = 0; x < labels.length; x++) {
            for (int y = 0; y < labels[0].length; y++) {
                if (max == labels[x][y]) {
                    coords.add(new Coords(x,y));
                }
            }
        }
        collection.add(coords);
        --max;
    }
    return collection;
}

private int getMaxValue(int[][] labels) {
    int max = labels[0][0];
    for (int tabValue[] : labels) {
        for (int value : tabValue) {
            if (max < value) {
                max = value;
            }
        }

    }
    return max;
}

For exemple : 举个例子 :
My matrix containt 我的矩阵包含

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

Expected result 预期结果

ArrayList{ 
  ArrayList{ Coord[0,0], Coord[1,0], Coord[2,0] }, // list of 0 value  
  ArrayList{ Coord[0,1], Coord[1,1], Coord[2,1], Coord[0,3] }, // list of 1 value
  ...
}

Your goal should be to iterate as little as possible. 您的目标应该是尽可能少地迭代。 You can actually do it in one single (nested) iteration, if you use a Map for building up your desired data structure. 如果使用Map构建所需的数据结构,则实际上可以一次(嵌套)迭代来完成。 Most helpful here is a TreeMap , because it automatically sorts by key.. 这里最有用的是TreeMap ,因为它会自动按键排序。

The logic is, instead of nested lists, build up a new TreeMap<Integer, ArrayList<Coords>> . 逻辑是建立一个new TreeMap<Integer, ArrayList<Coords>>而不是嵌套列表。 The Integer-key is your value, and the arrayList is the list of Coords for that value. Integer键是您的值,而arrayList是该值的Coords列表。

You iterate over the matrix as before, but without calculation of max . 您像以前一样遍历矩阵,但不计算max This saves you the whole getMaxValue method and the outer while loop. 这样可以节省整个getMaxValue方法和外部while循环。 For each value, you first check if the TreeMap has an entry with that key. 对于每个值,您首先要检查TreeMap是否具有带有该键的条目。 If yes, add your new Coord to map.get(val). 如果是,请将新Coord添加到map.get(val)。 If no, create a new ArrayList<Coord> , add your Coord to that list, and put it into the map. 如果否,则创建一个new ArrayList<Coord> ,将您的Coord添加到该列表中,然后将其放入地图中。

If you absolutely must have ArrayList> as return type, you can simply convert the map's valueSet at the end with return new ArrayList<ArrayList<Coord>>(map.values()) 如果绝对必须将ArrayList>作为返回类型,则只需在末尾转换地图的valueSet,然后return new ArrayList<ArrayList<Coord>>(map.values())

I would agree that your current algorithm might not perform well. 我同意您当前的算法可能效果不佳。 If you say your matrix has dimension M*N , a runtime analysis might look like: 如果您说矩阵的维度为M*N ,则运行时分析可能​​类似于:

max_val = get_max_val(); // O(M*N)
for i from max_val to 0: // O(max_val)
    // O(M*N)
    for x from 0 to M:
        for y from 0 to N:
            do_stuff;

In other words, this works out to O(M*N*max_val) . 换句话说,这O(M*N*max_val) This is dangerous if your max_val is very large; 如果您的max_val非常大,这很危险; especially since the actual algorithm doesn't have to depend on what the values are (just which ones are the same). 特别是因为实际的算法不必依赖于价值是什么 (只是哪些是相同的)。

An alternative algorithm which only depends on M and N : 仅取决于MN的替代算法:

HashMap<Integer, ArrayList<Coords>> coordsMap = new HashMap<Integer, ArrayList<Coords>>();
for (int i = 0; i < labels.length; i++) {
    for (int j = 0; j < labels[i].length; j++) {
        if (coordsMap.containsKey(labels[i][j])) {
            ArrayList<Coords> coords = coordsMap.get(labels[i][j]);
            coordsMap.put(labels[i][j], coords.add(new Coords(i, j));
        }
    }
}

// collect results
ArrayList<ArrayList<Coords>> coordsList = new ArrayList<ArrayList<Coords>>();
for (Integer label : coordsMap.keySet()) {
    coordsList.add(coordsMap.get(label));
}
return coordsList;

The runtime on this is M*N*(HashMap get/put time) . 在此的运行时间为M*N*(HashMap get/put time)

If you want to have an ArrayList for every value from 0 to max_val , where an absence of coordinates with that label is represented by an empty ArrayList, you could change the // collect results portion to look something like: 如果要为每个值( from 0 to max_val一个ArrayList,其中用空的ArrayList表示缺少带有该标签的坐标,则可以更改// collect results部分,使其类似于:

// collect results
for (int i = 0; i < max_val; i++) {
    if (coordsMap.containsKey(i)) {
        coordsList.add(coordsMap.get(i));
    }
    else {
        coordsList.add(new ArrayList<Coords>());
    }
}

As Marco13's comment on your question suggests, the actual performance will likely depend somewhat on how your data actually looks. 正如Marco13对您的问题的评论所暗示的那样,实际性能可能在某种程度上取决于您的数据的实际外观。

When the values are already sorted, the solution is rather simple: Just walk through the matrix (in the sorted order), and always create a new list when you encounter a new value. 当值已经排序后,解决方案就相当简单:只需遍历矩阵(按排序顺序),并在遇到新值时始终创建一个新列表。

When the values are not sorted, then you can create a list of coordinates, sort these by the corresponding value, and then apply the same method to it like for the sorted case. 当值排序,那么你就可以创建坐标列表,通过相应的值排序这些,然后采用相同的方法将其像排序情况。

Both versions are implemented here: 这两个版本都在这里实现:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class SortMatrixCoordinates
{
    static class Coords
    {
        int x;
        int y;
        Coords(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
        @Override
        public String toString()
        {
            return "("+x+","+y+")";
        }
    }

    public static void main(String[] args)
    {
        runWithSortedMatrix();
        runWithUnsortedMatrix();
    }

    private static void runWithSortedMatrix()
    {
        int labels[][] = new int[][]{
            { 0, 0, 0 },  
            { 1, 1, 1 },  
            { 1, 2, 2 },   
            { 2, 2, 5 } };

        System.out.println("Result with sorted matrix:");
        List<List<Coords>> result = storeValues(labels);
        for (List<Coords> list : result)
        {
            System.out.println(list);
        }
    }

    private static void runWithUnsortedMatrix()
    {
        int labels[][] = new int[][]{
            { 0, 0, 0 },  
            { 3, 3, 3 },  
            { 3, 2, 2 },   
            { 2, 2, 1 } };

        System.out.println("Result with unsorted matrix:");
        List<List<Coords>> result = storeValuesSorting(labels);
        for (List<Coords> list : result)
        {
            System.out.println(list);
        }
    }

    public static List<List<Coords>> storeValues(final int[][] labels)
    {
        List<List<Coords>> result = new ArrayList<List<Coords>>();
        List<Coords> coords = null;
        int previousValue = 0;
        for (int x = 0; x < labels.length; x++) 
        {
            for (int y = 0; y < labels[0].length; y++) 
            {
                int value = labels[x][y];
                if ((x == 0 && y == 0) || previousValue != value)
                {
                    coords = new ArrayList<Coords>();
                    result.add(coords);
                }
                coords.add(new Coords(x,y));
                previousValue = value;
            }
        }
        return result;
    }


    public static List<List<Coords>> storeValuesSorting(final int[][] labels) 
    {
      List<Coords> sortedCoords = new ArrayList<Coords>();
      for (int x = 0; x < labels.length; x++) 
      {
          for (int y = 0; y < labels[0].length; y++) 
          {
              sortedCoords.add(new Coords(x,y));
          }
      }
      Collections.sort(sortedCoords, new Comparator<Coords>()
      {
          @Override
          public int compare(Coords c0, Coords c1)
          {
              int v0 = labels[c0.x][c0.y];
              int v1 = labels[c1.x][c1.y];
              return Integer.compare(v0, v1);
          }

      });
      List<List<Coords>> result = new ArrayList<List<Coords>>();
      List<Coords> coords = null;
      int previousValue = 0;
      for (int i=0; i<sortedCoords.size(); i++) 
      {
          Coords c = sortedCoords.get(i);          
          int value = labels[c.x][c.y];
          if (i == 0 || previousValue != value)
          {
              coords = new ArrayList<Coords>();
              result.add(coords);
          }
          coords.add(c);
          previousValue = value;
      }
      return result;
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM