Advertisement
gg-master

Новая реализация с поиском всех уникальных циклов

May 13th, 2023
1,775
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.94 KB | None | 0 0
  1. #include <filesystem>
  2. #include <iostream>
  3. #include <fstream>
  4. #include <sstream>
  5. #include <vector>
  6. #include <map>
  7. #include <set>
  8.  
  9.  
  10. using namespace std;
  11.  
  12. struct Vertex {
  13.     std::string name;
  14.     std::vector<Vertex*> adjacentVertices;
  15.  
  16.     Vertex(const std::string& vertexName) : name(vertexName) {}
  17.  };
  18.  
  19. class InvalidGraphAssignment : public std::exception {
  20. public:
  21.     InvalidGraphAssignment(const std::string& message) : m_message(message) {}
  22.  
  23.     const char* what() const noexcept override {
  24.         return m_message.c_str();
  25.     }
  26. private:
  27.     std::string m_message;
  28. };
  29.  
  30. class InvalidInputFile : public std::exception {
  31. public:
  32.     InvalidInputFile(const std::string& message) : m_message(message) {}
  33.  
  34.     const char* what() const noexcept override {
  35.         return m_message.c_str();
  36.     }
  37. private:
  38.     std::string m_message;
  39. };
  40.  
  41. /*! Получить правильное имя цикла. Правильное имя состоит из строки, полученной при
  42.     конкатенации имен каждой вершины, и сортировки полученной строки в алфавитном порядке.
  43. * \param[in] cycle - цикл, для которого необходимо получить правильное имя.
  44. * \return правильное имя цикла.
  45. */
  46. std::string getCorrectCycleName(const std::vector<Vertex*> cycle) {
  47.     // Конкатенируем названия вершин в одну строку
  48.     std::string cycleName = ""; // Определяем строку для создания правильного имени цикла
  49.     // Для каждой вершины цикла
  50.     for (const Vertex* v : cycle) {
  51.         // добавить имя рассматриваемой вершины в строку для создания правильного имени цикла
  52.         cycleName += v->name;
  53.     }
  54.     // Получаем правильное имя цикла, отсортировав строку
  55.     std::sort(cycleName.begin(), cycleName.end());
  56.     // Возвращаем правильное имя цикла
  57.     return cycleName;
  58. }
  59.  
  60. /*! Определение наличия цикла для указанной вершины, используя алгоритм поиска в глубину.
  61. * \param[in] viewedVertex - рассматриваемая вершина.
  62. * \param[in, out] recStack - массив для промежуточного хранения цикла.
  63. * \param[in, out] visited - массив для хранения просмотренных вершин.
  64. * \param[in] cycles - массив с найденными циклами.
  65. * \return 0 - если цикл не найден. 1 - если цикл найден.
  66. */
  67. bool dfs(Vertex* viewedVertex, std::vector<Vertex*>& recStack, std::set<Vertex*>& visited, const std::map<std::string, vector<Vertex*>>& cycles) {
  68.     // Добавляем просматриваемую вершину в список посещенных
  69.     visited.insert(viewedVertex);
  70.     // Добавляем текущую вершину в массив с формируемым циклом
  71.     recStack.push_back(viewedVertex);
  72.  
  73.     // Для каждлой смежной вершины у просматриваемой вершины
  74.     for (Vertex* v : viewedVertex->adjacentVertices) {
  75.         // Если смежную вершину еще не посещали
  76.         if (!visited.contains(v)) {
  77.             // Запускаем алгоритм поиска в глубину для смежной вершины
  78.             if (dfs(v, recStack, visited, cycles)) {
  79.                 // Если нашли цикл, то возвращаем истину
  80.                 return true;
  81.             }
  82.         }
  83.         // Если смежную вершину уже посещали и  вершина является
  84.         // первой в списке с формируемом циклом, значит, мы нашли цикл
  85.         else if (v == recStack[0]) {
  86.             // Если ранее цикл с таким же правильным именем не находили
  87.             if (!cycles.contains(getCorrectCycleName(recStack)))
  88.                 // Считаем, что нашли новый цикл и возвращаем истину
  89.                 return true;
  90.         }
  91.     }
  92.     // Если цикла не нашли, то удаляем вершину из массива с формируемым циклом
  93.     recStack.pop_back();
  94.     // Возвращаем ложь, т.к. цикл не нашли
  95.     return false;
  96. }
  97.  
  98. /*! Поиск любого цикла в графе.
  99. * \param[in] vertexName - название искомой вершины.
  100. * \param[in] graph - массив с вершинами (граф), в котором идет поиск.
  101. * \param[out] cycles - массив с найденными циклами.
  102. * \return 0 - если цикл не найден. 1 - если цикл найден.
  103. */
  104. bool findCycles(const std::vector<Vertex*>& graph, std::map<std::string, vector<Vertex*>>& cycles) {
  105.     // Определям массив, для хранения возможного цикла и массив посещенных вершин
  106.     std::vector<Vertex*> recStack;
  107.     std::set<Vertex*> visited;
  108.  
  109.     bool isAnyCycleFound = false;
  110.  
  111.     // Для каждой вершины графа
  112.     for (Vertex* v : graph) {
  113.         // Если нашли цикл у текущей рассматриваемой вершины
  114.         if (dfs(v, recStack, visited, cycles)) {
  115.             // Добавляем цикл в массив найденных циклов
  116.             std::string cycleName = getCorrectCycleName(recStack);
  117.  
  118.             recStack.push_back(v);
  119.             cycles[cycleName] = recStack;
  120.  
  121.             // Помечаем, что был найден цикл
  122.             isAnyCycleFound = true;
  123.             // Очищаем массив для хранения возможного цикла
  124.             recStack.clear();
  125.         }
  126.         // Если цикл не нашли, то очищаем массив песещенных вершин
  127.         visited.clear();
  128.     }
  129.     // Возвращаем результат поиска цикла в графе
  130.     return isAnyCycleFound;
  131. }
  132.  
  133. /*! Прочитывает входные данные из CSV файла, на их основе
  134.     создает вершины Vertex и сохраняет их в массив graph.
  135. * \param[in] vertexName - название искомой вершины.
  136. * \param[out] graph - массив с вершинами (граф), в котором идет поиск.
  137. */
  138. Vertex* findVertexInGraph(const std::string vertexName, const vector<Vertex*>& graph) {
  139.     // Для всех вершин графа
  140.     for (Vertex* v : graph) {
  141.         // Если имя вершины равно искомому имени, вернуть эту вершину
  142.         if (v->name == vertexName) return v;
  143.     }
  144.     // Если не нашли вершину с искомым именем, вернуть пустой указатель
  145.     return nullptr;
  146. }
  147.  
  148. /*! Прочитывает входные данные из CSV файла, на их основе
  149.     создает вершины Vertex и сохраняет их в массив graph.
  150. * \param[in] input_file_path - строка с путем к файлу с входными данными.
  151. * \param[out] graph - массив с вершинами.
  152. * \param[out] delimiter - разделитель элементов в строке, использующийся в CSV файле.
  153. */
  154. void readCSVFile(const std::string& input_file_path, std::vector<Vertex*>&  graph, const char delimiter=';') {
  155.     // Открываем файл
  156.     std::ifstream input_file(input_file_path); // Закрывать не надо, т.к. после завершения функции закроется автоматом
  157.  
  158.     // Объявляем перменные для строки файла и строки для считанной клетки
  159.     std::string line, buff;
  160.  
  161.     // Считываем заголовок таблицы
  162.     std::getline(input_file, line);
  163.  
  164.     // Открываем поток для строки, чтобы разделить ее по разделителям
  165.     std::stringstream ss(line);
  166.  
  167.     std::getline(ss, buff, delimiter); // Первую клетку читаем в пустоту
  168.  
  169.     std::vector<Vertex*> headers_vertexes; // массив для вершин из заголовка
  170.  
  171.     // Читаем заголовок с названиями вершин
  172.     while (std::getline(ss, buff, delimiter)) {
  173.         // Проверяем на дублирование названий вершин
  174.         if (findVertexInGraph(buff, headers_vertexes) != nullptr)
  175.             throw InvalidGraphAssignment("Неверные входные данные. Повторение вершины " + buff);
  176.         // Добавляем вершину в массив вершин
  177.         headers_vertexes.push_back(new Vertex(buff));
  178.     }
  179.     // Ошибка чтения фала, если граф полностью пустой
  180.     if (headers_vertexes.size() == 0)
  181.         throw InvalidInputFile("Неверные входные данные. Граф не содержит вершин.");
  182.  
  183.     // Читаем остальной файл
  184.     // Считаем, что количество линий в файле (не считая заголов) - это количество вершин
  185.     int vertexes_count = 0;
  186.  
  187.     // Определяем массив для хранения вершин, указанных в первом столбце
  188.     std::map<std::string, Vertex*> body_vertexes;
  189.  
  190.     // Пока не встретили пустую строку
  191.     while (std::getline(input_file, line)) {
  192.         // Инкрементируем счетчик вершин, т.к. считаем, что каждая строка - описание вершин
  193.         vertexes_count++;
  194.  
  195.         // Если строк больше, чем столбоц в заголовке, то ошибка размерности
  196.         if (vertexes_count > headers_vertexes.size())
  197.             throw InvalidGraphAssignment("Неверные входные данные. Ошибка размерности матрицы");
  198.  
  199.         // Открываем поток для строки, чтобы разделить ее по разделителям
  200.         std::stringstream ss(line);
  201.         std::getline(ss, buff, delimiter); // Первая клетка содержит название вершины
  202.  
  203.         // Проверяем, что вершина была указана в множестве вершин из заголовка
  204.         if (findVertexInGraph(buff, headers_vertexes) == nullptr)
  205.             throw InvalidGraphAssignment("Неверные входные данные. Содержится лишняя вершина " + buff);
  206.  
  207.         // Проверяем, что название вершины не повторяется
  208.         if (body_vertexes.contains(buff))
  209.             throw InvalidGraphAssignment("Неверные входные данные. Повторение вершины " + buff);
  210.  
  211.         // Получаем объект вершины их массива вершин из заголовка, т.е. уже созаднную
  212.         Vertex* vertex = body_vertexes[buff] = findVertexInGraph(buff, headers_vertexes);
  213.  
  214.         // Создаем связи для вершины в строке
  215.         int count_cells = 0;
  216.         // Пока имеются клетки в строке матрицы
  217.         while (std::getline(ss, buff, delimiter)) {
  218.             // Переходим к следующему столбцу
  219.             count_cells++;
  220.             if (count_cells > headers_vertexes.size())  // количество клеток больше, чем количество вершин в
  221.                 throw InvalidGraphAssignment("Неверные входные данные. Матрица смежностей имеет неправильные размеры.");
  222.  
  223.             if (buff == "1") // в клетке установлена 1
  224.             {
  225.                 // Если новая связть создат петлю, то  выдаем ошибку
  226.                 if (vertex->name == headers_vertexes[count_cells - 1]->name)
  227.                     throw InvalidGraphAssignment("Неверные входные данные.В графе содержатся петли.");
  228.                 // Создаем связь, добавлением одной вершины в массив связанных у другой
  229.                 vertex->adjacentVertices.push_back(headers_vertexes[count_cells - 1]);
  230.             }
  231.             else if (buff != "0") // в клетке установлено значение отличное 0
  232.                 throw InvalidGraphAssignment("Неверные входные данные. Неверные параметры обозначения связей");
  233.         }
  234.         if (count_cells != headers_vertexes.size())
  235.             throw InvalidGraphAssignment("Неверные входные данные. "
  236.                 "Не указаны ребра, связанные с вершиной " + vertex->name);
  237.     }
  238.     // Копируем вершины в выходной параметр графа.
  239.     for (Vertex* v : headers_vertexes) {
  240.         graph.push_back(v);
  241.     }
  242. }
  243.  
  244. int main(int argc, char* argv[])
  245. {
  246.     setlocale(LC_ALL, "Russian");
  247.     if (argc != 3)
  248.     {
  249.         std::cerr << "Неправильно указаны параметры запуска. "
  250.             "Убедитесь, что параметры соотвествуют шаблону: \n"
  251.             << argv[0] << " <path/to/input_file.csv> <path/to/save_file.txt>\n";
  252.         return 1;
  253.     }
  254.  
  255.     std::ifstream input_file(argv[1]);
  256.  
  257.     if (!input_file.is_open()) {
  258.         std::cerr << "Неверно указан файл с входными данными. Возможно, файл не существует." << argv[1] << '\n';
  259.         return 1;
  260.     }
  261.  
  262.     filesystem::path output_path = filesystem::path(argv[2]);
  263.  
  264.     if (!(filesystem::exists(output_path.parent_path()) &&
  265.         filesystem::is_directory(output_path.parent_path()) &&
  266.         output_path.has_filename()))
  267.     {
  268.         std::cerr << "Неверно указан файл для выходных данных. "
  269.             "Возможно указанного расположения не существует или нет прав на запись." << '\n';
  270.         return 1;
  271.     }
  272.     std::ofstream output_file(output_path);
  273.  
  274.     if (!output_file.is_open()) {
  275.         std::cerr << "Неверно указан файл для выходных данных. "
  276.             "Возможно указанного расположения не существует или нет прав на запись." << '\n';
  277.         return 1;
  278.     }
  279.  
  280.     std::vector<Vertex*> graph;
  281.     try {
  282.         readCSVFile(argv[1], graph);
  283.     }
  284.     catch (const InvalidGraphAssignment& e) {
  285.         std::cerr << e.what();
  286.         return 1;
  287.     }
  288.     catch (const InvalidInputFile& e) {
  289.         std::cerr << e.what();
  290.         return 1;
  291.     }
  292.  
  293.     std::map<std::string, vector<Vertex*>> cycles;
  294.  
  295.     if (findCycles(graph, cycles)) {
  296.         output_file << "True" << '\n';
  297.     }
  298.     else {
  299.         output_file << "False" << '\n';
  300.     }
  301.    
  302.     for (const auto& [cycleName, cycleVector] : cycles) {
  303.         for (int j = 0; j < cycleVector.size(); j++) {
  304.             output_file << cycleVector[j]->name;
  305.             if (j < cycleVector.size() - 1) output_file << '-';
  306.         }
  307.         output_file << '\n';
  308.     }
  309.     // Закрыть файлы
  310.     input_file.close();
  311.     output_file.close();
  312.  
  313.     return 0;
  314. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement