[英]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.