mirror of
https://github.com/koloideal/SortLab.git
synced 2026-06-10 10:25:30 +03:00
final
This commit is contained in:
+34
-16
@@ -15,21 +15,40 @@ FetchContent_Declare(
|
||||
)
|
||||
FetchContent_MakeAvailable(SFML)
|
||||
|
||||
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
|
||||
)
|
||||
if(WIN32)
|
||||
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
|
||||
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
|
||||
|
||||
@@ -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 {
|
||||
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_;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+32
-49
@@ -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 (stack_.empty()) {
|
||||
array.resetStates();
|
||||
for (int k = 0; k < n_; ++k) {
|
||||
array.setState(k, Array::State::SORTED);
|
||||
if (needNewRange_) {
|
||||
while (!stack_.empty() && stack_.back().low >= stack_.back().high) {
|
||||
stack_.pop_back();
|
||||
}
|
||||
finished_ = true;
|
||||
|
||||
if (stack_.empty()) {
|
||||
array.resetStates();
|
||||
for (int k = 0; k < n_; ++k)
|
||||
array.setState(k, Array::State::SORTED);
|
||||
finished_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Range range = stack_.back();
|
||||
stack_.pop_back();
|
||||
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_ == 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_;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user