155 lines
4.8 KiB
C#
155 lines
4.8 KiB
C#
using InternalSortMethods.Models;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading;
|
||
|
||
namespace InternalSortMethods.Models;
|
||
|
||
/// <summary>
|
||
/// Быстрая сортировка (Quick Sort).
|
||
/// Разделяет массив по опорному элементу (pivot) и рекурсивно сортирует части.
|
||
/// Сложность: O(n log n) в среднем, O(n²) в худшем случае.
|
||
/// </summary>
|
||
public sealed class QuickSort : SortAlgorithm
|
||
{
|
||
public override string DisplayName => "Quick Sort";
|
||
|
||
public override IEnumerable<SortStep> GenerateSteps(int[] array, CancellationToken cancellationToken = default)
|
||
{
|
||
var arr = (int[])array.Clone();
|
||
var steps = QuickSortRecursive(arr, 0, arr.Length - 1, cancellationToken).ToList();
|
||
|
||
// Помечаем все элементы как отсортированные в конце
|
||
steps.Add(new SortStep
|
||
{
|
||
Type = SortStepType.Sorted,
|
||
Indices = Enumerable.Range(0, arr.Length).ToArray(),
|
||
ArrayState = (int[])arr.Clone()
|
||
});
|
||
|
||
return steps;
|
||
}
|
||
|
||
private IEnumerable<SortStep> QuickSortRecursive(int[] arr, int low, int high, CancellationToken ct)
|
||
{
|
||
if (low < high)
|
||
{
|
||
ct.ThrowIfCancellationRequested();
|
||
|
||
// Разбиение и получение опорного индекса
|
||
foreach (var step in PartitionSteps(arr, low, high, ct))
|
||
{
|
||
yield return step;
|
||
}
|
||
|
||
// Определяем позицию pivot после разбиения
|
||
int pivotIdx = FindPivotIndex(arr, low, high);
|
||
|
||
// Рекурсивная сортировка левой и правой частей
|
||
foreach (var step in QuickSortRecursive(arr, low, pivotIdx - 1, ct))
|
||
yield return step;
|
||
|
||
foreach (var step in QuickSortRecursive(arr, pivotIdx + 1, high, ct))
|
||
yield return step;
|
||
}
|
||
else if (low == high)
|
||
{
|
||
yield return new SortStep
|
||
{
|
||
Type = SortStepType.Sorted,
|
||
Indices = [low],
|
||
ArrayState = (int[])arr.Clone()
|
||
};
|
||
}
|
||
}
|
||
|
||
private int FindPivotIndex(int[] arr, int low, int high)
|
||
{
|
||
// После partition pivot стоит на правильном месте
|
||
// Ищем элемент, который уже на своём месте
|
||
for (int i = low; i <= high; i++)
|
||
{
|
||
bool isPivot = true;
|
||
for (int j = low; j < i; j++)
|
||
{
|
||
if (arr[j] > arr[i]) { isPivot = false; break; }
|
||
}
|
||
for (int j = i + 1; j <= high; j++)
|
||
{
|
||
if (arr[j] < arr[i]) { isPivot = false; break; }
|
||
}
|
||
if (isPivot) return i;
|
||
}
|
||
return low;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Генерирует шаги для операции разбиения (partition) с визуализацией.
|
||
/// </summary>
|
||
private IEnumerable<SortStep> PartitionSteps(int[] arr, int low, int high, CancellationToken ct)
|
||
{
|
||
var temp = (int[])arr.Clone();
|
||
int pivot = arr[high];
|
||
int i = low - 1;
|
||
|
||
yield return new SortStep
|
||
{
|
||
Type = SortStepType.Pivot,
|
||
Indices = [high],
|
||
ArrayState = (int[])temp.Clone()
|
||
};
|
||
|
||
for (int j = low; j < high; j++)
|
||
{
|
||
ct.ThrowIfCancellationRequested();
|
||
|
||
yield return new SortStep
|
||
{
|
||
Type = SortStepType.Compare,
|
||
Indices = [j, high],
|
||
ArrayState = (int[])temp.Clone()
|
||
};
|
||
|
||
if (temp[j] <= pivot)
|
||
{
|
||
i++;
|
||
if (i != j)
|
||
{
|
||
(temp[i], temp[j]) = (temp[j], temp[i]);
|
||
|
||
yield return new SortStep
|
||
{
|
||
Type = SortStepType.Swap,
|
||
Indices = [i, j],
|
||
ArrayState = (int[])temp.Clone()
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
if (i + 1 != high)
|
||
{
|
||
(temp[i + 1], temp[high]) = (temp[high], temp[i + 1]);
|
||
|
||
yield return new SortStep
|
||
{
|
||
Type = SortStepType.Swap,
|
||
Indices = [i + 1, high],
|
||
ArrayState = (int[])temp.Clone()
|
||
};
|
||
}
|
||
|
||
yield return new SortStep
|
||
{
|
||
Type = SortStepType.Sorted,
|
||
Indices = [i + 1],
|
||
ArrayState = (int[])temp.Clone()
|
||
};
|
||
|
||
// Копируем результат в оригинальный массив
|
||
for (int k = low; k <= high; k++)
|
||
arr[k] = temp[k];
|
||
}
|
||
}
|