Files
BABA_YAGA/Assets/Opsive/UltimateCharacterController/Scripts/Utility/QuickSelect.cs
2026-06-09 02:05:00 +07:00

107 lines
5.3 KiB
C#

/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Utility
{
using System.Collections.Generic;
/// <summary>
/// The QuickSelect algorithm is a selection algorithm that uses a similar method as the QuickSort sorting algorithm. More information can be found on this page:
/// https://en.wikipedia.org/wiki/Quickselect.
/// </summary>
public class QuickSelect
{
/// <summary>
/// Returns the element that is the Kth smallest within the array.
/// </summary>
/// <param name="array">The array that should be searched.</param>
/// <param name="arrayCount">The number of elements to search within the array.</param>
/// <param name="k">The nth smallest value to retrieve. 0 indicates the smallest element, endIndex - 1 indicates the largest.</param>
/// <param name="comparer">The IComparer used to compare the array.</param>
/// <returns>The element that is the Kth smallest within the array.</returns>
public static T SmallestK<T>(T[] array, int arrayCount, int k, IComparer<T> comparer)
{
if (k > arrayCount - 1) {
k = arrayCount - 1;
}
return SmallestK<T>(array, 0, arrayCount - 1, k, comparer);
}
/// <summary>
/// Returns the element that is the Kth smallest within the array.
/// </summary>
/// <param name="array">The array that should be searched.</param>
/// <param name="startIndex">The starting index of the array.</param>
/// <param name="endIndex">The ending index of the array.</param>
/// <param name="k">The nth smallest value to retrieve. 0 indicates the smallest element, endIndex - 1 indicates the largest.</param>
/// <param name="comparer">The IComparer used to compare the array.</param>
/// <returns>The element that is the Kth smallest within the array.</returns>
private static T SmallestK<T>(T[] array, int startIndex, int endIndex, int k, IComparer<T> comparer)
{
if (startIndex == endIndex) {
return array[startIndex];
}
// Similar to the QuickSort algorithm, split the array into a subset and reorder based on the pivot.
var pivotIndex = Partition(array, startIndex, endIndex, comparer);
// If the pivot is same as k then the kth smallest value has been found.
if (pivotIndex == k) {
return array[pivotIndex];
}
// If the pivot is less, then the Kth smallest element is in the right subgroup.
if (pivotIndex < k) {
return SmallestK(array, pivotIndex + 1, endIndex, k, comparer);
}
// If the pivot is greater, then the Kth smallest element is in the left subgroup.
return SmallestK<T>(array, startIndex, pivotIndex - 1, k, comparer);
}
/// <summary>
/// Partition the array into two groups based on the pivot. All values smaller than the pivot will be moved to the left, and all values greater will be moved
/// to the right. This is similar to the QuickSort algorithm.
/// </summary>
/// <param name="array">The array that should be sorted.</param>
/// <param name="startIndex">The starting index of the array.</param>
/// <param name="endIndex">The ending index of the array.</param>
/// <param name="k">The nth smallest value to retrieve. 0 indicates the smallest element, endIndex - 1 indicates the largest.</param>
/// <param name="comparer">The IComparer used to compare the array.</param>
/// <returns>The position of the pivot.</returns>
private static int Partition<T>(T[] array, int startIndex, int endIndex, IComparer<T> comparer)
{
var pivotIndex = UnityEngine.Random.Range(startIndex, endIndex + 1);
// The pivot has not been reordered yet. Move all elements that are less than the pivot to the left, and move all elements that are greater to the right.
var pivotValue = array[pivotIndex];
// The pivot should be moved to the end so it won't be compared against itself.
Swap(array, pivotIndex, endIndex);
var index = startIndex;
for (int i = startIndex; i < endIndex; ++i) {
if (comparer.Compare(array[i], pivotValue) < 0) {
Swap(array, index, i);
index++;
}
}
// Ensure the pivot is on the right of the smaller values.
Swap(array, index, endIndex);
return index;
}
/// <summary>
/// Swap the first and second elements.
/// </summary>
/// <param name="array">The array that should be sorted.</param>
/// <param name="firstIndex">The first index that should be swapped.</param>
/// <param name="secondIndex">The second index that should be swapped.</param>
private static void Swap<T>(T[] array, int firstIndex, int secondIndex)
{
var temp = array[firstIndex];
array[firstIndex] = array[secondIndex];
array[secondIndex] = temp;
}
}
}