Given the constraint of just having a compare-and-swap callback, the sort algorithm has no real use for the array that it is given, other than its size, as it cannot compare elements "itself". All the array access will therefore only need to happen via the callback. As this callback does not have a return value (is opaque), the sequence of calls does not depend on the array contents, but only on its size. This sequence is what sorting networks can provide. If the callback has a constant time complexity, the time complexity of the sort function will not have a distinct worst or best case: all cases are the same.
The arguments given to the sorting function could be limited to n and the callback function.
Simplistic O(n²) algorithms
Sorting algorithms such as bubble sort, selection sort, and insertion sort can have a naive implementation with such opaque compare-and-swap callback. I say naive in the sense that they (obviously) cannot exit one of their loops early based on some comparison, as you would do in their standard definitions. Using Python syntax here:
def bubble_sort(n, compare_and_swap):
for i in range(n, 1, -1): # i is size of unsorted prefix
for j in range(1, i):
compare_and_swap(j - 1, j)
def selection_sort(n, compare_and_swap):
for i in range(n - 1): # i is size of sorted prefix
for j in range(i + 1, n):
compare_and_swap(i, j)
def insertion_sort(n, compare_and_swap):
for i in range(1, n): # i is size of sorted prefix
for j in range(i, 0, -1): # backwards
compare_and_swap(j - 1, j)
More efficient algorithms
On the other hand, several of the more efficient algorithms like merge sort, quick sort and heap sort are out as their logic heavily depends on the outcome of comparisons:
merge sort needs to know which of the two sorted partitions has the least value, as it is that partition that will "shorten" as it yields that least value. With the opaque callback, we can know the least value, but not which partition it came from.
quick sort needs to track the position of the pivot (as we can only call the callback with indices, not with values), but after calling the callback with this index we don't know where the pivot is after that call.
heap sort needs to decide which side to trickle down to based on comparison outcomes.
Sorting networks
As stated earlier, with these constraints the sequence of comparisons is only determined by the size of the input, not the array values. So for a given , that sequence would be a fixed one.
Sorting networks can be produced in O(log²) time, which is also the volume of index pairs they produce.
Here is Batcher odd–even mergesort implemented in Python for the given constraints:
def batcher_odd_even_sort(n, compare_and_swap):
p = 1
while p < n:
k = p
while k >= 1:
for j in range(k % p, n - k, 2*k):
for i in range(j, j + min(k, n - j - k)):
if i // (2*p) == (i + k) // (2*p):
compare_and_swap(i, i + k)
k >>= 1
p <<= 1
This would be one of the more efficient implementations of a sorting algorithm that only gets these arguments as input.