/// <summary>Создает случайный граф</summary> /// <param name="elements">Количество элементов</param> /// <returns>Созданный граф</returns> private static Graph CreateRandomGraph(int elements = ELEMENTS) { Graph newGraph = new Graph(); //Добавляем в граф вершины for (int i = 0; i < elements; i++) { newGraph.AddNode(i); } GraphPrinter.CreateGraphInfo(newGraph); //Добавляем связи между вершинами for (int i = 0; i < elements; i++) { for (int j = 0; j < rnd.Next(1) + 2; j++) { bool isGenerated = false; while (!isGenerated) { int toNode = rnd.Next(elements); if (toNode != i) { isGenerated = newGraph.AddEdge(i, toNode, rnd.Next(MAX_WEIGHT)); } if (newGraph.Nodes[i].Edges.Count >= 2) { isGenerated = true; } } } } return(newGraph); }
/// <summary> /// Поиск (обход) графа в глубину /// </summary> /// <param name="id">Искомое значение</param> /// <returns>true, если вершина с таким значением есть в графе</returns> public bool DFS(int id) { GraphPrinter.ClearText();//Очищаем текстовый буфер печатальщика bool isFound = DFS(id, this.Nodes[0]); ColorPrint("Поиск завершен"); Console.WriteLine(); return(isFound); }
/// <summary>Вывод дерева на экран с раскраской вершин /// Используется для визуализации алгоритмов обхода графа</summary> /// <param name="line">Строка идущая в текстовый буфер</param> private void ColorPrint(string line = null) { Console.Clear(); if (line != null) { GraphPrinter.AddText(line); } GraphPrinter.Print(this, false); System.Threading.Thread.Sleep(Delay);//небольшая задержка для наглядности работы алгоритма }
/// <summary> /// Поиск (обход) графа в ширину /// </summary> /// <param name="id">Искомое значение</param> /// <returns>true, если вершина с таким значением есть в графе</returns> public bool BFS(int id) { GraphPrinter.ClearText(); //Очищаем текстовый буфер печатальщика Queue <Node> bufer = new Queue <Node>(); //Сюда будут заносится вершины для последующей проверки bufer.Enqueue(this.Nodes[0]); //Обход начинаем с 0-й вершины bool isFound = false; //Повторяем до тех пор пока не найдем или пока не кончатся вершины while (bufer.Count != 0 && !isFound) { Node element = bufer.Dequeue();//Достаем элемент из очереди if (element.ID == id) { isFound = true; //Если это нужны элемент то радуемся } //Выставляем флаг проверки того, что элемент обрабатывается или найден //ПУРПУРНЫЙ - элемент в процессе обработки //ЗЕЛЕНЫЙ - элемент обработан и совпадает с искомым значением element.State = isFound ? Node.Status.founded : Node.Status.in_process; ColorPrint($"Проверяемая вершина: {element.ID}"); if (!isFound)//Если не нашли значение то добавляем сопряженные вершины в очередь на проверку { foreach (Edge edge in element.Edges) { if (edge.ConnectedNode.State == Node.Status.not_processed) { //ЖЕЛТЫЙ - элемент помещается в очередь для последующей обработки edge.ConnectedNode.State = Node.Status.marked_to_process; bufer.Enqueue(edge.ConnectedNode); ColorPrint($"Вершина {edge.ConnectedNode.ID} идет в очередь"); } } //КРАСНЫЙ - элемент обработан element.State = Node.Status.processed; } } ColorPrint("Поиск завершен"); Console.WriteLine(); return(isFound); }
/// <summary>Меню передвижения вершины</summary> /// <param name="graph">Граф</param> /// <param name="menu">Содержание меню</param> private static void MoveMenu(Graph graph, string[] menu) { string menuMessage = MenuMessage(menu); int id = NumberInput(messages[Messages.EnterNumber], 0, graph.Count - 1, false); graph.FindNode(id).State = Node.Status.marked_to_process; Print(graph, false); bool isExit = false; while (!isExit) { int input = NumberInput(menuMessage, 0, menu.Length - 1); switch (input) { case 1: //Left GraphPrinter.MoveNode(id, -1, 0); break; case 2: //Right GraphPrinter.MoveNode(id, 1, 0); break; case 3: //Down GraphPrinter.MoveNode(id, 0, 1); break; case 4: //Up GraphPrinter.MoveNode(id, 0, -1); break; case 5: //change node Print(graph); id = NumberInput(messages[Messages.EnterNumber], 0, graph.Count - 1, false); graph.FindNode(id).State = Node.Status.marked_to_process; break; case 0: //exit isExit = true; break; } Console.Clear(); Print(graph, false); } }
/// <summary>Создает заранее зарадкоженый граф (для наглядности ДЗ)</summary> /// <returns>Созданный граф</returns> private static Graph CreatePredefinedGraph() { const int ELEMENTS_PREDEF = 15; Graph newGraph = new Graph(); //Добавляем в граф вершины for (int i = 0; i < ELEMENTS_PREDEF; i++) { newGraph.AddNode(i); } //Предварительное создание массива координат вершин GraphPrinter.CreateGraphInfo(newGraph); //Расставляем вершины так как нам нужно int[,] coords = { { 32, 0 }, { 16, 3 }, { 32, 4 }, { 48, 2 }, { 6, 7 }, { 24, 7 }, { 36, 8 }, { 54, 7 }, { 7, 11 }, { 22, 12 }, { 36, 12 }, { 54, 12 }, { 18, 15 }, { 30, 15 }, { 34, 19 } }; for (int i = 0; i < ELEMENTS_PREDEF; i++) { GraphPrinter.MoveNode(i, coords[i, 0], coords[i, 1], false); } //Добавляем ребра int[,] edges = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 4 }, { 1, 5 }, { 2, 6 }, { 3, 6 }, { 3, 7 }, { 4, 8 }, { 5, 9 }, { 5, 10 }, { 6, 10 }, { 6, 11 }, { 7, 11 }, { 8, 9 }, { 8, 12 }, { 9, 13 }, { 10, 13 }, { 11, 14 }, { 13, 14 }, { 12, 14 } }; for (int i = 0; i < edges.Length / 2; i++) { newGraph.AddEdge(edges[i, 0], edges[i, 1], rnd.Next(MAX_WEIGHT)); } return(newGraph); }
/// <summary>Меню работы с графом</summary> /// <param name="graph">Граф</param> /// <param name="menu">Содержание меню</param> private static Graph GraphMenu(Graph graph, string[] menu) { string menuMessage = MenuMessage(menu); Print(graph); bool isExit = false; while (!isExit) { int input = NumberInput(menuMessage, 0, menu.Length - 1); switch (input) { case 1: //add edge between two nodes Print(graph); int firstID = NumberInput(messages[Messages.EnterNumber], 0, graph.Count - 1, false); int secondID = NumberInput(messages[Messages.EnterNumber], 0, graph.Count - 1, false); int weight = NumberInput(messages[Messages.EnterNumber], 0, MAX_WEIGHT - 1, false); graph.AddEdge(firstID, secondID, weight); Print(graph); break; case 2: //add edge between two nodes Print(graph); firstID = NumberInput(messages[Messages.EnterNumber], 0, graph.Count - 1, false); secondID = NumberInput(messages[Messages.EnterNumber], 0, graph.Count - 1, false); graph.RemoveEdge(firstID, secondID); Print(graph); break; case 3: //change two nodes Print(graph); firstID = NumberInput(messages[Messages.EnterNumber], 0, graph.Count - 1, false); secondID = NumberInput(messages[Messages.EnterNumber], 0, graph.Count - 1, false); GraphPrinter.ChangeTwoNodes(firstID, secondID); Print(graph); break; case 4: //move one node Print(graph); MoveMenu(graph, moveMenu); break; case 5: //create random graph Print(graph); graph = CreateGraph(true, elements); break; case 6: //return to default graph Print(graph); graph = CreateGraph(false); break; case 0: //exit isExit = true; break; } Console.Clear(); Print(graph); } return(graph); }
/// <summary>Вызов печати графаы</summary> /// <param name="graph"></param> /// <param name="isColorsClear"></param> private static void Print(Graph graph, bool isColorsClear = true) { Console.Clear(); GraphPrinter.ClearText(); GraphPrinter.Print(graph, isColorsClear); }