mirror of
https://github.com/koloideal/SortLab.git
synced 2026-06-10 10:25:30 +03:00
final
This commit is contained in:
+21
-3
@@ -15,7 +15,8 @@ FetchContent_Declare(
|
|||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(SFML)
|
FetchContent_MakeAvailable(SFML)
|
||||||
|
|
||||||
add_executable(SortLab
|
if(WIN32)
|
||||||
|
add_executable(SortLab
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/App.cpp
|
src/App.cpp
|
||||||
src/App_audio.cpp
|
src/App_audio.cpp
|
||||||
@@ -29,7 +30,25 @@ add_executable(SortLab
|
|||||||
src/sorters/InsertionSorter.cpp
|
src/sorters/InsertionSorter.cpp
|
||||||
src/sorters/MergeSorter.cpp
|
src/sorters/MergeSorter.cpp
|
||||||
src/sorters/QuickSorter.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)
|
if(MSVC)
|
||||||
target_compile_options(SortLab PRIVATE /utf-8)
|
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)
|
target_link_libraries(SortLab PRIVATE sfml-graphics sfml-window sfml-system sfml-audio)
|
||||||
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_custom_command(TARGET SortLab POST_BUILD
|
add_custom_command(TARGET SortLab POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
# 🎯 SortLab
|
||||||
|
|
||||||
|
### ⚡ Интерактивный визуализатор алгоритмов сортировки
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 О проекте
|
||||||
|
|
||||||
|
**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 | built with ❤️ and C++17</sub>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
IDI_ICON1 ICON "icon.ico"
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
@@ -5,30 +5,21 @@
|
|||||||
class QuickSorter : public Sorter {
|
class QuickSorter : public Sorter {
|
||||||
public:
|
public:
|
||||||
QuickSorter();
|
QuickSorter();
|
||||||
|
|
||||||
void step(Array& array) override;
|
void step(Array& array) override;
|
||||||
bool isFinished() const override;
|
bool isFinished() const override;
|
||||||
std::string getName() const override;
|
std::string getName() const override;
|
||||||
void reset() override;
|
|
||||||
std::string getTimeComplexity() const override;
|
std::string getTimeComplexity() const override;
|
||||||
std::string getSpaceComplexity() const override;
|
std::string getSpaceComplexity() const override;
|
||||||
|
void reset() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Range { int low; int high; };
|
||||||
enum class Phase { PARTITIONING, SWAPPING_PIVOT, PUSHING_RANGES };
|
enum class Phase { PARTITIONING, SWAPPING_PIVOT, PUSHING_RANGES };
|
||||||
|
|
||||||
struct Range {
|
|
||||||
int low;
|
|
||||||
int high;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Range> stack_;
|
std::vector<Range> stack_;
|
||||||
int currentLow_;
|
Phase phase_;
|
||||||
int currentHigh_;
|
int currentLow_, currentHigh_, pivotIndex_, i_, j_, n_;
|
||||||
int pivotIndex_;
|
|
||||||
int i_;
|
|
||||||
int j_;
|
|
||||||
float pivotValue_;
|
float pivotValue_;
|
||||||
bool finished_;
|
bool finished_;
|
||||||
Phase phase_;
|
bool needNewRange_;
|
||||||
int n_;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ App::App()
|
|||||||
beepSound_.setBuffer(beepBuffer_);
|
beepSound_.setBuffer(beepBuffer_);
|
||||||
beepSound_.setVolume(100.0f);
|
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")) {
|
if (bottomPanelFont_.loadFromFile("assets/fonts/JetBrainsMono-Regular.ttf")) {
|
||||||
bottomPanelFontLoaded_ = true;
|
bottomPanelFontLoaded_ = true;
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-37
@@ -1,74 +1,61 @@
|
|||||||
#include "sorters/QuickSorter.hpp"
|
#include "sorters/QuickSorter.hpp"
|
||||||
|
|
||||||
QuickSorter::QuickSorter()
|
QuickSorter::QuickSorter() {
|
||||||
: currentLow_(0), currentHigh_(0), pivotIndex_(0), i_(0), j_(0)
|
reset();
|
||||||
, pivotValue_(0.0f), finished_(false), phase_(Phase::PARTITIONING), n_(0) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickSorter::step(Array& array) {
|
void QuickSorter::step(Array& array) {
|
||||||
if (finished_) {
|
if (finished_) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n_ == 0) {
|
if (n_ == 0) {
|
||||||
n_ = array.getSize();
|
n_ = array.getSize();
|
||||||
stack_.push_back({0, n_ - 1});
|
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()) {
|
if (stack_.empty()) {
|
||||||
array.resetStates();
|
array.resetStates();
|
||||||
for (int k = 0; k < n_; ++k) {
|
for (int k = 0; k < n_; ++k)
|
||||||
array.setState(k, Array::State::SORTED);
|
array.setState(k, Array::State::SORTED);
|
||||||
}
|
|
||||||
finished_ = true;
|
finished_ = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (phase_ == Phase::PARTITIONING) {
|
|
||||||
if (j_ == currentLow_) {
|
|
||||||
Range range = stack_.back();
|
Range range = stack_.back();
|
||||||
stack_.pop_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;
|
currentLow_ = range.low;
|
||||||
currentHigh_ = range.high;
|
currentHigh_ = range.high;
|
||||||
pivotIndex_ = currentHigh_;
|
pivotIndex_ = currentHigh_;
|
||||||
pivotValue_ = array.getValue(pivotIndex_);
|
pivotValue_ = array.getValue(pivotIndex_);
|
||||||
i_ = currentLow_ - 1;
|
i_ = currentLow_ - 1;
|
||||||
j_ = currentLow_;
|
j_ = currentLow_;
|
||||||
|
needNewRange_ = false;
|
||||||
|
phase_ = Phase::PARTITIONING;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (phase_ == Phase::PARTITIONING) {
|
||||||
if (j_ < currentHigh_) {
|
if (j_ < currentHigh_) {
|
||||||
array.resetStates();
|
array.resetStates();
|
||||||
array.setState(j_, Array::State::COMPARE);
|
array.setState(j_, Array::State::COMPARE);
|
||||||
array.setState(pivotIndex_, Array::State::SWAP);
|
array.setState(pivotIndex_, Array::State::SWAP);
|
||||||
|
|
||||||
array.incrementComparisons();
|
array.incrementComparisons();
|
||||||
|
|
||||||
if (array.getValue(j_) < pivotValue_) {
|
if (array.getValue(j_) < pivotValue_) {
|
||||||
i_++;
|
i_++;
|
||||||
|
|
||||||
if (i_ != j_) {
|
if (i_ != j_) {
|
||||||
array.setState(i_, Array::State::SWAP);
|
array.setState(i_, Array::State::SWAP);
|
||||||
|
|
||||||
float temp = array.getValue(i_);
|
float temp = array.getValue(i_);
|
||||||
array.setValue(i_, array.getValue(j_));
|
array.setValue(i_, array.getValue(j_));
|
||||||
array.setValue(j_, temp);
|
array.setValue(j_, temp);
|
||||||
|
|
||||||
array.incrementSwaps();
|
array.incrementSwaps();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
j_++;
|
j_++;
|
||||||
} else {
|
} else {
|
||||||
phase_ = Phase::SWAPPING_PIVOT;
|
phase_ = Phase::SWAPPING_PIVOT;
|
||||||
@@ -81,22 +68,17 @@ void QuickSorter::step(Array& array) {
|
|||||||
float temp = array.getValue(i_ + 1);
|
float temp = array.getValue(i_ + 1);
|
||||||
array.setValue(i_ + 1, array.getValue(pivotIndex_));
|
array.setValue(i_ + 1, array.getValue(pivotIndex_));
|
||||||
array.setValue(pivotIndex_, temp);
|
array.setValue(pivotIndex_, temp);
|
||||||
|
|
||||||
array.incrementSwaps();
|
array.incrementSwaps();
|
||||||
pivotIndex_ = i_ + 1;
|
|
||||||
|
|
||||||
|
pivotIndex_ = i_ + 1;
|
||||||
phase_ = Phase::PUSHING_RANGES;
|
phase_ = Phase::PUSHING_RANGES;
|
||||||
} else if (phase_ == Phase::PUSHING_RANGES) {
|
} else if (phase_ == Phase::PUSHING_RANGES) {
|
||||||
if (pivotIndex_ - 1 > currentLow_) {
|
if (pivotIndex_ - 1 > currentLow_)
|
||||||
stack_.push_back({currentLow_, pivotIndex_ - 1});
|
stack_.push_back({currentLow_, pivotIndex_ - 1});
|
||||||
}
|
if (pivotIndex_ + 1 < currentHigh_)
|
||||||
|
|
||||||
if (pivotIndex_ + 1 < currentHigh_) {
|
|
||||||
stack_.push_back({pivotIndex_ + 1, currentHigh_});
|
stack_.push_back({pivotIndex_ + 1, currentHigh_});
|
||||||
}
|
|
||||||
|
|
||||||
j_ = currentLow_;
|
needNewRange_ = true;
|
||||||
phase_ = Phase::PARTITIONING;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +95,7 @@ std::string QuickSorter::getTimeComplexity() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string QuickSorter::getSpaceComplexity() const {
|
std::string QuickSorter::getSpaceComplexity() const {
|
||||||
return "O(n)";
|
return "O(log n)";
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickSorter::reset() {
|
void QuickSorter::reset() {
|
||||||
@@ -125,6 +107,7 @@ void QuickSorter::reset() {
|
|||||||
j_ = 0;
|
j_ = 0;
|
||||||
pivotValue_ = 0.0f;
|
pivotValue_ = 0.0f;
|
||||||
finished_ = false;
|
finished_ = false;
|
||||||
|
needNewRange_ = true;
|
||||||
phase_ = Phase::PARTITIONING;
|
phase_ = Phase::PARTITIONING;
|
||||||
n_ = 0;
|
n_ = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user