diff --git a/CMakeLists.txt b/CMakeLists.txt index a93d126..7314a43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,9 @@ if(WIN32) src/sorters/InsertionSorter.cpp src/sorters/MergeSorter.cpp src/sorters/QuickSorter.cpp + src/sorters/HeapSorter.cpp + src/sorters/ShellSorter.cpp + src/sorters/RadixSorter.cpp assets/app.rc ) else() @@ -47,6 +50,9 @@ else() src/sorters/InsertionSorter.cpp src/sorters/MergeSorter.cpp src/sorters/QuickSorter.cpp + src/sorters/HeapSorter.cpp + src/sorters/ShellSorter.cpp + src/sorters/RadixSorter.cpp ) endif() diff --git a/README.md b/README.md index 3810156..fc43669 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ | `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) | +| `6` | 🌳 Heap Sort | O(n log n) | O(1) | +| `7` | 🐚 Shell Sort | O(n log² n) | O(1) | +| `8` | 🔢 Radix Sort | O(n·k) | O(n + k) | --- @@ -44,7 +47,7 @@ | `R` | 🔀 Перемешать массив | | `→` | 👣 Один шаг (в режиме паузы) | | `↑` / `↓` | 🐇 / 🐢 Увеличить / уменьшить скорость | -| `1` — `5` | 🔢 Выбрать алгоритм | +| `1` — `8` | 🔢 Выбрать алгоритм | | `I` | ℹ️ Справка о программе | | `Q` | 🚪 Выход | diff --git a/include/sorters/HeapSorter.hpp b/include/sorters/HeapSorter.hpp new file mode 100644 index 0000000..3defa73 --- /dev/null +++ b/include/sorters/HeapSorter.hpp @@ -0,0 +1,29 @@ +#pragma once +#include "Sorter.hpp" + +class HeapSorter : public Sorter { +public: + HeapSorter(); + + 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; + +private: + enum class Phase { BUILDING_HEAP, HEAPIFY_DOWN, EXTRACTING, EXTRACT_SWAP, EXTRACT_HEAPIFY }; + + int n_; + int heapSize_; + int buildIndex_; + int extractIndex_; + int heapifyIndex_; + int leftChild_; + int rightChild_; + int largest_; + bool finished_; + Phase phase_; + bool needSwap_; +}; diff --git a/include/sorters/RadixSorter.hpp b/include/sorters/RadixSorter.hpp new file mode 100644 index 0000000..f99323e --- /dev/null +++ b/include/sorters/RadixSorter.hpp @@ -0,0 +1,27 @@ +#pragma once +#include "Sorter.hpp" +#include + +class RadixSorter : public Sorter { +public: + RadixSorter(); + + 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; + +private: + enum class Phase { INIT, COUNTING, PLACING, COPYING_BACK }; + + int n_; + int maxValue_; + int exp_; + int i_; + std::vector count_; + std::vector output_; + bool finished_; + Phase phase_; +}; diff --git a/include/sorters/ShellSorter.hpp b/include/sorters/ShellSorter.hpp new file mode 100644 index 0000000..a86c55b --- /dev/null +++ b/include/sorters/ShellSorter.hpp @@ -0,0 +1,25 @@ +#pragma once +#include "Sorter.hpp" + +class ShellSorter : public Sorter { +public: + ShellSorter(); + + 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; + +private: + enum class Phase { COMPARING, SHIFTING, PLACING }; + + int n_; + int gap_; + int i_; + int j_; + float temp_; + bool finished_; + Phase phase_; +}; diff --git a/src/App.cpp b/src/App.cpp index fd100e5..101b680 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -4,6 +4,9 @@ #include "sorters/InsertionSorter.hpp" #include "sorters/MergeSorter.hpp" #include "sorters/QuickSorter.hpp" +#include "sorters/HeapSorter.hpp" +#include "sorters/ShellSorter.hpp" +#include "sorters/RadixSorter.hpp" App::App() : window_(sf::VideoMode(1600, 900), "SortLab") @@ -97,6 +100,18 @@ void App::handleEvents() { switchSorter(std::make_unique()); break; + case sf::Keyboard::Num6: + switchSorter(std::make_unique()); + break; + + case sf::Keyboard::Num7: + switchSorter(std::make_unique()); + break; + + case sf::Keyboard::Num8: + switchSorter(std::make_unique()); + break; + case sf::Keyboard::R: array_.shuffle(); array_.resetStates(); diff --git a/src/UI.cpp b/src/UI.cpp index aef6ba1..57cf923 100644 --- a/src/UI.cpp +++ b/src/UI.cpp @@ -58,7 +58,7 @@ UI::UI() : fontLoaded_(false) { controlsText_.setCharacterSize(30); controlsText_.setScale(0.5f, 0.5f); controlsText_.setFillColor(sf::Color(200, 200, 200)); - controlsText_.setString("[1-5] Algorithms [Space] Play/Pause [Right] Step [Up/Down] Speed [R] Shuffle"); + controlsText_.setString("[1-8] Algorithms [Space] Play/Pause [Right] Step [Up/Down] Speed [R] Shuffle"); leftBackground_.setFillColor(sf::Color(0, 0, 0, 180)); rightBackground_.setFillColor(sf::Color(0, 0, 0, 180)); @@ -201,7 +201,7 @@ void UI::drawInfoOverlay(sf::RenderWindow& window) { body2.setCharacterSize(32); body2.setScale(0.5f, 0.5f); body2.setFillColor(sf::Color(200, 200, 200)); - body2.setString(toSfStr(u8"[1] Bubble Sort Время: O(n) / O(n^2) / O(n^2) Память: O(1)\n[2] Selection Sort Время: O(n^2) / O(n^2) / O(n^2) Память: O(1)\n[3] Insertion Sort Время: O(n) / O(n^2) / O(n^2) Память: O(1)\n[4] Merge Sort Время: O(n log n) / O(n log n) / O(n log n) Память: O(n)\n[5] Quick Sort Время: O(n log n) / O(n log n) / O(n^2) Память: O(log n)")); + body2.setString(toSfStr(u8"[1] Bubble Sort Время: O(n) / O(n^2) / O(n^2) Память: O(1)\n[2] Selection Sort Время: O(n^2) / O(n^2) / O(n^2) Память: O(1)\n[3] Insertion Sort Время: O(n) / O(n^2) / O(n^2) Память: O(1)\n[4] Merge Sort Время: O(n log n) / O(n log n) / O(n log n) Память: O(n)\n[5] Quick Sort Время: O(n log n) / O(n log n) / O(n^2) Память: O(log n)\n[6] Heap Sort Время: O(n log n) Память: O(1)\n[7] Shell Sort Время: O(n log^2 n) Память: O(1)\n[8] Radix Sort Время: O(n*k) Память: O(n + k)")); sf::Text section3; section3.setFont(font_); diff --git a/src/sorters/HeapSorter.cpp b/src/sorters/HeapSorter.cpp new file mode 100644 index 0000000..c59d3f5 --- /dev/null +++ b/src/sorters/HeapSorter.cpp @@ -0,0 +1,133 @@ +#include "sorters/HeapSorter.hpp" + +HeapSorter::HeapSorter() { + reset(); +} + +void HeapSorter::step(Array& array) { + if (finished_) return; + + if (n_ == 0) { + n_ = array.getSize(); + if (n_ <= 1) { + finished_ = true; + return; + } + heapSize_ = n_; + buildIndex_ = n_ / 2 - 1; + phase_ = Phase::BUILDING_HEAP; + heapifyIndex_ = buildIndex_; + } + + if (phase_ == Phase::BUILDING_HEAP) { + if (buildIndex_ < 0) { + phase_ = Phase::EXTRACTING; + extractIndex_ = n_ - 1; + return; + } + heapifyIndex_ = buildIndex_; + phase_ = Phase::HEAPIFY_DOWN; + needSwap_ = false; // Используем как флаг, что мы в контексте BUILD + } + else if (phase_ == Phase::HEAPIFY_DOWN || phase_ == Phase::EXTRACT_HEAPIFY) { + leftChild_ = 2 * heapifyIndex_ + 1; + rightChild_ = 2 * heapifyIndex_ + 2; + largest_ = heapifyIndex_; + + array.resetStates(); + array.setState(heapifyIndex_, Array::State::COMPARE); + + if (leftChild_ < heapSize_) { + array.setState(leftChild_, Array::State::COMPARE); + array.incrementComparisons(); + if (array.getValue(leftChild_) > array.getValue(largest_)) { + largest_ = leftChild_; + } + } + + if (rightChild_ < heapSize_) { + array.setState(rightChild_, Array::State::COMPARE); + array.incrementComparisons(); + if (array.getValue(rightChild_) > array.getValue(largest_)) { + largest_ = rightChild_; + } + } + + if (largest_ != heapifyIndex_) { + array.setState(heapifyIndex_, Array::State::SWAP); + array.setState(largest_, Array::State::SWAP); + + float temp = array.getValue(heapifyIndex_); + array.setValue(heapifyIndex_, array.getValue(largest_)); + array.setValue(largest_, temp); + array.incrementSwaps(); + + heapifyIndex_ = largest_; + // Остаемся в текущей фазе, чтобы продолжить просеивание вниз + } else { + // Элемент на своем месте, просеивание закончено + if (phase_ == Phase::HEAPIFY_DOWN) { + buildIndex_--; + phase_ = Phase::BUILDING_HEAP; + } else { + phase_ = Phase::EXTRACTING; + } + } + } + else if (phase_ == Phase::EXTRACTING) { + if (extractIndex_ <= 0) { + array.resetStates(); + for (int k = 0; k < n_; ++k) { + array.setState(k, Array::State::SORTED); + } + finished_ = true; + return; + } + phase_ = Phase::EXTRACT_SWAP; + } + else if (phase_ == Phase::EXTRACT_SWAP) { + array.resetStates(); + array.setState(0, Array::State::SWAP); + array.setState(extractIndex_, Array::State::SWAP); + + float temp = array.getValue(0); + array.setValue(0, array.getValue(extractIndex_)); + array.setValue(extractIndex_, temp); + array.incrementSwaps(); + + heapSize_--; + extractIndex_--; + heapifyIndex_ = 0; + phase_ = Phase::EXTRACT_HEAPIFY; + } +} + +bool HeapSorter::isFinished() const { + return finished_; +} + +std::string HeapSorter::getName() const { + return "Heap Sort"; +} + +std::string HeapSorter::getTimeComplexity() const { + return "O(n log n)"; +} + +std::string HeapSorter::getSpaceComplexity() const { + return "O(1)"; +} + +void HeapSorter::reset() { + n_ = 0; + heapSize_ = 0; + buildIndex_ = 0; + extractIndex_ = 0; + heapifyIndex_ = 0; + leftChild_ = 0; + rightChild_ = 0; + largest_ = 0; + finished_ = false; + phase_ = Phase::BUILDING_HEAP; + needSwap_ = false; +} diff --git a/src/sorters/RadixSorter.cpp b/src/sorters/RadixSorter.cpp new file mode 100644 index 0000000..5387977 --- /dev/null +++ b/src/sorters/RadixSorter.cpp @@ -0,0 +1,109 @@ +#include "sorters/RadixSorter.hpp" +#include +#include + +RadixSorter::RadixSorter() { + reset(); +} + +void RadixSorter::step(Array& array) { + if (finished_) return; + + if (n_ == 0) { + n_ = array.getSize(); + maxValue_ = 0; + for (int k = 0; k < n_; ++k) { + int val = static_cast(std::round(std::abs(array.getValue(k)))); + if (val > maxValue_) maxValue_ = val; + } + if (maxValue_ == 0) maxValue_ = 1; + exp_ = 1; + count_.resize(10, 0); + output_.resize(n_, 0.0f); + phase_ = Phase::INIT; + } + + if (phase_ == Phase::INIT) { + if (exp_ > maxValue_) { + array.resetStates(); + for (int k = 0; k < n_; ++k) { + array.setState(k, Array::State::SORTED); + } + finished_ = true; + return; + } + + std::fill(count_.begin(), count_.end(), 0); + i_ = 0; + phase_ = Phase::COUNTING; + } else if (phase_ == Phase::COUNTING) { + if (i_ < n_) { + array.resetStates(); + array.setState(i_, Array::State::COMPARE); + + int val = static_cast(std::round(std::abs(array.getValue(i_)))); + int digit = (val / exp_) % 10; + count_[digit]++; + array.incrementComparisons(); + i_++; + } else { + for (int k = 1; k < 10; ++k) { + count_[k] += count_[k - 1]; + } + i_ = n_ - 1; + phase_ = Phase::PLACING; + } + } else if (phase_ == Phase::PLACING) { + if (i_ >= 0) { + array.resetStates(); + array.setState(i_, Array::State::SWAP); + + int val = static_cast(std::round(std::abs(array.getValue(i_)))); + int digit = (val / exp_) % 10; + output_[count_[digit] - 1] = array.getValue(i_); + count_[digit]--; + array.incrementSwaps(); + i_--; + } else { + i_ = 0; + phase_ = Phase::COPYING_BACK; + } + } else if (phase_ == Phase::COPYING_BACK) { + if (i_ < n_) { + array.resetStates(); + array.setState(i_, Array::State::SWAP); + array.setValue(i_, output_[i_]); + i_++; + } else { + exp_ *= 10; + phase_ = Phase::INIT; + } + } +} + +bool RadixSorter::isFinished() const { + return finished_; +} + +std::string RadixSorter::getName() const { + return "Radix Sort"; +} + +std::string RadixSorter::getTimeComplexity() const { + return "O(n*k)"; +} + +std::string RadixSorter::getSpaceComplexity() const { + return "O(n + k)"; +} + +void RadixSorter::reset() { + n_ = 0; + maxValue_ = 0; + exp_ = 1; + i_ = 0; + count_.clear(); + output_.clear(); + finished_ = false; + phase_ = Phase::INIT; +} diff --git a/src/sorters/ShellSorter.cpp b/src/sorters/ShellSorter.cpp new file mode 100644 index 0000000..229222d --- /dev/null +++ b/src/sorters/ShellSorter.cpp @@ -0,0 +1,97 @@ +#include "sorters/ShellSorter.hpp" + +ShellSorter::ShellSorter() { + reset(); +} + +void ShellSorter::step(Array& array) { + if (finished_) return; + + if (n_ == 0) { + n_ = array.getSize(); + gap_ = n_ / 2; + i_ = gap_; + phase_ = Phase::COMPARING; + } + + if (gap_ == 0) { + array.resetStates(); + for (int k = 0; k < n_; ++k) { + array.setState(k, Array::State::SORTED); + } + finished_ = true; + return; + } + + if (phase_ == Phase::COMPARING) { + if (i_ >= n_) { + gap_ /= 2; + i_ = gap_; + if (gap_ == 0) { + array.resetStates(); + for (int k = 0; k < n_; ++k) { + array.setState(k, Array::State::SORTED); + } + finished_ = true; + } + return; + } + + temp_ = array.getValue(i_); + j_ = i_; + phase_ = Phase::SHIFTING; + } else if (phase_ == Phase::SHIFTING) { + if (j_ >= gap_) { + array.resetStates(); + array.setState(j_, Array::State::COMPARE); + array.setState(j_ - gap_, Array::State::COMPARE); + array.incrementComparisons(); + + if (array.getValue(j_ - gap_) > temp_) { + array.setState(j_, Array::State::SWAP); + array.setState(j_ - gap_, Array::State::SWAP); + + array.setValue(j_, array.getValue(j_ - gap_)); + array.incrementSwaps(); + j_ -= gap_; + } else { + phase_ = Phase::PLACING; + } + } else { + phase_ = Phase::PLACING; + } + } else if (phase_ == Phase::PLACING) { + array.resetStates(); + array.setState(j_, Array::State::SWAP); + array.setValue(j_, temp_); + + i_++; + phase_ = Phase::COMPARING; + } +} + +bool ShellSorter::isFinished() const { + return finished_; +} + +std::string ShellSorter::getName() const { + return "Shell Sort"; +} + +std::string ShellSorter::getTimeComplexity() const { + return "O(n log^2 n)"; +} + +std::string ShellSorter::getSpaceComplexity() const { + return "O(1)"; +} + +void ShellSorter::reset() { + n_ = 0; + gap_ = 0; + i_ = 0; + j_ = 0; + temp_ = 0.0f; + finished_ = false; + phase_ = Phase::COMPARING; +}