Funkin/source/funkin/util/tools/ArraySortTools.hx

158 lines
5.1 KiB
Haxe
Raw Normal View History

2023-06-22 01:41:01 -04:00
package funkin.util.tools;
/**
* Contains code for sorting arrays using various algorithms.
* @see https://algs4.cs.princeton.edu/20sorting/
*/
class ArraySortTools
{
/**
* Sorts the input array using the merge sort algorithm.
* Stable and guaranteed to run in linearithmic time `O(n log n)`,
* but less efficient in "best-case" situations.
*
* @param input The array to sort in-place.
* @param compare The comparison function to use.
*/
public static function mergeSort<T>(input:Array<T>, compare:CompareFunction<T>):Void
{
if (input == null || input.length <= 1) return;
if (compare == null) throw 'No comparison function provided.';
// Haxe implements merge sort by default.
haxe.ds.ArraySort.sort(input, compare);
}
/**
* Sorts the input array using the quick sort algorithm.
* More efficient on smaller arrays, but is inefficient `O(n^2)` in "worst-case" situations.
* Not stable; relative order of equal elements is not preserved.
*
* @see https://stackoverflow.com/questions/33884057/quick-sort-stackoverflow-error-for-large-arrays
* Fix for stack overflow issues.
* @param input The array to sort in-place.
* @param compare The comparison function to use.
*/
public static function quickSort<T>(input:Array<T>, compare:CompareFunction<T>):Void
{
if (input == null || input.length <= 1) return;
if (compare == null) throw 'No comparison function provided.';
quickSortInner(input, 0, input.length - 1, compare);
}
/**
* Internal recursive function for the quick sort algorithm.
* Written with ChatGPT!
*/
static function quickSortInner<T>(input:Array<T>, low:Int, high:Int, compare:CompareFunction<T>):Void
{
// When low == high, the array is empty or too small to sort.
// EDIT: Recurse on the smaller partition, and loop for the larger partition.
while (low < high)
{
// Designate the first element in the array as the pivot, then partition the array around it.
// Elements less than the pivot will be to the left, and elements greater than the pivot will be to the right.
// Return the index of the pivot.
var pivot:Int = quickSortPartition(input, low, high, compare);
if ((pivot) - low <= high - (pivot + 1))
{
quickSortInner(input, low, pivot, compare);
low = pivot + 1;
}
else
{
quickSortInner(input, pivot + 1, high, compare);
high = pivot;
}
}
}
/**
* Internal function for sorting a partition of an array in the quick sort algorithm.
* Written with ChatGPT!
*/
static function quickSortPartition<T>(input:Array<T>, low:Int, high:Int, compare:CompareFunction<T>):Int
{
// Designate the first element in the array as the pivot.
var pivot:T = input[low];
// Designate two pointers, used to divide the array into two partitions.
var i:Int = low - 1;
var j:Int = high + 1;
while (true)
{
// Move the left pointer to the right until it finds an element greater than the pivot.
do
{
i++;
}
while (compare(input[i], pivot) < 0);
// Move the right pointer to the left until it finds an element less than the pivot.
do
{
j--;
}
while (compare(input[j], pivot) > 0);
// If i and j have crossed, the array has been partitioned, and the pivot will be at the index j.
if (i >= j) return j;
// Else, swap the elements at i and j, and start over.
// This slowly moves the pivot towards the middle of the partition,
// while moving elements less than the pivot to the left and elements greater than the pivot to the right.
var temp:T = input[i];
input[i] = input[j];
input[j] = temp;
}
// Don't expect to get here.
return -1;
2023-06-22 01:41:01 -04:00
}
/**
* Sorts the input array using the insertion sort algorithm.
* Stable and is very fast on nearly-sorted arrays,
* but is inefficient `O(n^2)` in "worst-case" situations.
*
* @param input The array to sort in-place.
* @param compare The comparison function to use.
*/
public static function insertionSort<T>(input:Array<T>, compare:CompareFunction<T>):Void
{
if (input == null || input.length <= 1) return;
if (compare == null) throw 'No comparison function provided.';
// Iterate through the array, starting at the second element.
for (i in 1...input.length)
{
// Store the current element.
var current:T = input[i];
// Store the index of the previous element.
var j:Int = i - 1;
// While the previous element is greater than the current element,
// move the previous element to the right and move the index to the left.
while (j >= 0 && compare(input[j], current) > 0)
{
input[j + 1] = input[j];
j--;
}
// Insert the current element into the array.
input[j + 1] = current;
}
}
}
/**
* A comparison function.
* Returns a negative number if the first argument is less than the second,
* a positive number if the first argument is greater than the second,
* or zero if the two arguments are equal.
*/
typedef CompareFunction<T> = T->T->Int;