Практика 6(СД)
.docxМинистерство науки и высшего образования Российской Федерации
Федеральное государственное автономное образовательное учреждение
высшего образования
ТОМСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ СИСТЕМ
УПРАВЛЕНИЯ И РАДИОЭЛЕКТРОНИКИ (ТУСУР)
Кафедра комплексной информационной безопасности электронно-вычислительных систем (КИБЭВС)
ГРАФЫ
Отчёт по практической работе №6
по дисциплине «Структуры данных»
Студент гр. 714-1
_______ И.С. Максимов
__.__.2025
Доцент кафедры КИБЭВС
_______ Е.Е. Лунёва
__.__.2025
Томск 2025
Оглавление
Введение 3
Заключение 9
Введение
Целью работы является реализация программы для работы с графами, представляющими систему дорог с переменным числом узлов и рёбер. Программа позволяет вводить графы случайным образом или с клавиатуры и выводить матрицу смежности или весов. Основное внимание уделяется нахождению города с максимальной суммой кратчайших расстояний до остальных городов.
Ход работы
Была создана основная точка входа в программу, реализующую работу с графом и алгоритм Дейстры.
На рисунке 1.1 представлен фрагмент код.
Рисунок
1.1 – Основная точка входа в программу
Реализованный класс Graph. Часть кода, представленная на рисунке 1.2 описывает класс и его конструктор, который создаёт граф с заданным числом вершин.
Рисунок 1.2 – Реализованный класс Graph
AddEdge: добавляет ориентированное ребро с весом.
На рисунке 1.3 представлен фрагмент кода.
Рисунок 1.3 – Метод AddEdge
RandomGraph: генерирует случайный граф с заданным числом ребер и весами.
На рисунке 1.4 представлен фрагмент кода.
Рисунок 1.4 – метод RandomGraph
Метод Display: выводит матрицу весов с подписями.
На рисунке 1.5 представлен фрагмент кода.
Рисунок 1.5 – метод Display
Метод Dijkstra: ищет минимальные расстояния из start во все остальные вершины.
На рисунках 1.6 и 1.7 представлен фрагмент кода.
Рисунок 1.6 – Начало реализации метода Dijkstra
Рисунок 1.7 – Основной цикл метода Dijkstra
Метод MinDistance: служит для нахождения вершины с минимальным dist среди не посещённых.
На рисунках 1.8 представлен фрагмент кода.
Рисунок 1.8 – Метод MinDistance
Метод PrintPath: строит и выводит полный маршрут от стартовой вершины до j в правильном порядке.
На рисунках 1.9 представлен фрагмент кода.
Рисунок 1.9 – Метод PrintPath
Заключение
В ходе работы была успешно реализована программа для анализа графов дорожной сети. Выполнены задачи по вводу, выводу и поиску города с максимальными кратчайшими расстояниями. Это может быть полезным для оптимизации транспортных маршрутов.
Приложение А
Листинг кода:
// Класс программы с точкой входа
class Program
{
static void Main()
{
Console.WriteLine("Введите количество узлов в графе:");
int n = int.Parse(Console.ReadLine()); // ввод количества вершин
Graph graph = new Graph(n); // создаём граф
graph.RandomGraph(); // случайно генерируем ребра (без петель)
graph.Display(); // выводим матрицу смежности
Console.WriteLine($"Введите начальный узел для поиска кратчайших путей (от A до {(char)('A' + n - 1)}):");
char startChar = Char.ToUpper(Console.ReadKey().KeyChar); // строка считывает с клавиатуры одну нажатую клавишу, превращает её в заглавный символ и сохраняет в startChar
Console.WriteLine();
int start = startChar - 'A'; // преобразуем букву в индекс
graph.Dijkstra(start); // запускаем алгоритм Дейкстры
}
}
// Класс Graph описывает граф через матрицу весов
class Graph
{
private int[,] matrix; // матрица весов: matrix[i,j] — вес ребра из i в j (0 = ребро отсутствует)
private int verticesCount; // число вершин графа
public Graph(int n) // конструктор класса Graph
{
verticesCount = n;
matrix = new int[n, n];
// Инициализация матрицы нулями — пока ребер нет
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
matrix[i, j] = 0;
}
// Добавляет ориентированное ребро с весом
public void AddEdge(int from, int to, int weight = 1) // from — индекс начальной вершины ребра. to — индекс конечной вершины ребра. weight — вес ребра
{
matrix [from, to] = weight;
}
// Генерирует случайный граф с заданным числом ребер и весами
public void RandomGraph(int maxWeight = 10)
{
Random rand = new Random();
Console.WriteLine("Введите количество ребер для случайного графа:");
int m = int.Parse(Console.ReadLine());
int added = 0;
while (added < m) // заполняем матрицу
{
int from = rand.Next(verticesCount);
int to = rand.Next(verticesCount);
if (from == to) continue; // Исключаем петли — ребра из вершины в саму себя
int weight = rand.Next(1, maxWeight + 1); // вес ребра от 1 до maxWeight (10)
AddEdge(from, to, weight);
added++;
}
}
// Выводит матрицу весов с подписями
public void Display()
{
Console.Write("\t");
for (int j = 0; j < verticesCount; j++)
Console.Write((char)('A' + j) + "\t"); // подписи столбцов
Console.WriteLine();
for (int i = 0; i < verticesCount; i++)
{
Console.Write((char)('A' + i) + "\t"); // подписи строк
for (int j = 0; j < verticesCount; j++)
Console.Write(matrix[i, j] + "\t"); // веса ребер
Console.WriteLine();
}
}
// Реализация алгоритма Дейкстры, ищет минимальные расстояния из start во все остальные вершины
public void Dijkstra(int start)
{
const int INF = int.MaxValue; // бесконечность для инициализации
int[] dist = new int[verticesCount]; // массив расстояний от start
bool[] visited = new bool[verticesCount]; // пометки посещенных вершин
int[] prev = new int[verticesCount]; // массив для восстановления пути
for (int i = 0; i < verticesCount; i++) // изначальная установка данных
{
dist[i] = INF; // расстояния изначально бесконечны
visited[i] = false; // вершины не посещены
prev[i] = -1; // предшественник не известен
}
dist[start] = 0; // расстояние от себя до себя равно 0
// Основной цикл. Графы, рёбра, начальная вершина(выбирает пользователь)
for (int count = 0; count < verticesCount - 1; count++) // На каждом шаге мы расширяем область известных кратчайших путей, постепенно находя минимальное расстояние до новых вершин.
{
int u = MinDistance(dist, visited); // выбираем непосещённую вершину с минимальной дистанцией
if (u == -1) break; // если таких нет, останавливаемся
visited[u] = true; // отмечаем вершину посещённой
// Релаксация — пытаемся улучшить расстояния до соседей вершины u
for (int v = 0; v < verticesCount; v++)
{
if (!visited[v] && matrix[u, v] > 0 && dist[u] != INF
&& dist[u] + matrix[u, v] < dist[v])
{
dist[v] = dist[u] + matrix[u, v];
prev[v] = u; // запоминаем путь
}
}
} // на выходе массив расстояний и массив предшественников
// Вывод результатов: кратчайшие расстояния и пути
Console.WriteLine($"Кратчайшие пути из вершины {(char)('A' + start)}:");
for (int i = 0; i < verticesCount; i++)
{
if (i == start) continue; // путь из вершины в себя не выводим
if (dist[i] == INF)
Console.WriteLine($"{(char)('A' + start)} -> {(char)('A' + i)}: пути нет");
else
{
Console.Write($"{(char)('A' + start)} -> {(char)('A' + i)} : {dist[i]} - длина пути. Маршрут пути: ");
PrintPath(prev, i); // показывает маршрут
Console.WriteLine();
}
}
}
// Метод для нахождения вершины с минимальным dist среди непосещённых
private int MinDistance(int[] dist, bool[] visited)
{
int min = int.MaxValue;
int minIndex = -1;
for (int v = 0; v < verticesCount; v++) // перебирает вершины
{
if (!visited[v] && dist[v] <= min) // проверка посещаемости и меньше ли найденного минимума
{
min = dist[v];
minIndex = v;
}
}
return minIndex;
}
// Рекурсивный вывод пути от start к вершине j
private void PrintPath(int[] prev, int j)
{
if (prev[j] == -1) // если предшественник вершины j не определён (равен -1), значит, мы достигли начальной вершины.
{
Console.Write((char)('A' + j));
return;
}
PrintPath(prev, prev[j]);
Console.Write(" -> " + (char)('A' + j)); // происходит последовательный вывод пути от начальной вершины до текущей вершины с индексом j.
}
}
