繁体   English   中英

根据第一列对二维数组进行排序

[英]Sort a two dimensional array based on the first column

我已经阅读了很多stackoverflow-posts和一些google-results,但是还没有找到完美的东西。

基本上我有一个二维数组,在第一行中带有点(整数) ,并有一个整数来标识这些点属于哪个“游程”。 我想基于这些点对数组进行排序,但是在排序时不会丢失标识符。

编写排序代码的捷径是什么?

之前:

string points[][] = 
        {
          { 12, 13, 10, 0, 0, 0 },
          { 1, 2, 3, 4, 5, 6 },      
        }

后:

string points[][] = 
        {
          { 13, 12, 10, 0, 0, 0 },
          { 2, 1, 3, 4, 5, 6 },      
        }

不是最好的例子,但我希望您明白这一点。

不幸的是,Java的Collections库缺少用于对“并行数组”进行排序的方法。 创建一个类来保存您的运行,使用带有自定义比较器的类库对其进行排序,然后将数据放回数组中:

public class Run {
    public int points;
    public int runId;
}

Run[] runs = new Run[points[0].length];
for (int i = 0 ; i != runs.length ; i++) {
    Run r = new Run();
    r.points = points[0][i];
    r.runId = points[1][i];
    runs[i] = r;
}
Arrays.sort(runs, new Comparator<Run>() {
    public int compare(Run a, Run b) { 
        return -Integer.compare(a.points, b.points);
    }
});
for (int i = 0 ; i != runs.length ; i++) {
    points[0][i] = runs[i].points;
    points[1][i] = runs[i].runId;
}

演示

好的,所以通常我建议使用排序的地图/数组列表,但是如果那不是一个选择的话...

像在第一行中进行其他操作一样,通过正常的排序算法运行。 但是,如果您必须进行交换,则只需以相同的方式同时交换第二行的值,例如:

//loop through int i

if(points[i][0] < points[i+1][0]){
    int temp = points[i][0];
    points[i][0] = points[i + 1][0];
    points[i+1][0] = temp;

    temp = points[i][1];
    points[i][1] = points[i + 1][1];
    points[i+1][1] = temp;
}

漂亮又简单。

使用简单的选择排序:

public static void test()
{
        Integer points[][] = 
                {
                  { 12, 13, 10, 0, 0, 0 },
                  { 1, 2, 3, 4, 5, 6 },      
                };
        selectionSort(points[0], points[1]);
        System.out.println(Arrays.toString(points[0]));
        System.out.println(Arrays.toString(points[1]));
}

private static void swap(Integer[] arr, Integer[] arr2, int i, int minIndex)
{
    int tmp = arr[i];
    arr[i] = arr[minIndex];
    arr[minIndex] = tmp;

    tmp = arr2[i];
    arr2[i] = arr2[minIndex];
    arr2[minIndex] = tmp;       
}

// Use any sorting algorithm you like, just keep the swap method.
public static void selectionSort(Integer[] arr, Integer[] arr2) 
{
    int i, j, minIndex;
    int n = arr.length;
    for (i = 0; i < n - 1; i++) 
    {
        minIndex = i;

        for (j = i + 1; j < n; j++)
            if (arr[j] > arr[minIndex])
                minIndex = j;

        if (minIndex != i) 
        {
            swap(arr, arr2, i, minIndex);
        }
    }
}

您是否可以交换索引,即在示例中将2x6数组存储为6x2数组,并通过points [y] [x]而不是points [x] [y]对其进行索引?

如果是这样,您可以将常规排序功能与自定义比较器一起使用,该比较器将比较每个子数组中的第一个元素:

Arrays.sort(points, new Comparator<int[]>() {
    @Override
    public int compare(int[] o1, int[] o2) {
        return o2[0] - o1[0];
    }
});

由于不需要在所有相关行中手动交换元素,因此运行速度也快得多。

因此,如果您可以重新组织其余代码以适应排序而又不影响其他主要性能,则可以这样做。

否则,您可能必须像其他答案中所建议的那样实施手动排序,或者可以在排序之前和之后对数组进行转置,尽管我怀疑这样做会更快。

恕我直言,好的方法是仅对索引进行排序,但使用自定义比较器比较这些值。 与0Andrea提出的解决方案仅略有不同。 不同之处在于,这里我们有两个不同的数组,一个用于索引,一个用于值,而不是多列行。

这样做的好处是您不必实现排序算法,而只需使用在JDK中本地实现的TimSort。 这是一个Junit实现

public class SortTest {
    Integer points[][] = {
        { 12, 13, 10, 0, 0, 0 },
        { 1, 2, 3, 4, 5, 6 }, 
    };

    private Integer[] sort(Integer tab[][]) {
        IndexComparator<Integer> comparator = new IndexComparator<>(tab[0]);
        List<Integer> indexList = (List<Integer>) new ArrayList<>(Arrays.asList(tab[1]));
        Collections.sort(indexList, comparator);
        Integer[] result = new Integer[indexList.size()];
        return indexList.toArray(result);
    }

    private static class IndexComparator<T extends Comparable> implements Comparator<Integer> {
        Integer[] values;

        public IndexComparator(Integer[] values) {
            this.values = values;
        }

        @Override
        public int compare(Integer t, Integer t1) {
            return values[t1-1] - values[t-1];
        }
    }

    @Test
    public void test() {
        Integer[] result = sort(points);
        assertEquals(new Integer[]{2,1,3,4, 5, 6}, result);
        Integer[] values = new Integer[result.length];
        for (int i=0; i<values.length; i++) {
            values[i] = points[0][result[i] - 1];
        }
        assertEquals(new Integer[]{13,12,10,0,0,0}, values);
    }
}

暂无
暂无

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

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