0

I have an array of integers which consists of triples.

[a1, b1, c1, a2, b2, c2, ... ,aN, bN, cN]

Every tuple (ai, bi) is unique regardless of the value of ci in the triple (ai, bi, ci).

I want to sort my array ascending by the value of ai and then ascending by the value of bi.

Of course I could introduce a struct Triple, make an array of instances of Triple and sort it with a custom sorter but this would have a big memory allocation and performance overhead.

How can I sort it within the integer array?

9
  • Sort by threes: i.e. every iteration, you're moving and manipulating 3 integers at a time. Commented Mar 21, 2021 at 21:05
  • 3
    I'm curious why you think a collection of a three integer structs is going to use more memory and take more machine cycles to sort, compared to a collection of 3x integers? Commented Mar 21, 2021 at 21:17
  • 1
    Because I would have to create them in the first place. I already have the integer array. Commented Mar 21, 2021 at 21:21
  • 1
    Write a type that acts as a facade over three integers in an array, but that looks and smells like a three integer tuple. Commented Mar 21, 2021 at 21:28
  • 4
    Do you really feel like writing a custom Sort implementation that treats each consecutive triplet of an array as a distinct entity? Writing such code correctly is not for the faint-hearted. If I was in your place I would strongly consider paying the cost of an extra array allocation, to avoid having to write such complex and error-prone code. Commented Mar 21, 2021 at 22:00

1 Answer 1

2

Here is a method for sorting arrays composed by consecutive triplets, that should be quite efficient. Each sorting operation allocates an auxiliary int[] indices array, having a third of the size of the source array. The sorting is performed on this auxiliary array, reducing the swap operations on the larger source array to the absolute minimum (at most N - 1 swaps).

public static void SortTriplets<T>(T[] source, Comparison<(T, T, T)> comparison)
{
    // Create an array of incremental indices (zero based)
    var indices = new int[source.Length / 3];
    for (int i = 0; i < indices.Length; i++) indices[i] = i;

    // Sort the indices according to the source array and the comparison delegate
    Array.Sort<int>(indices, (a, b) =>
    {
        a *= 3;
        b *= 3;
        return comparison(
            (source[a], source[a + 1], source[a + 2]),
            (source[b], source[b + 1], source[b + 2]));
    });

    // Rearrange the source array according to the sorted indices
    for (int i = 0; i < indices.Length - 1; i++)
    {
        int k = indices[i];
        while (k < i) k = indices[k];
        if (k == i) continue;
        Swap(i, k);
    }

    void Swap(int a, int b)
    {
        a *= 3;
        b *= 3;
        (T, T, T) temp = (source[a], source[a + 1], source[a + 2]);
        source[a] = source[b];
        source[a + 1] = source[b + 1];
        source[a + 2] = source[b + 2];
        source[b] = temp.Item1;
        source[b + 1] = temp.Item2;
        source[b + 2] = temp.Item3;
    }
}

Usage example:

var random = new Random();
int[] source = Enumerable.Range(1, 15).Select(_ => random.Next(1, 6)).ToArray();
Console.WriteLine($"Source: {String.Join(", ", source)}");
SortTriplets(source, (a, b) =>
{
    int ret = Comparer<int>.Default.Compare(a.Item1, b.Item1);
    if (ret == 0) ret = Comparer<int>.Default.Compare(a.Item2, b.Item2);
    return ret;
});
Console.WriteLine($"Sorted: {String.Join(", ", source)}");

Output:

Source: 3, 2, 3, 2, 2, 1, 2, 3, 2, 5, 4, 2, 1, 5, 4
Sorted: 1, 5, 4, 2, 2, 1, 2, 3, 2, 3, 2, 3, 5, 4, 2

Try it on Fiddle.

Sign up to request clarification or add additional context in comments.

1 Comment

It's awesome. And it's fast! I'm impressed, thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.