Files
InternalSortMethods/InternalSortMethods/Models/QuickSort.cs
2026-04-12 19:15:09 +03:00

155 lines
4.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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];
}
}