This commit is contained in:
2026-03-03 21:55:57 +03:00
parent 000a587211
commit 9bca5735e8
9 changed files with 278 additions and 90 deletions
+21 -3
View File
@@ -15,7 +15,8 @@ FetchContent_Declare(
)
FetchContent_MakeAvailable(SFML)
add_executable(SortLab
if(WIN32)
add_executable(SortLab
src/main.cpp
src/App.cpp
src/App_audio.cpp
@@ -29,7 +30,25 @@ add_executable(SortLab
src/sorters/InsertionSorter.cpp
src/sorters/MergeSorter.cpp
src/sorters/QuickSorter.cpp
)
assets/app.rc
)
else()
add_executable(SortLab
src/main.cpp
src/App.cpp
src/App_audio.cpp
src/Array.cpp
src/Sorter.cpp
src/UI.cpp
src/OperationsHistory.cpp
src/ProgressMap.cpp
src/sorters/BubbleSorter.cpp
src/sorters/SelectionSorter.cpp
src/sorters/InsertionSorter.cpp
src/sorters/MergeSorter.cpp
src/sorters/QuickSorter.cpp
)
endif()
if(MSVC)
target_compile_options(SortLab PRIVATE /utf-8)
@@ -39,7 +58,6 @@ target_include_directories(SortLab PRIVATE ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(SortLab PRIVATE sfml-graphics sfml-window sfml-system sfml-audio)
if(WIN32)
add_custom_command(TARGET SortLab POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
+190
View File
@@ -0,0 +1,190 @@
# 🎯 SortLab
### ⚡ Интерактивный визуализатор алгоритмов сортировки
![C++](https://img.shields.io/badge/C++-17-00599C?style=flat&logo=cplusplus&logoColor=white)
![SFML](https://img.shields.io/badge/SFML-2.6-8CC445?style=flat&logo=sfml&logoColor=white)
![CMake](https://img.shields.io/badge/CMake-3.16+-064F8C?style=flat&logo=cmake&logoColor=white)
![Platform](https://img.shields.io/badge/Platform-Windows-0078D4?style=flat&logo=windows&logoColor=white)
![License](https://img.shields.io/badge/License-MIT-yellow?style=flat)
---
![SortLab Preview](assets/preview.png)
---
## 📖 О проекте
**SortLab** — учебный инструмент для наглядного изучения алгоритмов сортировки.
Каждый элемент массива отображается как столбик. Высота = значение. Цвет = состояние.
Программа показывает работу алгоритма в реальном времени, шаг за шагом, с подсветкой
сравниваемых и переставляемых элементов, счётчиком операций и визуализацией прогресса.
---
## 🧮 Алгоритмы
| # | Алгоритм | Время (лучшее / среднее / худшее) | Память |
|---|---|---|---|
| `1` | 🫧 Bubble Sort | O(n) / O(n²) / O(n²) | O(1) |
| `2` | 🔍 Selection Sort | O(n²) / O(n²) / O(n²) | O(1) |
| `3` | 🃏 Insertion Sort | O(n) / O(n²) / O(n²) | O(1) |
| `4` | 🔀 Merge Sort | O(n log n) / O(n log n) / O(n log n) | O(n) |
| `5` | ⚡ Quick Sort | O(n log n) / O(n log n) / O(n²) | O(log n) |
---
## 🎮 Управление
| Клавиша | Действие |
|---|---|
| `Space` | ▶️ Старт / ⏸️ Пауза |
| `R` | 🔀 Перемешать массив |
| `→` | 👣 Один шаг (в режиме паузы) |
| `↑` / `↓` | 🐇 / 🐢 Увеличить / уменьшить скорость |
| `1``5` | 🔢 Выбрать алгоритм |
| `I` | ℹ️ Справка о программе |
| `Q` | 🚪 Выход |
---
## 🖥️ Интерфейс
```
┌──────────────────────────────────────────────────────────────┐
│ 📊 Algorithm: Quick Sort Time: O(n log n) Comparisons: 1.2k │ ← UI панель
├────────────────────────────────────┬─────────────────────────┤
│ │ │
│ 📈 Столбики массива │ 🗺️ Progress Map │
│ │ │
├────────────────────────────────────┴─────────────────────────┤
│ 📉 Δ Comparisons / step [гистограмма] │ ← нижняя панель
└──────────────────────────────────────────────────────────────┘
```
- 📊 **Главная область** — столбики с цветовой индикацией состояний
- 🗺️ **Progress Map** — миниатюрная сетка всего массива, зеленеет по мере сортировки
- 📉 **Δ Гистограмма** — количество сравнений на каждом шаге (пульс алгоритма)
### 🎨 Цветовая схема
| Цвет | Состояние |
|---|---|
| 🔵 Синий | Обычный элемент |
| 🩵 Голубой | Сравниваемые элементы |
| 🟠 Оранжевый | Переставляемые элементы |
| 🟢 Зелёный | Отсортированный элемент |
---
## 🔧 Сборка
### 📦 Зависимости
- 🛠️ [CMake](https://cmake.org/download/) ≥ 3.16
- 💻 [Visual Studio 2022](https://visualstudio.microsoft.com/) с компонентом **Desktop development with C++**
- 🌐 Git (для FetchContent — SFML скачивается автоматически)
### 🚀 Установка
```bash
git clone https://github.com/your-username/SortLab.git
cd SortLab
cmake -S . -B build
cmake --build build --config Release
.\build\Release\SortLab.exe
```
> 💡 SFML 2.6 скачивается и компилируется автоматически при первой сборке.
> ⏱️ Первый запуск `cmake -S . -B build` займёт 1–3 минуты.
### 🔤 Шрифт
Скачай [JetBrains Mono](https://www.jetbrains.com/lp/mono/) и положи файл в:
```
assets/fonts/JetBrainsMono-Regular.ttf
```
---
## 📁 Структура проекта
```
SortLab/
├── 🎨 assets/
│ ├── fonts/JetBrainsMono-Regular.ttf
│ ├── icon.ico
│ ├── icon.png
│ ├── preview.png
│ └── app.rc
├── 📋 include/
│ ├── App.hpp
│ ├── Array.hpp
│ ├── Sorter.hpp
│ ├── UI.hpp
│ ├── OperationsHistory.hpp
│ ├── ProgressMap.hpp
│ └── sorters/
│ ├── BubbleSorter.hpp
│ ├── SelectionSorter.hpp
│ ├── InsertionSorter.hpp
│ ├── MergeSorter.hpp
│ └── QuickSorter.hpp
├── 💻 src/
│ ├── main.cpp
│ ├── App.cpp
│ ├── App_audio.cpp
│ ├── Array.cpp
│ ├── Sorter.cpp
│ ├── UI.cpp
│ ├── OperationsHistory.cpp
│ ├── ProgressMap.cpp
│ └── sorters/
│ ├── BubbleSorter.cpp
│ ├── SelectionSorter.cpp
│ ├── InsertionSorter.cpp
│ ├── MergeSorter.cpp
│ └── QuickSorter.cpp
└── ⚙️ CMakeLists.txt
```
---
## 🏗️ Архитектура
```
main()
└── App::run()
├── handleEvents() — ввод с клавиатуры
├── update(dt) — шаг сортировки + гистограмма
└── render()
├── Array bars — основная визуализация
├── UI overlay — текст, статистика
├── ProgressMap — мини-карта
└── HistogramRenderer — Δ гистограмма
Sorter (abstract)
├── BubbleSorter
├── SelectionSorter
├── InsertionSorter
├── MergeSorter
└── QuickSorter
```
> 🔑 Ключевой паттерн: все алгоритмы реализованы как **конечные автоматы** (state machines).
> Метод `step()` выполняет ровно одну атомарную операцию и возвращает управление,
> не блокируя главный поток рендеринга.
---
## 📜 Лицензия
```
MIT License — 2026
```
---
<div align="center">
<sub>🎓 made for college coursework &nbsp;|&nbsp; built with ❤️ and C++17</sub>
</div>
+1
View File
@@ -0,0 +1 @@
IDI_ICON1 ICON "icon.ico"
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

+5 -14
View File
@@ -5,30 +5,21 @@
class QuickSorter : public Sorter {
public:
QuickSorter();
void step(Array& array) override;
bool isFinished() const override;
std::string getName() const override;
void reset() override;
std::string getTimeComplexity() const override;
std::string getSpaceComplexity() const override;
void reset() override;
private:
struct Range { int low; int high; };
enum class Phase { PARTITIONING, SWAPPING_PIVOT, PUSHING_RANGES };
struct Range {
int low;
int high;
};
std::vector<Range> stack_;
int currentLow_;
int currentHigh_;
int pivotIndex_;
int i_;
int j_;
Phase phase_;
int currentLow_, currentHigh_, pivotIndex_, i_, j_, n_;
float pivotValue_;
bool finished_;
Phase phase_;
int n_;
bool needNewRange_;
};
+5
View File
@@ -28,6 +28,11 @@ App::App()
beepSound_.setBuffer(beepBuffer_);
beepSound_.setVolume(100.0f);
sf::Image icon;
if (icon.loadFromFile("assets/icon.png")) {
window_.setIcon(icon.getSize().x, icon.getSize().y, icon.getPixelsPtr());
}
if (bottomPanelFont_.loadFromFile("assets/fonts/JetBrainsMono-Regular.ttf")) {
bottomPanelFontLoaded_ = true;
}
+20 -37
View File
@@ -1,74 +1,61 @@
#include "sorters/QuickSorter.hpp"
QuickSorter::QuickSorter()
: currentLow_(0), currentHigh_(0), pivotIndex_(0), i_(0), j_(0)
, pivotValue_(0.0f), finished_(false), phase_(Phase::PARTITIONING), n_(0) {
QuickSorter::QuickSorter() {
reset();
}
void QuickSorter::step(Array& array) {
if (finished_) {
return;
}
if (finished_) return;
if (n_ == 0) {
n_ = array.getSize();
stack_.push_back({0, n_ - 1});
needNewRange_ = true;
}
if (needNewRange_) {
while (!stack_.empty() && stack_.back().low >= stack_.back().high) {
stack_.pop_back();
}
if (stack_.empty()) {
array.resetStates();
for (int k = 0; k < n_; ++k) {
for (int k = 0; k < n_; ++k)
array.setState(k, Array::State::SORTED);
}
finished_ = true;
return;
}
if (phase_ == Phase::PARTITIONING) {
if (j_ == currentLow_) {
Range range = stack_.back();
stack_.pop_back();
if (range.low >= range.high) {
if (stack_.empty()) {
array.resetStates();
for (int k = 0; k < n_; ++k) {
array.setState(k, Array::State::SORTED);
}
finished_ = true;
}
return;
}
currentLow_ = range.low;
currentHigh_ = range.high;
pivotIndex_ = currentHigh_;
pivotValue_ = array.getValue(pivotIndex_);
i_ = currentLow_ - 1;
j_ = currentLow_;
needNewRange_ = false;
phase_ = Phase::PARTITIONING;
return;
}
if (phase_ == Phase::PARTITIONING) {
if (j_ < currentHigh_) {
array.resetStates();
array.setState(j_, Array::State::COMPARE);
array.setState(pivotIndex_, Array::State::SWAP);
array.incrementComparisons();
if (array.getValue(j_) < pivotValue_) {
i_++;
if (i_ != j_) {
array.setState(i_, Array::State::SWAP);
float temp = array.getValue(i_);
array.setValue(i_, array.getValue(j_));
array.setValue(j_, temp);
array.incrementSwaps();
}
}
j_++;
} else {
phase_ = Phase::SWAPPING_PIVOT;
@@ -81,22 +68,17 @@ void QuickSorter::step(Array& array) {
float temp = array.getValue(i_ + 1);
array.setValue(i_ + 1, array.getValue(pivotIndex_));
array.setValue(pivotIndex_, temp);
array.incrementSwaps();
pivotIndex_ = i_ + 1;
pivotIndex_ = i_ + 1;
phase_ = Phase::PUSHING_RANGES;
} else if (phase_ == Phase::PUSHING_RANGES) {
if (pivotIndex_ - 1 > currentLow_) {
if (pivotIndex_ - 1 > currentLow_)
stack_.push_back({currentLow_, pivotIndex_ - 1});
}
if (pivotIndex_ + 1 < currentHigh_) {
if (pivotIndex_ + 1 < currentHigh_)
stack_.push_back({pivotIndex_ + 1, currentHigh_});
}
j_ = currentLow_;
phase_ = Phase::PARTITIONING;
needNewRange_ = true;
}
}
@@ -113,7 +95,7 @@ std::string QuickSorter::getTimeComplexity() const {
}
std::string QuickSorter::getSpaceComplexity() const {
return "O(n)";
return "O(log n)";
}
void QuickSorter::reset() {
@@ -125,6 +107,7 @@ void QuickSorter::reset() {
j_ = 0;
pivotValue_ = 0.0f;
finished_ = false;
needNewRange_ = true;
phase_ = Phase::PARTITIONING;
n_ = 0;
}