在许多情况下,我需要知道数组的排序顺序,而不是对数组进行排序。例如,假设有五(5)个数组包含不同类型的各种信息(字符串,整数,双等),并且数组是同步的,这意味着每个数组的第n个元素组合在一起。想象一下,现在第一个数组有一个“名称”值,一个字符串,我想删除所有具有重复名称的值。我需要获取名称数组的排序键,并使用它来消除所有五个数组中的重复项。我无法对名称数组进行排序,因为它将不再与其他数组同步。
为了解决这个问题,我一直在编写自己的排序例程。最初,我有几个不同版本的QuickSort(取决于数据类型,数组是基于一个还是从零开始,它是什么类型,升序,降序,区分大小写,不区分大小写等)最近我一直在尝试制作各种基数类型,我发现它们比某些类型的数据快于QuickSort。我的例程返回排序键,而不是对数组本身进行排序。换句话说,它们返回一个包含整数的数组,每个整数指示目标数组的哪个元素属于该位置。因此,例如,如果sort键的第一个值是43,那么它意味着目标数组的第43个元素是排序顺序中的第一个元素。
现在,虽然编写所有这些排序例程令人着迷和教育,但我想知道是否有更好的技术可以让我利用现有的排序库?有没有办法可以使用Java / C中的标准库资源获取排序键?
的更新 的
我尝试了Juan Lopez推荐的间接排序方法,似乎有效。代码:
private final static void test_indirect_sort(){
final String[] test_array = { "pear", "peach", "doggie", "apple", "dog", "prairie", "a", "tundra", "flamingo", "barn" };
Integer[] sorted_keys = new Integer[test_array.length];
for( int i = 0; i < sorted_keys.length; i++ ) sorted_keys[i] = i;
java.util.Arrays.sort(sorted_keys, new java.util.Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return test_array[o1].compareTo(test_array[o2]);
}
});
for( int xKey = 0; xKey < sorted_keys.length; xKey++ ) System.out.print( test_array[sorted_keys[xKey]] + " " );
System.out.println();
for( int xKey = 0; xKey < sorted_keys.length; xKey++ ) System.out.print( test_array[xKey] + " " );
System.out.println();
}
这个解决方案的主要缺点是需要Integer对象而不是原始int,这意味着密钥数组的空间至少增加4倍,并且在Integer包装器中访问int的速度变慢。
关于效果的说明
从评论看来,有些人似乎认为使用对象数组与使用并行数组一样快。当我运行以下代码时:
private final static void testArrayAccess(){
int[] a = new int[30000000];
int[] b = new int[30000000];
int[] c = new int[30000000];
MultiArray[] list = new MultiArray[30000000];
java.util.Random random = new Random();
for( int x = 0; x < 30000000; x++ ){
a[x] = random.nextInt(100);
b[x] = random.nextInt(100);
c[x] = random.nextInt(100);
list[x] = new MultiArray();
list[x].a = a[x];
list[x].b = b[x];
list[x].c = c[x];
}
long start1 = System.currentTimeMillis();
int sum = 0;
for( int x = 0; x < 30000000; x++ ){
sum += a[x] + b[x] + c[x];
}
long end1 = System.currentTimeMillis();
long start2 = System.currentTimeMillis();
sum = 0;
for( int x = 0; x < 30000000; x++ ){
sum += list[x].a + list[x].b + list[x].c;
}
long end2 = System.currentTimeMillis();
System.out.format( "parallel arrays: %d bundled object: %d\n", (end1 - start1), (end2-start2) );
}
我得到了输出:
parallel arrays: 4 bundled object: 15
显示在我的系统上使用捆绑对象的测试用例几乎比使用并行数组慢4倍(更不用说使用更多内存)。要查看它为什么慢一点,这里是sum +=
语句的字节代码反汇编,左边是并行数组,右边是捆绑对象:
LINENUMBER 32 L20 LINENUMBER 39 L27
ILOAD 7 ILOAD 7
ALOAD 0 ALOAD 3
ILOAD 8 ILOAD 12
IALOAD AALOAD
ALOAD 1 GETFIELD cra/common/MultiArray.a : I
ILOAD 8 ALOAD 3
IALOAD ILOAD 12
IADD AALOAD
ALOAD 2 GETFIELD cra/common/MultiArray.b : I
ILOAD 8 IADD
IALOAD ALOAD 3
IADD ILOAD 12
IADD AALOAD
ISTORE 7 GETFIELD cra/common/MultiArray.c : I
IADD
IADD
ISTORE 7
左边的字节代码总是慢于右边的字节代码。
答案 0 :(得分:5)
您可以创建一个间接数组,并对其进行排序而不是原始数组。示例(在Java中):
final String[] names = {"some", "names", "some"};
Integer[] indirection = new Integer[names.length];
for (int i = 0; i < indirection.length; i++)
indirection[i] = i;
Arrays.sort(indirection, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return names[o1].compareTo(names[o2]);
}
});
答案 1 :(得分:1)
一种方法是创建一个包含原始索引信息的“holder”对象,例如:
static class Holder <T> {
int originalIndex;
T data;
}
然后,您可以将它们存储在容器中,相应地设置它们的originalIndex
(或其他),然后排序(使用适当的Comparator
)。
之后,您可以遍历已排序的数组,originalIndex
(或其他)将包含您感兴趣的信息。
更好的方法是根本不使用并行数组,而是像Don Ruby在注释中提到的那样,正确使用类来将所有相关数据保存在一个地方。然后,根本没有必要(加上传递数据更容易,例如,而不是传递firstName[]
,lastName[]
和index
,你只需传递{{1} })。