简体   繁体   中英

Is there any simple way to preserve original indices after sorting array without using Comparator?

I implemented an array in which I've taken one array which is zero-indexed. Consider the array below :

Array :             X S T U A C D B F H 
Index :             0 1 2 3 4 5 6 7 8 9
After sorting this array : 
Array :             A B C D F H S T U X
Index :             0 1 2 3 4 5 6 7 8 9
Original indices :  4 7 5 6 8 9 1 2 3 0

Earlier A was at index 4 but in the sorted array, it is at index 0 and the same for other values. The output expected from the program is represented as original indices. Instead of printing the elements in the sorted array, I want to print their original indices after they are sorted.

Although this code is working properly, But I want to know if there is any other simple technique that we can use to preserve the original array indices without using Comparator. The idea here is to preserve the index and hence I am using Comparator :

In this Comparator, I preserved the actual array which is to be sorted. Also, if you notice, the compare method compares the values of the actual array based on the indices supplied to it. The key here is to supply the indices and not the actual values to be sorted.

S is the character array to be sorted, indexArray is an auxiliary array. Before sorting, the index array will contain numbers in a sequence 0 – length of the array to be sorted. When I invoked the Arrays.sort method on the index array, it passes the two indices to the compare method and compares the values stored at those indices.

 ```public class ArrayIndexComparator implements Comparator<Integer>
    {
        private final Character[] A;
        public ArrayIndexComparator(Character[] arr)
        {
            this.A = arr;
        }

        
        public int compare(Integer o1, Integer o2)
        {
            return A[o1].compareTo(A[o2]);
        }
    }```
 ```public static void main(String[] args)
    {
        Character[] S = new Character[]{'X','S','T','U','A','C','D','B','F','H'};
        Integer[] indexArray = new Integer[S.length];
        IntStream.range(0, S.length).forEach(val -> indexArray[val] = val);
        ArrayIndexComparator comp = new ArrayIndexComparator(S);
        Arrays.sort(indexArray, comp);
        System.out.println(Arrays.toString(indexArray));
    }```

You could do it like this.

String[] arr =
        { "X", "S", "T", "U", "A", "C", "D", "B", "F", "H" };

Sort the indices based on the string

int[] intArray = IntStream.range(0, arr.length).boxed()
        .sorted(Comparator.comparing(i -> arr[i]))
        .mapToInt(Integer::intValue).toArray();
System.out.println(Arrays.toString(intArray));

Then use Arrays.setAll to construct the sorted array of characters.

String[] newArr = new String[arr.length];
Arrays.setAll(newArr,i->arr[intArray[i]]);
System.out.println(Arrays.toString(newArr));

Prints

[4, 7, 5, 6, 8, 9, 1, 2, 3, 0]
[A, B, C, D, F, H, S, T, U, X]

There are at least a couple ways of avoiding the use of a custom Comparator to sort by index rather than value, but they're not simple and I would strongly advise against using them - I'm presented theme here for the sake of interest.

By the way, using the lambda form of Comparator your code could be simplified a little:

Arrays.setAll(indexArray, i -> i);    
Arrays.sort(indexArray, (a, b) -> S[a].compareTo(S[b]));

The first idea would be to create a surrogate value that combines the original Character value and its index, eg by multiplying the value by the length of the array n and adding the index. After sorting on this surrogate value you reverse the packing by taking mod n to get the index.

Character[] s = new Character[]{'X','S','T','U','A','C','D','B','F','H'};

int n = s.length;

Integer[] idx = new Integer[s.length];
for(int i=0; i<s.length; i++) idx[i] = s[i]*n + i;

System.out.println(Arrays.toString(idx));

Arrays.sort(idx);

System.out.println(Arrays.toString(idx));

for(int i=0; i<s.length; i++) idx[i] %= n;

System.out.println(Arrays.toString(idx));

Output:

[880, 831, 842, 853, 654, 675, 686, 667, 708, 729]
[654, 667, 675, 686, 708, 729, 831, 842, 853, 880]
[4, 7, 5, 6, 8, 9, 1, 2, 3, 0]

The obvious problem here is overflow in the case of large arrays and/or characters.

The other option would be to use a map to store the original positions of the characters, and perform a lookup after sorting the original array.

Character[] s = new Character[]{'X','S','T','U','A','C','D','B','F','H'};

Map<Character,Integer> map = new HashMap<>();
for(int i=0; i<s.length; i++) map.put(s[i], i);

System.out.println(map);

Arrays.sort(s);

Integer[] idx = new Integer[s.length];
for(int i=0; i<s.length; i++) idx[i] = map.get(s[i]);

System.out.println(Arrays.toString(idx));

Output:

{A=4, B=7, C=5, S=1, D=6, T=2, U=3, F=8, H=9, X=0}
[4, 7, 5, 6, 8, 9, 1, 2, 3, 0]

The problem here is that duplicates are not allowed.

您可以使用Comparator.comparing更轻松地定义您的Comparator

Comparator<Integer> comp = Comparator.comparing(i -> S[i]);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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