- add: sort think
This commit is contained in:
10
.idea/.idea.InternalSortMethods/.idea/deploymentTargetSelector.xml
generated
Normal file
10
.idea/.idea.InternalSortMethods/.idea/deploymentTargetSelector.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="InternalSortMethods.Android">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,4 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise">
|
||||
<file url="file://$PROJECT_DIR$/InternalSortMethods/ViewModels/MainViewModel.cs" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/.idea.InternalSortMethods/.idea/libraries/androidx_annotation_annotation_jvm.xml
generated
Normal file
9
.idea/.idea.InternalSortMethods/.idea/libraries/androidx_annotation_annotation_jvm.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<component name="libraryTable">
|
||||
<library name="androidx.annotation.annotation-jvm">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.nuget/packages/xamarin.androidx.annotation.jvm/1.9.1.7/jar/androidx.annotation.annotation-jvm.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
13
.qwen/settings.json
Normal file
13
.qwen/settings.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(mkdir *)",
|
||||
"Bash(dotnet *)",
|
||||
"Bash(powershell *)",
|
||||
"Bash(del *)",
|
||||
"Bash(rmdir *)",
|
||||
"Bash(findstr *)"
|
||||
]
|
||||
},
|
||||
"$version": 3
|
||||
}
|
||||
7
.qwen/settings.json.orig
Normal file
7
.qwen/settings.json.orig
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(mkdir *)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:InternalSortMethods"
|
||||
x:Class="InternalSortMethods.App"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
RequestedThemeVariant="Dark">
|
||||
|
||||
<Application.DataTemplates>
|
||||
<local:ViewLocator/>
|
||||
@@ -11,5 +10,52 @@
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
|
||||
<!-- Liquid Glass стили встроены напрямую -->
|
||||
<Style Selector="Button.glass-btn">
|
||||
<Setter Property="Background">
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
|
||||
<GradientStop Color="#ffffff15" Offset="0.0"/>
|
||||
<GradientStop Color="#ffffff0a" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
</Setter>
|
||||
<Setter Property="Foreground" Value="#f0f0f5"/>
|
||||
<Setter Property="BorderBrush">
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
|
||||
<GradientStop Color="#ffffff25" Offset="0.0"/>
|
||||
<GradientStop Color="#ffffff0d" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
</Setter>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Padding" Value="16,10"/>
|
||||
<Style Selector="^:disabled">
|
||||
<Setter Property="Opacity" Value="0.4"/>
|
||||
</Style>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Button.accent-btn">
|
||||
<Setter Property="Background">
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
|
||||
<GradientStop Color="#7c5cfc" Offset="0.0"/>
|
||||
<GradientStop Color="#6d28d9" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
</Setter>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Padding" Value="16,10"/>
|
||||
<Style Selector="^:pointerover">
|
||||
<Setter Property="Opacity" Value="0.9"/>
|
||||
</Style>
|
||||
<Style Selector="^:pressed">
|
||||
<Setter Property="Opacity" Value="0.8"/>
|
||||
</Style>
|
||||
<Style Selector="^:disabled">
|
||||
<Setter Property="Opacity" Value="0.4"/>
|
||||
</Style>
|
||||
</Style>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
</Application>
|
||||
|
||||
36
InternalSortMethods/Converters/StateToColorConverter.cs
Normal file
36
InternalSortMethods/Converters/StateToColorConverter.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using InternalSortMethods.ViewModels;
|
||||
|
||||
namespace InternalSortMethods.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Конвертирует SortItemState в соответствующий цвет столбца.
|
||||
/// Используется в DataTemplate для визуализации.
|
||||
/// </summary>
|
||||
public class StateToColorConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is not SortItemState state)
|
||||
return Colors.Transparent;
|
||||
|
||||
return state switch
|
||||
{
|
||||
SortItemState.Default => Color.Parse("#818cf8"), // Индиго
|
||||
SortItemState.Comparing => Color.Parse("#fbbf24"), // Жёлтый
|
||||
SortItemState.Swapping => Color.Parse("#f87171"), // Красный
|
||||
SortItemState.Writing => Color.Parse("#22d3ee"), // Голубой
|
||||
SortItemState.Pivot => Color.Parse("#c084fc"), // Фиолетовый
|
||||
SortItemState.Sorted => Color.Parse("#34d399"), // Зелёный
|
||||
_ => Color.Parse("#818cf8")
|
||||
};
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
73
InternalSortMethods/Models/BubbleSort.cs
Normal file
73
InternalSortMethods/Models/BubbleSort.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using InternalSortMethods.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace InternalSortMethods.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Сортировка пузырьком (Bubble Sort).
|
||||
/// Последовательно сравнивает соседние элементы и меняет их, если они стоят в неверном порядке.
|
||||
/// Сложность: O(n²) по времени, O(1) по памяти.
|
||||
/// </summary>
|
||||
public sealed class BubbleSort : SortAlgorithm
|
||||
{
|
||||
public override string DisplayName => "Bubble Sort";
|
||||
|
||||
public override IEnumerable<SortStep> GenerateSteps(int[] array, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var arr = (int[])array.Clone();
|
||||
int n = arr.Length;
|
||||
|
||||
for (int i = 0; i < n - 1; i++)
|
||||
{
|
||||
bool swapped = false;
|
||||
|
||||
for (int j = 0; j < n - i - 1; j++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Шаг сравнения
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Compare,
|
||||
Indices = [j, j + 1],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
if (arr[j] > arr[j + 1])
|
||||
{
|
||||
// Перестановка
|
||||
(arr[j], arr[j + 1]) = (arr[j + 1], arr[j]);
|
||||
swapped = true;
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Swap,
|
||||
Indices = [j, j + 1],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Элемент n-i-1 встал на своё место
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Sorted,
|
||||
Indices = [n - i - 1],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
if (!swapped) break; // Массив отсортирован
|
||||
}
|
||||
|
||||
// Помечаем все элементы как отсортированные
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Sorted,
|
||||
Indices = Enumerable.Range(0, n).ToArray(),
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
}
|
||||
}
|
||||
121
InternalSortMethods/Models/HeapSort.cs
Normal file
121
InternalSortMethods/Models/HeapSort.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using InternalSortMethods.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace InternalSortMethods.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Пирамидальная сортировка (Heap Sort).
|
||||
/// Строит максимальную кучу, затем извлекает корень и восстанавливает кучу.
|
||||
/// Сложность: O(n log n) по времени, O(1) по памяти.
|
||||
/// </summary>
|
||||
public sealed class HeapSort : SortAlgorithm
|
||||
{
|
||||
public override string DisplayName => "Heap Sort";
|
||||
|
||||
public override IEnumerable<SortStep> GenerateSteps(int[] array, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var arr = (int[])array.Clone();
|
||||
int n = arr.Length;
|
||||
|
||||
// Построение кучи (heapify)
|
||||
for (int i = n / 2 - 1; i >= 0; i--)
|
||||
{
|
||||
foreach (var step in Heapify(arr, n, i, cancellationToken))
|
||||
yield return step;
|
||||
}
|
||||
|
||||
// Извлечение элементов из кучи
|
||||
for (int i = n - 1; i > 0; i--)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Перемещаем корень (максимум) в конец
|
||||
(arr[0], arr[i]) = (arr[i], arr[0]);
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Swap,
|
||||
Indices = [0, i],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Sorted,
|
||||
Indices = [i],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
// Восстановление кучи
|
||||
foreach (var step in Heapify(arr, i, 0, cancellationToken))
|
||||
yield return step;
|
||||
}
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Sorted,
|
||||
Indices = [0],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Восстанавливает свойство максимальной кучи для поддерева с корнем в index.
|
||||
/// </summary>
|
||||
private IEnumerable<SortStep> Heapify(int[] arr, int n, int index, CancellationToken ct)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
int largest = index;
|
||||
int left = 2 * index + 1;
|
||||
int right = 2 * index + 2;
|
||||
|
||||
// Сравнение с левым потомком
|
||||
if (left < n)
|
||||
{
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Compare,
|
||||
Indices = [largest, left],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
if (arr[left] > arr[largest])
|
||||
largest = left;
|
||||
}
|
||||
|
||||
// Сравнение с правым потомком
|
||||
if (right < n)
|
||||
{
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Compare,
|
||||
Indices = [largest, right],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
if (arr[right] > arr[largest])
|
||||
largest = right;
|
||||
}
|
||||
|
||||
// Перестановка, если корень не максимум
|
||||
if (largest != index)
|
||||
{
|
||||
(arr[index], arr[largest]) = (arr[largest], arr[index]);
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Swap,
|
||||
Indices = [index, largest],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
// Рекурсивный heapify для затронутого поддерева
|
||||
foreach (var step in Heapify(arr, n, largest, ct))
|
||||
yield return step;
|
||||
}
|
||||
}
|
||||
}
|
||||
88
InternalSortMethods/Models/InsertionSort.cs
Normal file
88
InternalSortMethods/Models/InsertionSort.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using InternalSortMethods.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace InternalSortMethods.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Сортировка вставками (Insertion Sort).
|
||||
/// Вставляет каждый элемент в уже отсортированную часть массива.
|
||||
/// Сложность: O(n²) по времени, O(1) по памяти.
|
||||
/// </summary>
|
||||
public sealed class InsertionSort : SortAlgorithm
|
||||
{
|
||||
public override string DisplayName => "Insertion Sort";
|
||||
|
||||
public override IEnumerable<SortStep> GenerateSteps(int[] array, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var arr = (int[])array.Clone();
|
||||
int n = arr.Length;
|
||||
|
||||
// Первый элемент считается отсортированным
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Sorted,
|
||||
Indices = [0],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
for (int i = 1; i < n; i++)
|
||||
{
|
||||
int key = arr[i];
|
||||
int j = i - 1;
|
||||
|
||||
// Подсвечиваем вставляемый элемент
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Pivot,
|
||||
Indices = [i],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
while (j >= 0 && arr[j] > key)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Сравнение
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Compare,
|
||||
Indices = [j, i],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
// Сдвиг элемента вправо
|
||||
arr[j + 1] = arr[j];
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Write,
|
||||
Indices = [j + 1],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
j--;
|
||||
}
|
||||
|
||||
// Вставка ключа на правильную позицию
|
||||
arr[j + 1] = key;
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Write,
|
||||
Indices = [j + 1],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
// Элементы до i включительно отсортированы
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Sorted,
|
||||
Indices = Enumerable.Range(0, i + 1).ToArray(),
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
140
InternalSortMethods/Models/MergeSort.cs
Normal file
140
InternalSortMethods/Models/MergeSort.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using InternalSortMethods.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace InternalSortMethods.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Сортировка слиянием (Merge Sort).
|
||||
/// Рекурсивно делит массив пополам, сортирует части и сливает их.
|
||||
/// Сложность: O(n log n) по времени, O(n) по памяти.
|
||||
/// </summary>
|
||||
public sealed class MergeSort : SortAlgorithm
|
||||
{
|
||||
public override string DisplayName => "Merge Sort";
|
||||
|
||||
public override IEnumerable<SortStep> GenerateSteps(int[] array, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var arr = (int[])array.Clone();
|
||||
var steps = MergeSortRecursive(arr, 0, arr.Length - 1, cancellationToken).ToList();
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Sorted,
|
||||
Indices = Enumerable.Range(0, arr.Length).ToArray(),
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<SortStep> MergeSortRecursive(int[] arr, int left, int right, CancellationToken ct)
|
||||
{
|
||||
if (left >= right)
|
||||
{
|
||||
if (left == right)
|
||||
{
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Sorted,
|
||||
Indices = [left],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
int mid = left + (right - left) / 2;
|
||||
|
||||
foreach (var step in MergeSortRecursive(arr, left, mid, ct))
|
||||
yield return step;
|
||||
|
||||
foreach (var step in MergeSortRecursive(arr, mid + 1, right, ct))
|
||||
yield return step;
|
||||
|
||||
foreach (var step in Merge(arr, left, mid, right, ct))
|
||||
yield return step;
|
||||
}
|
||||
|
||||
private IEnumerable<SortStep> Merge(int[] arr, int left, int mid, int right, CancellationToken ct)
|
||||
{
|
||||
int n1 = mid - left + 1;
|
||||
int n2 = right - mid;
|
||||
|
||||
var L = new int[n1];
|
||||
var R = new int[n2];
|
||||
|
||||
Array.Copy(arr, left, L, 0, n1);
|
||||
Array.Copy(arr, mid + 1, R, 0, n2);
|
||||
|
||||
int i = 0, j = 0, k = left;
|
||||
|
||||
while (i < n1 && j < n2)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Compare,
|
||||
Indices = [left + i, mid + 1 + j],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
if (L[i] <= R[j])
|
||||
{
|
||||
arr[k] = L[i];
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
arr[k] = R[j];
|
||||
j++;
|
||||
}
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Write,
|
||||
Indices = [k],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
k++;
|
||||
}
|
||||
|
||||
while (i < n1)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
arr[k] = L[i];
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Write,
|
||||
Indices = [k],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
i++;
|
||||
k++;
|
||||
}
|
||||
|
||||
while (j < n2)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
arr[k] = R[j];
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Write,
|
||||
Indices = [k],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
j++;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
154
InternalSortMethods/Models/QuickSort.cs
Normal file
154
InternalSortMethods/Models/QuickSort.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
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];
|
||||
}
|
||||
}
|
||||
83
InternalSortMethods/Models/SelectionSort.cs
Normal file
83
InternalSortMethods/Models/SelectionSort.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using InternalSortMethods.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace InternalSortMethods.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Сортировка выбором (Selection Sort).
|
||||
/// Находит минимальный элемент в неотсортированной части и ставит его на позицию.
|
||||
/// Сложность: O(n²) по времени, O(1) по памяти.
|
||||
/// </summary>
|
||||
public sealed class SelectionSort : SortAlgorithm
|
||||
{
|
||||
public override string DisplayName => "Selection Sort";
|
||||
|
||||
public override IEnumerable<SortStep> GenerateSteps(int[] array, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var arr = (int[])array.Clone();
|
||||
int n = arr.Length;
|
||||
|
||||
for (int i = 0; i < n - 1; i++)
|
||||
{
|
||||
int minIdx = i;
|
||||
|
||||
// Подсвечиваем текущий минимум
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Pivot,
|
||||
Indices = [minIdx],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
for (int j = i + 1; j < n; j++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Сравнение с текущим минимумом
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Compare,
|
||||
Indices = [minIdx, j],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
|
||||
if (arr[j] < arr[minIdx])
|
||||
{
|
||||
minIdx = j;
|
||||
}
|
||||
}
|
||||
|
||||
// Перестановка минимума на позицию i
|
||||
if (minIdx != i)
|
||||
{
|
||||
(arr[i], arr[minIdx]) = (arr[minIdx], arr[i]);
|
||||
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Swap,
|
||||
Indices = [i, minIdx],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
}
|
||||
|
||||
// Элемент i встал на своё место
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Sorted,
|
||||
Indices = [i],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
}
|
||||
|
||||
// Последний элемент автоматически отсортирован
|
||||
yield return new SortStep
|
||||
{
|
||||
Type = SortStepType.Sorted,
|
||||
Indices = [n - 1],
|
||||
ArrayState = (int[])arr.Clone()
|
||||
};
|
||||
}
|
||||
}
|
||||
27
InternalSortMethods/Models/SortAlgorithm.cs
Normal file
27
InternalSortMethods/Models/SortAlgorithm.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using InternalSortMethods.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace InternalSortMethods.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Базовый класс для всех алгоритмов сортировки.
|
||||
/// Генерирует последовательность шагов (SortStep), которые ViewModel выполняет
|
||||
/// с задержкой для анимации. Это позволяет реализовать паузу, шаг вперёд и отмену
|
||||
/// без блокировки UI-потока.
|
||||
/// </summary>
|
||||
public abstract class SortAlgorithm
|
||||
{
|
||||
/// <summary>Отображаемое название алгоритма.</summary>
|
||||
public abstract string DisplayName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Генерирует шаги сортировки для заданного массива.
|
||||
/// Каждый шаг содержит копию состояния массива после операции,
|
||||
/// что позволяет визуализировать процесс пошагово.
|
||||
/// </summary>
|
||||
/// <param name="array">Массив для сортировки.</param>
|
||||
/// <param name="cancellationToken">Токен отмены для прерывания генерации шагов.</param>
|
||||
/// <returns>Последовательность шагов сортировки.</returns>
|
||||
public abstract IEnumerable<SortStep> GenerateSteps(int[] array, CancellationToken cancellationToken = default);
|
||||
}
|
||||
43
InternalSortMethods/Models/SortStep.cs
Normal file
43
InternalSortMethods/Models/SortStep.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
namespace InternalSortMethods.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Представляет один шаг выполнения алгоритма сортировки.
|
||||
/// Используется для пошаговой визуализации с анимацией.
|
||||
/// </summary>
|
||||
public sealed class SortStep
|
||||
{
|
||||
/// <summary>Тип операции шага.</summary>
|
||||
public SortStepType Type { get; init; }
|
||||
|
||||
/// <summary>Индексы элементов, участвующих в операции (сравнение или перестановка).</summary>
|
||||
public int[] Indices { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Состояние массива после выполнения данного шага.
|
||||
/// Копия создаётся для того, чтобы UI мог отобразить актуальное состояние.
|
||||
/// </summary>
|
||||
public int[] ArrayState { get; init; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Типы операций шага сортировки для визуальной подсветки.
|
||||
/// </summary>
|
||||
public enum SortStepType
|
||||
{
|
||||
/// <summary>Сравнение двух элементов (подсвечиваем жёлтым).</summary>
|
||||
Compare,
|
||||
|
||||
/// <summary>Перестановка двух элементов (подсвечиваем красным).</summary>
|
||||
Swap,
|
||||
|
||||
/// <summary>Запись/вставка элемента на позицию (подсвечиваем голубым).</summary>
|
||||
Write,
|
||||
|
||||
/// <summary>Элемент определён как опорный (pivot) — для QuickSort.</summary>
|
||||
Pivot,
|
||||
|
||||
/// <summary>Элемент окончательно стоит на своём месте.</summary>
|
||||
Sorted
|
||||
}
|
||||
@@ -1,8 +1,383 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using InternalSortMethods.Models;
|
||||
|
||||
namespace InternalSortMethods.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Ãëàâíàÿ ViewModel ïðèëîæåíèÿ.
|
||||
///
|
||||
/// Ðàçäåëåíèå îòâåòñòâåííîñòè ïî MVVM:
|
||||
/// - ViewModel ÍÅ ñîäåðæèò ññûëîê íà UI-ýëåìåíòû (Button, ListBox è ò.ä.)
|
||||
/// - Âñå äàííûå äëÿ îòîáðàæåíèÿ (Items, IsRunning è ò.ä.) — ýòî ñâîéñòâà ñ INotifyPropertyChanged
|
||||
/// - Âñå äåéñòâèÿ ïîëüçîâàòåëÿ îáðàáàòûâàþòñÿ ÷åðåç RelayCommand
|
||||
/// - Àëãîðèòìû ñîðòèðîâêè íàõîäÿòñÿ â îòäåëüíûõ Model-êëàññàõ
|
||||
///
|
||||
/// Àñèíõðîííîñòü áåç Thread.Sleep:
|
||||
/// - Èñïîëüçóåòñÿ await Task.Delay(ms, _cancellationToken) — ýòî ÍÅ áëîêèðóåò UI-ïîòîê
|
||||
/// - CancellationToken ïîçâîëÿåò ìãíîâåííî îòìåíèòü âûïîëíåíèå
|
||||
/// - SemaphoreSlim èñïîëüçóåòñÿ äëÿ ïàóçû/ïðîäîëæåíèÿ
|
||||
/// </summary>
|
||||
public partial class MainViewModel : ViewModelBase
|
||||
{
|
||||
[ObservableProperty] private string _greeting = "Welcome to Avalonia!";
|
||||
}
|
||||
// ===== Êîíñòàíòû =====
|
||||
private const int MinArraySize = 5;
|
||||
private const int MaxArraySize = 100;
|
||||
private const int DefaultValue = 20;
|
||||
|
||||
// ===== Äîñòóïíûå àëãîðèòìû =====
|
||||
private static readonly SortAlgorithm[] Algorithms =
|
||||
[
|
||||
new BubbleSort(),
|
||||
new SelectionSort(),
|
||||
new InsertionSort(),
|
||||
new QuickSort(),
|
||||
new MergeSort(),
|
||||
new HeapSort()
|
||||
];
|
||||
|
||||
// ===== Ïîëÿ =====
|
||||
private readonly SemaphoreSlim _pauseSemaphore = new(1, 1);
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
private int[] _currentArray = [];
|
||||
private int _comparisons;
|
||||
private int _swaps;
|
||||
|
||||
// Äëÿ ïîøàãîâîãî âûïîëíåíèÿ
|
||||
private System.Collections.Generic.IEnumerator<SortStep>? _stepEnumerator;
|
||||
private System.Collections.Generic.List<SortStep>? _cachedSteps;
|
||||
private int _currentStepIndex;
|
||||
|
||||
// ===== ObservableProperty (CommunityToolkit ãåíåðèðóåò INPC) =====
|
||||
|
||||
[ObservableProperty] private ObservableCollection<SortItemViewModel> _items = [];
|
||||
[ObservableProperty] private int _arraySize = DefaultValue;
|
||||
[ObservableProperty] private int _selectedAlgorithmIndex;
|
||||
[ObservableProperty] private int _animationSpeed = 100;
|
||||
[ObservableProperty] private bool _isRunning;
|
||||
[ObservableProperty] private bool _isCompleted;
|
||||
[ObservableProperty] private string _statusText = "Ãîòîâî";
|
||||
[ObservableProperty] private int _currentStep;
|
||||
[ObservableProperty] private int _totalSteps;
|
||||
[ObservableProperty] private int _comparisonsCount;
|
||||
[ObservableProperty] private int _swapsCount;
|
||||
[ObservableProperty] private bool _isPaused;
|
||||
|
||||
// ===== Âû÷èñëÿåìûå ñâîéñòâà =====
|
||||
|
||||
/// <summary>Ñïèñîê íàçâàíèé àëãîðèòìîâ äëÿ ComboBox.</summary>
|
||||
public string[] AlgorithmNames => Algorithms.Select(a => a.DisplayName).ToArray();
|
||||
|
||||
/// <summary>Ìîæíî ëè çàïóñòèòü ñîðòèðîâêó.</summary>
|
||||
public bool CanStart => !IsRunning && Items.Count > 0;
|
||||
|
||||
/// <summary>Ìîæíî ëè ïîñòàâèòü íà ïàóçó/ïðîäîëæèòü.</summary>
|
||||
public bool CanPause => IsRunning && !IsCompleted;
|
||||
|
||||
/// <summary>Ìîæíî ëè ñäåëàòü øàã âïåð¸ä (ðàáîòàåò òîëüêî íà ïàóçå).</summary>
|
||||
public bool CanStepForward => IsRunning && IsPaused && !IsCompleted;
|
||||
|
||||
/// <summary>Ìîæíî ëè ñáðîñèòü.</summary>
|
||||
public bool CanReset => IsRunning || IsCompleted || Items.Count > 0;
|
||||
|
||||
// ===== Êîìàíäû =====
|
||||
|
||||
/// <summary>
|
||||
/// Ãåíåðàöèÿ íîâîãî ñëó÷àéíîãî ìàññèâà.
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private void GenerateArray()
|
||||
{
|
||||
if (IsRunning) return;
|
||||
|
||||
ArraySize = Math.Clamp(ArraySize, MinArraySize, MaxArraySize);
|
||||
ResetState();
|
||||
|
||||
var random = new Random();
|
||||
_currentArray = Enumerable.Range(0, ArraySize)
|
||||
.Select(_ => random.Next(1, 100))
|
||||
.ToArray();
|
||||
|
||||
UpdateItems(_currentArray);
|
||||
StatusText = $"Ñãåíåðèðîâàí ìàññèâ èç {ArraySize} ýëåìåíòîâ";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Çàïóñê ñîðòèðîâêè.
|
||||
///
|
||||
/// Àñèíõðîííîñòü áåç Thread.Sleep:
|
||||
/// - Task.Delay ÍÅ áëîêèðóåò UI-ïîòîê (èñïîëüçóåòñÿ continuation)
|
||||
/// - CancellationToken ïîçâîëÿåò îòìåíèòü çàäåðæêó
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private async Task StartSortAsync()
|
||||
{
|
||||
if (IsRunning || Items.Count == 0) return;
|
||||
|
||||
var algorithm = Algorithms[SelectedAlgorithmIndex];
|
||||
ResetState();
|
||||
IsRunning = true;
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
var token = _cancellationTokenSource.Token;
|
||||
|
||||
try
|
||||
{
|
||||
// Ãåíåðèðóåì ÂÑÅ øàãè çàðàíåå äëÿ ïîäñ÷¸òà è êýøèðîâàíèÿ
|
||||
_cachedSteps = algorithm.GenerateSteps(_currentArray, token).ToList();
|
||||
TotalSteps = _cachedSteps.Count;
|
||||
_stepEnumerator = _cachedSteps.GetEnumerator();
|
||||
_currentStepIndex = 0;
|
||||
|
||||
StatusText = $"Ñîðòèðîâêà: {algorithm.DisplayName}...";
|
||||
|
||||
// Àñèíõðîííîå âûïîëíåíèå øàãîâ ñ àíèìàöèåé
|
||||
foreach (var step in _cachedSteps)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
// Æä¸ì ðàçðåøåíèÿ íà ïðîäîëæåíèå (ïàóçà)
|
||||
await _pauseSemaphore.WaitAsync(token);
|
||||
_pauseSemaphore.Release();
|
||||
|
||||
// Åñëè íà ïàóçå — æä¸ì ñíÿòèÿ
|
||||
if (IsPaused)
|
||||
{
|
||||
await WaitUntilResumed(token);
|
||||
}
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
ApplyStep(step);
|
||||
_currentStepIndex++;
|
||||
CurrentStep = _currentStepIndex;
|
||||
|
||||
// Çàäåðæêà äëÿ àíèìàöèè — ÍÅ áëîêèðóåò UI-ïîòîê!
|
||||
// Task.Delay èñïîëüçóåò ñèñòåìíûé òàéìåð, à íå Thread.Sleep
|
||||
await Task.Delay(AnimationSpeed, token);
|
||||
}
|
||||
|
||||
IsCompleted = true;
|
||||
StatusText = $"Ñîðòèðîâêà çàâåðøåíà! Ñðàâíåíèé: {ComparisonsCount}, ïåðåñòàíîâîê: {SwapsCount}";
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
StatusText = "Ñîðòèðîâêà îòìåíåíà";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusText = $"Îøèáêà: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ïàóçà èëè ïðîäîëæåíèå âûïîëíåíèÿ.
|
||||
///
|
||||
/// Êàê ðàáîòàåò ïàóçà:
|
||||
/// - SemaphoreSlim âûñòóïàåò êàê «âîðîòà».
|
||||
/// - Ïðè ïàóçå ìû «çàáèðàåì» permit (Wait), è öèêë ñîðòèðîâêè áëîêèðóåòñÿ íà WaitAsync.
|
||||
/// - Ïðè ïðîäîëæåíèè ìû «âîçâðàùàåì» permit (Release), è öèêë ïðîäîëæàåò ðàáîòó.
|
||||
/// - Äîïîëíèòåëüíî ôëàã IsPaused àêòèâèðóåò îæèäàíèå ÷åðåç WaitUntilResumed.
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private void PauseResume()
|
||||
{
|
||||
if (!IsRunning || IsCompleted) return;
|
||||
|
||||
IsPaused = !IsPaused;
|
||||
|
||||
if (IsPaused)
|
||||
{
|
||||
// Çàáèðàåì permit — ñëåäóþùèé WaitAsync çàáëîêèðóåòñÿ
|
||||
_pauseSemaphore.Wait();
|
||||
StatusText = "Ïàóçà";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Âîçâðàùàåì permit — ðàçáëîêèðóåì WaitAsync
|
||||
_pauseSemaphore.Release();
|
||||
StatusText = "Ïðîäîëæåíèå...";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Øàã âïåð¸ä — âûïîëíÿåò îäèí øàã ñîðòèðîâêè.
|
||||
/// Ðàáîòàåò òîëüêî êîãäà ñîðòèðîâêà íà ïàóçå.
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private async Task StepForwardAsync()
|
||||
{
|
||||
if (!IsRunning || !IsPaused || IsCompleted) return;
|
||||
|
||||
if (_currentStepIndex < TotalSteps)
|
||||
{
|
||||
// Âðåìåííî ñíèìàåì ñ ïàóçû äëÿ îäíîãî øàãà
|
||||
IsPaused = false;
|
||||
_pauseSemaphore.Release();
|
||||
|
||||
// Ïðèìåíÿåì ñëåäóþùèé øàã
|
||||
var step = _cachedSteps![_currentStepIndex];
|
||||
ApplyStep(step);
|
||||
_currentStepIndex++;
|
||||
CurrentStep = _currentStepIndex;
|
||||
|
||||
await Task.Delay(50); // Ìèíèìàëüíàÿ çàäåðæêà äëÿ îáíîâëåíèÿ UI
|
||||
|
||||
// Ñíîâà ñòàâèì íà ïàóçó
|
||||
IsPaused = true;
|
||||
_pauseSemaphore.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ñáðîñ — îòìåíÿåò âûïîëíåíèå è âîçâðàùàåò èñõîäíîå ñîñòîÿíèå.
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private void Reset()
|
||||
{
|
||||
CancelRunningOperation();
|
||||
ResetState();
|
||||
UpdateItems(_currentArray);
|
||||
StatusText = "Ãîòîâî";
|
||||
}
|
||||
|
||||
// ===== Âñïîìîãàòåëüíûå ìåòîäû =====
|
||||
|
||||
/// <summary>
|
||||
/// Àñèíõðîííîå îæèäàíèå ñíÿòèÿ ïàóçû.
|
||||
/// Ïðîâåðÿåì êàæäûå 50ìñ, íå áûëà ëè îòìåíåíà îïåðàöèÿ.
|
||||
/// Ýòî ÍÅ áëîêèðóåò UI-ïîòîê áëàãîäàðÿ await Task.Delay.
|
||||
/// </summary>
|
||||
private async Task WaitUntilResumed(CancellationToken token)
|
||||
{
|
||||
while (IsPaused)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
await Task.Delay(50, token);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ïðèìåíÿåò îäèí øàã ñîðòèðîâêè ê Items è îáíîâëÿåò ñòàòèñòèêó.
|
||||
/// </summary>
|
||||
private void ApplyStep(SortStep step)
|
||||
{
|
||||
// Ñáðàñûâàåì ñîñòîÿíèÿ âñåõ ýëåìåíòîâ
|
||||
foreach (var item in Items)
|
||||
{
|
||||
item.State = SortItemState.Default;
|
||||
}
|
||||
|
||||
// Ïîäñâå÷èâàåì ó÷àñòâóþùèå ýëåìåíòû
|
||||
foreach (var idx in step.Indices)
|
||||
{
|
||||
if (idx >= 0 && idx < Items.Count)
|
||||
{
|
||||
Items[idx].State = step.Type switch
|
||||
{
|
||||
SortStepType.Compare => SortItemState.Comparing,
|
||||
SortStepType.Swap => SortItemState.Swapping,
|
||||
SortStepType.Write => SortItemState.Writing,
|
||||
SortStepType.Pivot => SortItemState.Pivot,
|
||||
SortStepType.Sorted => SortItemState.Sorted,
|
||||
_ => SortItemState.Default
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Îáíîâëÿåì çíà÷åíèÿ ýëåìåíòîâ (åñëè ìàññèâ èçìåíèëñÿ)
|
||||
for (int i = 0; i < Math.Min(step.ArrayState.Length, Items.Count); i++)
|
||||
{
|
||||
Items[i].Value = step.ArrayState[i];
|
||||
}
|
||||
|
||||
// Îáíîâëÿåì ñòàòèñòèêó
|
||||
if (step.Type == SortStepType.Compare)
|
||||
{
|
||||
ComparisonsCount++;
|
||||
_comparisons++;
|
||||
}
|
||||
|
||||
if (step.Type == SortStepType.Swap)
|
||||
{
|
||||
SwapsCount++;
|
||||
_swaps++;
|
||||
}
|
||||
|
||||
// Îáíîâëÿåì òåêóùèé ìàññèâ
|
||||
_currentArray = (int[])step.ArrayState.Clone();
|
||||
}
|
||||
|
||||
private void UpdateItems(int[] array)
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
foreach (var value in array)
|
||||
{
|
||||
Items.Add(new SortItemViewModel(value));
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetState()
|
||||
{
|
||||
IsRunning = false;
|
||||
IsCompleted = false;
|
||||
IsPaused = false;
|
||||
CurrentStep = 0;
|
||||
TotalSteps = 0;
|
||||
ComparisonsCount = 0;
|
||||
SwapsCount = 0;
|
||||
_comparisons = 0;
|
||||
_swaps = 0;
|
||||
_stepEnumerator = null;
|
||||
_cachedSteps = null;
|
||||
_currentStepIndex = 0;
|
||||
|
||||
// Ñáðîñ ñîñòîÿíèé ýëåìåíòîâ
|
||||
foreach (var item in Items)
|
||||
{
|
||||
item.State = SortItemState.Default;
|
||||
}
|
||||
|
||||
// Ñáðîñ ñåìàôîðà
|
||||
if (_pauseSemaphore.CurrentCount == 0)
|
||||
{
|
||||
_pauseSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelRunningOperation()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
|
||||
// Îñâîáîæäàåì ñåìàôîð, åñëè îïåðàöèÿ íà ïàóçå
|
||||
if (IsPaused)
|
||||
{
|
||||
IsPaused = false;
|
||||
if (_pauseSemaphore.CurrentCount == 0)
|
||||
{
|
||||
_pauseSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== ×àñòè÷íûå ìåòîäû äëÿ ObservableProperty =====
|
||||
partial void OnArraySizeChanged(int value)
|
||||
{
|
||||
if (!IsRunning)
|
||||
{
|
||||
GenerateArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
62
InternalSortMethods/ViewModels/SortItemViewModel.cs
Normal file
62
InternalSortMethods/ViewModels/SortItemViewModel.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace InternalSortMethods.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel для одного элемента массива в визуализации.
|
||||
/// Содержит значение и текущее состояние подсветки (цвет).
|
||||
/// </summary>
|
||||
public partial class SortItemViewModel : ObservableObject
|
||||
{
|
||||
private int _value;
|
||||
private SortItemState _state = SortItemState.Default;
|
||||
|
||||
public SortItemViewModel(int value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
/// <summary>Значение элемента (высота столбца).</summary>
|
||||
public int Value
|
||||
{
|
||||
get => _value;
|
||||
set => SetProperty(ref _value, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Текущее состояние элемента для визуальной подсветки.
|
||||
/// Изменяется при каждом шаге сортировки.
|
||||
/// </summary>
|
||||
public SortItemState State
|
||||
{
|
||||
get => _state;
|
||||
set => SetProperty(ref _state, value);
|
||||
}
|
||||
|
||||
/// <summary>Относительная высота для привязки к UI (0.0 – 1.0).</summary>
|
||||
public double NormalizedHeight { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Состояния элемента для цветовой подсветки.
|
||||
/// </summary>
|
||||
public enum SortItemState
|
||||
{
|
||||
/// <summary>Обычное состояние (нейтральный цвет).</summary>
|
||||
Default,
|
||||
|
||||
/// <summary>Элемент сравнивается с другим (жёлтый/оранжевый).</summary>
|
||||
Comparing,
|
||||
|
||||
/// <summary>Элемент участвует в перестановке (красный).</summary>
|
||||
Swapping,
|
||||
|
||||
/// <summary>Элемент записан/вставлен (синий/голубой).</summary>
|
||||
Writing,
|
||||
|
||||
/// <summary>Опорный элемент — pivot для QuickSort (фиолетовый).</summary>
|
||||
Pivot,
|
||||
|
||||
/// <summary>Элемент окончательно отсортирован (зелёный).</summary>
|
||||
Sorted
|
||||
}
|
||||
@@ -3,14 +3,238 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:InternalSortMethods.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
xmlns:converters="using:InternalSortMethods.Converters"
|
||||
x:Class="InternalSortMethods.Views.MainView"
|
||||
x:DataType="vm:MainViewModel">
|
||||
x:DataType="vm:MainViewModel"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800"
|
||||
d:DesignHeight="700">
|
||||
|
||||
<Design.DataContext>
|
||||
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||
<vm:MainViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<!-- ===== Конвертеры ===== -->
|
||||
<UserControl.Resources>
|
||||
<converters:StateToColorConverter x:Key="StateToColor"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<!-- Основной фон (градиент) -->
|
||||
<Panel>
|
||||
<Panel.Background>
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
|
||||
<GradientStop Color="#0f0c29" Offset="0.0"/>
|
||||
<GradientStop Color="#302b63" Offset="0.5"/>
|
||||
<GradientStop Color="#24243e" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
</Panel.Background>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<Grid RowDefinitions="Auto,*,Auto" Margin="16">
|
||||
|
||||
<!-- ===== Заголовок ===== -->
|
||||
<StackPanel Grid.Row="0" Spacing="4" Margin="0,0,0,16">
|
||||
<TextBlock Text="Методы внутренней сортировки"
|
||||
FontSize="24" FontWeight="Bold"
|
||||
Foreground="#f0f0f5"
|
||||
HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="Визуализация алгоритмов сортировки"
|
||||
FontSize="13"
|
||||
Foreground="#a0a0b0"
|
||||
HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- ===== Область визуализации ===== -->
|
||||
<Border Grid.Row="1" Margin="0,0,0,16" MinHeight="250"
|
||||
CornerRadius="16" Padding="16"
|
||||
BoxShadow="0 8 32 0 #00000050">
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
|
||||
<GradientStop Color="#121212" Offset="0.0"/>
|
||||
<GradientStop Color="#131313" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
|
||||
<DockPanel>
|
||||
<!-- Статус и прогресс -->
|
||||
<StackPanel DockPanel.Dock="Top" Spacing="8" Margin="0,0,0,12">
|
||||
<ProgressBar Value="{Binding CurrentStep}"
|
||||
Maximum="{Binding TotalSteps}"
|
||||
Height="8" CornerRadius="4">
|
||||
<ProgressBar.Background>
|
||||
<SolidColorBrush Color="#ffffff15"/>
|
||||
</ProgressBar.Background>
|
||||
<ProgressBar.Foreground>
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,0%">
|
||||
<GradientStop Color="#7c5cfc" Offset="0.0"/>
|
||||
<GradientStop Color="#6d28d9" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
</ProgressBar.Foreground>
|
||||
</ProgressBar>
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto,Auto">
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding StatusText}"
|
||||
FontSize="13" Foreground="#a0a0b0"
|
||||
VerticalAlignment="Center"/>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="12"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock FontSize="12" Foreground="#fbbf24"
|
||||
Text="{Binding ComparisonsCount, StringFormat='⚡ {0}'}"/>
|
||||
<TextBlock FontSize="12" Foreground="#f87171"
|
||||
Text="{Binding SwapsCount, StringFormat='⇄ {0}'}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="2" FontSize="12" Foreground="#818cf8"
|
||||
VerticalAlignment="Center" Margin="12,0,0,0">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="Шаг {0}/{1}">
|
||||
<Binding Path="CurrentStep"/>
|
||||
<Binding Path="TotalSteps"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Столбчатая диаграмма -->
|
||||
<Viewbox Stretch="Uniform" StretchDirection="Both"
|
||||
VerticalAlignment="Bottom">
|
||||
<ItemsControl ItemsSource="{Binding Items}"
|
||||
MinHeight="200" MaxHeight="400">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
Spacing="1"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:SortItemViewModel">
|
||||
<!-- Столбец: высота = Value, цвет = по State -->
|
||||
<Border Width="14"
|
||||
Height="{Binding Value}"
|
||||
VerticalAlignment="Bottom"
|
||||
CornerRadius="3,3,0,0"
|
||||
BoxShadow="0 2 4 0 #00000040">
|
||||
<Border.Background>
|
||||
<SolidColorBrush Color="{Binding State, Converter={StaticResource StateToColor}}"/>
|
||||
</Border.Background>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Viewbox>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<!-- ===== Панель управления ===== -->
|
||||
<Border Grid.Row="2" CornerRadius="16" Padding="16"
|
||||
BoxShadow="0 8 32 0 #00000050">
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
|
||||
<GradientStop Color="#121212" Offset="0.0"/>
|
||||
<GradientStop Color="#131313" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
|
||||
<StackPanel Spacing="16">
|
||||
<!-- Алгоритм + Размер массива -->
|
||||
<Grid ColumnDefinitions="*,*" ColumnSpacing="16">
|
||||
<StackPanel Grid.Column="0" Spacing="6">
|
||||
<TextBlock Text="Алгоритм" FontSize="11" Foreground="#a0a0b0"
|
||||
FontWeight="SemiBold"/>
|
||||
<ComboBox ItemsSource="{Binding AlgorithmNames}"
|
||||
SelectedIndex="{Binding SelectedAlgorithmIndex}"
|
||||
CornerRadius="12" MinHeight="48" FontSize="14">
|
||||
<ComboBox.Background>
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
|
||||
<GradientStop Color="#121212" Offset="0.0"/>
|
||||
<GradientStop Color="#131313" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
</ComboBox.Background>
|
||||
<ComboBox.Foreground>
|
||||
<SolidColorBrush Color="#f0f0f5"/>
|
||||
</ComboBox.Foreground>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1" Spacing="6">
|
||||
<TextBlock Text="Размер массива" FontSize="11" Foreground="#a0a0b0"
|
||||
FontWeight="SemiBold"/>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<Slider Grid.Column="0"
|
||||
Value="{Binding ArraySize}"
|
||||
Minimum="5" Maximum="80"
|
||||
TickFrequency="1" IsSnapToTickEnabled="True"
|
||||
MinHeight="48"/>
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding ArraySize}"
|
||||
FontSize="16" FontWeight="Bold"
|
||||
Foreground="#f0f0f5"
|
||||
VerticalAlignment="Center"
|
||||
Margin="12,0,0,0" MinWidth="32"
|
||||
TextAlignment="Center"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Скорость -->
|
||||
<StackPanel Spacing="6">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||
<TextBlock Grid.Column="0" Text="Скорость"
|
||||
FontSize="11" Foreground="#a0a0b0"
|
||||
FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<Slider Grid.Column="1"
|
||||
Value="{Binding AnimationSpeed}"
|
||||
Minimum="1" Maximum="500"
|
||||
TickFrequency="10" IsSnapToTickEnabled="True"
|
||||
MinHeight="48"/>
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding AnimationSpeed, StringFormat={}{0} мс}"
|
||||
FontSize="13" Foreground="#a0a0b0"
|
||||
VerticalAlignment="Center" MinWidth="60"
|
||||
TextAlignment="Right" Margin="12,0,0,0"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Кнопки -->
|
||||
<Grid ColumnDefinitions="*,*,Auto,Auto,Auto" ColumnSpacing="8">
|
||||
<Button Grid.Column="0" Content="Генерация"
|
||||
Classes="glass-btn"
|
||||
Command="{Binding GenerateArrayCommand}"
|
||||
CornerRadius="12" MinHeight="48"
|
||||
HorizontalAlignment="Stretch"/>
|
||||
<Button Grid.Column="1" Content="▶ Старт"
|
||||
Classes="accent-btn"
|
||||
Command="{Binding StartSortCommand}"
|
||||
IsEnabled="{Binding CanStart}"
|
||||
CornerRadius="12" MinHeight="48"
|
||||
HorizontalAlignment="Stretch"/>
|
||||
<Button Grid.Column="2" Content="⏸"
|
||||
Classes="glass-btn"
|
||||
Command="{Binding PauseResumeCommand}"
|
||||
IsEnabled="{Binding CanPause}"
|
||||
CornerRadius="12" MinHeight="48" MinWidth="52"
|
||||
Padding="12,10" FontSize="18"/>
|
||||
<Button Grid.Column="3" Content="⏭"
|
||||
Classes="glass-btn"
|
||||
Command="{Binding StepForwardCommand}"
|
||||
IsEnabled="{Binding CanStepForward}"
|
||||
CornerRadius="12" MinHeight="48" MinWidth="52"
|
||||
Padding="12,10" FontSize="18"/>
|
||||
<Button Grid.Column="4" Content="↺"
|
||||
Classes="glass-btn"
|
||||
Command="{Binding ResetCommand}"
|
||||
IsEnabled="{Binding CanReset}"
|
||||
CornerRadius="12" MinHeight="48" MinWidth="52"
|
||||
Padding="12,10" FontSize="18"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Panel>
|
||||
</UserControl>
|
||||
|
||||
Reference in New Issue
Block a user