- •Курсовая работа по дисциплине «Алгоритмы и структуры данных» Тема: Реализация алгоритма поиска минимального остова на основе алгоритма Краскала
- •Задание на курсовую работу
- •Введение
- •Алгоритм Крускала и определение графа
- •2. Структура ребра
- •3. Структура графа
- •4. Сортировка пузырьком
- •5. Обход графа
- •6. Чтение матрицы смежности из файла
- •7. Список смежности
- •8. Матрица инцидентности
- •9. Сортировка ребер в лексикографическом порядке
- •10. Оценка общей временной сложности
- •Заключение
- •Список использованных источников
- •Приложение а. Код программы
- •Приложение в пример работы программы
10. Оценка общей временной сложности
Таблица 1
Функция |
Временная сложность |
Создание графа (массив рёбер) |
O(n) |
Обработка |
O(n) |
Сортировка ребер по возрастанию весов |
O(n log f(n) ) |
Создание минимального остовного дерева |
O(1) |
Сортировка ребер в лексикографическом порядке |
O(n^2) |
Вывод минимального остовного дерева |
O(n) |
Заключение
В результате работы алгоритма получается минимальное остовное дерево, которое является подмножеством исходного графа и содержит все вершины исходного графа. Вес минимального остовного дерева будет минимальным среди всех возможных остовных деревьев данного графа.
В заключении курсовой работы можно отметить, что успешная реализация алгоритма Крускала позволила найти минимальное остовное дерево в графе. Реализация этого алгоритма подтверждает знания в области сортировки (используя сортировку пузырьком для ребер), обхода графов (через функции Find и Merge для определения и добавления ребер в остовное дерево) и хранения графов (в данном случае, представление графа в виде массива родительских вершин).
Список использованных источников
Дискретная математика: учебник для студентов вузов / С. Н. Поздняков, С. В. Рыбин. Москва: Издательский центр «Академия», 2008. 448 с.
Marcoutte.me. URL: https://markoutte.me/students/algos-final-task/
Ru.Wikipedia.org.URL: https://ru.wikipedia.org/wiki/
Приложение а. Код программы
#include <iostream>
#include <windows.h>
#include <fstream>
#include <string>
#include <conio.h>
#include <sstream>
using namespace std;
void ShowMenu(int str)
{
cout << "Выберите действие:" << endl;
printf("%s1. Вывод списка смежности\n", str == 1 ? " => " : " ");
printf("%s2. Вывод матрицы инцидентности\n", str == 2 ? " => " : " ");
printf("%s3. Обход в глубину\n", str == 3 ? " => " : " ");
printf("%s4. Обход в ширину\n", str == 4 ? " => " : " ");
printf("%s5. Алгоритм Крускала\n", str == 5 ? " => " : " ");
printf("%s6. Выход\n", str == 6 ? " => " : " ");
}
struct Node
{
int data;
Node* next;
Node(int data)
{
this->data = data;
this->next = nullptr;
}
};
//Ребро
struct Edge
{
int first, end; //вершины
int weight; //вес
};
//Функция для чтения матрицы смежности из файла
int readAdjacencyMatrix(const string& filename, int matrix[50][50], string graph[50], bool correct)
{
int size = 0;
string s;
ifstream file(filename);
if (file.is_open())
{
getline(file, s);
istringstream iss(s);
while (iss >> graph[size]) {
size++;
}
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
file >> matrix[i][j];
if (matrix[i][j] < 0)
{
cout << "Выход за границы допустимых значений";
correct = false;
return 0;
}
}
}
file.close();
}
else
{
cout << "Невозможно открыть файл" << endl;
}
return size;
}
//Проверка на симметричность
int isCorrectMatrix(int matrix[50][50], int ver, bool correct)
{
int edge = 0;
for (int i = 0; i < ver; i++) //все элементы на главной диагонали = 0
{
if (matrix[i][i] != 0 || matrix[i][i] > 1023) //диапазон веса = [0;1023]
{
cout << "Выход за границы допустимых значений";
correct = false;
return 0;
}
}
for (int i = 0; i < ver; i++)
{
for (int j = i + 1; j < ver; j++)
{
if (matrix[i][j] != matrix[j][i] || matrix[i][j] > 1023 || matrix[i][j] < 0)
{
cout << "Ошибка ввода матрицы";
correct = false;
return 0;
}
else if (matrix[i][j] != 0)
{
edge++;
}
}
}
return edge;
}
//Функция для поиска корня поддерева
int findParent(int parent[], int i) {
while (parent[i] != -1)
i = parent[i];
return i;
}
//Функция для объединения двух подмножеств
void unionSets(int parent[], int x, int y) {
int rootX = findParent(parent, x);
int rootY = findParent(parent, y);
parent[rootX] = rootY;
}
class List
{
public:
Node* head, * tail;
public:
List()
{
this->head = this->tail = nullptr;
}
~List()
{
while (head != nullptr)
pop_front();
}
Node* push_back(int data)
{
Node* ptr = new Node(data);
if (tail != nullptr)
tail->next = ptr;
if (head == nullptr)
head = ptr;
tail = ptr;
return ptr;
}
void pop_front()
{
if (head == nullptr) return;
Node* ptr = head->next;
delete head;
head = ptr;
}
};
//Граф
struct Graph
{
int V, E; //кол-во вершин и ребер
Edge* edges; //массив ребер
List* adjacencyList; //массив связных списков
};
struct Queue
{
Node* front, * rear;
Queue()
{
this->front = this->rear = nullptr;
}
~Queue()
{
while (front != nullptr)
dequeue();
}
void enqueue(int data)
{
Node* ptr = new Node(data);
if (rear != nullptr)
rear->next = ptr;
if (front == nullptr)
front = ptr;
rear = ptr;
}
void dequeue()
{
if (front == nullptr) return;
Node* ptr = front->next;
delete front;
front = ptr;
if (front == nullptr)
rear = nullptr;
}
bool isEmpty() const
{
return front == nullptr;
}
int getFront() const
{
if (front != nullptr)
return front->data;
return -1;
}
};
//Вывод списка смежности
void printAdjacencyList(const Graph& graph, const string* vertex)
{
cout << "Текущий список смежности:" << endl;
for (int i = 0; i < graph.V; ++i)
{
cout << vertex[i] << ": ";
Node* current = graph.adjacencyList[i].head;
while (current != nullptr)
{
cout << vertex[current->data] << " ";
current = current->next;
}
cout << endl;
}
}
//Вывод матрицы инцидентности
void printIdentityMatrix(const Graph& graph)
{
cout << "Матрица инцидентности:" << endl;
for (int i = 0; i < graph.V; ++i)
{
for (int j = 0; j < graph.E; ++j)
{
if (i == graph.edges[j].first || i == graph.edges[j].end)
cout << "1 ";
else
cout << "0 ";
}
cout << endl;
}
}
// Функция для сравнения по лексикографическому порядку
bool VertexAlpha(const Edge& e1, const Edge& e2, const string* vertex) {
if (vertex[e1.first] > vertex[e2.first] || (vertex[e1.first] == vertex[e2.first] && vertex[e1.end] > vertex[e2.end]))
return true;
return false;
}
//Сортировка пузырьком
void bubbleSort(Edge arr[], int n)
{
for (int i = 0; i < n - 1; ++i)
{
for (int j = 0; j < n - i - 1; ++j)
{
if (arr[j].weight > arr[j + 1].weight)//сравнение по весу
{
Edge temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//Cортировка по лексикографическому порядку
void sortABC(Edge arr[], int n, const string* vertex) {
for (int i = 0; i < n - 1; ++i) {
for (int j = 0; j < n - i - 1; ++j) {
if (VertexAlpha(arr[j], arr[j + 1], vertex)) {
Edge temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//Алгоритм Крускала
void AlgKruscal(Graph& graph, const string* vertex) {
bubbleSort(graph.edges, graph.E);
int* parent = new int[graph.V];
fill(parent, parent + graph.V, -1);
Edge* resultEdges = new Edge[graph.V - 1];
int resultEdgesCount = 0;
cout << "Минимальный остов:" << endl;
int totalWeight = 0;
for (int i = 0; i < graph.E; ++i) {
int root1 = findParent(parent, graph.edges[i].first);
int root2 = findParent(parent, graph.edges[i].end);
if (root1 != root2) {
resultEdges[resultEdgesCount++] = graph.edges[i];
unionSets(parent, root1, root2);
totalWeight += graph.edges[i].weight;
}
}
//cортировка рёбер в алфавитном порядке
sortABC(resultEdges, resultEdgesCount, vertex);
//Вывод рёбер остовного дерева в алфавитном порядке
for (int i = 0; i < resultEdgesCount; ++i) {
cout << vertex[resultEdges[i].first] << " " << vertex[resultEdges[i].end] << " " << resultEdges[i].weight << " " << endl;
}
cout << "Длина минимального остова: ";
cout << totalWeight << endl;
//oсвобождение памяти
delete[] parent;
delete[] resultEdges;
}
//Доп функция обхода в глубину
void add_DFS(const Graph& graph, int v, bool visited[], const string* vertex) {
visited[v] = true;
cout << vertex[v] << " ";
Node* current = graph.adjacencyList[v].head;
while (current != nullptr) {
if (!visited[current->data]) {
add_DFS(graph, current->data, visited, vertex);
}
current = current->next;
}
}
//Функция для полного обхода в глубину (DFS)
void depthFirstSearch(const Graph& graph, int startVertex, const string* vertex) {
bool* visited = new bool[graph.V];
fill(visited, visited + graph.V, false);
cout << "Результат обхода DFS с " << vertex[startVertex] << ": ";
add_DFS(graph, startVertex, visited, vertex);
cout << endl;
delete[] visited;
}
//Функция для полного обхода в ширину (BFS)
void breadthFirstSearch(const Graph& graph, int startVertex, const string* vertex) {
bool* visited = new bool[graph.V];
fill(visited, visited + graph.V, false);
cout << "Результат обхода BFS с " << vertex[startVertex] << ": ";
Queue q;
visited[startVertex] = true;
q.enqueue(startVertex);
while (!q.isEmpty()) {
int v = q.getFront();
q.dequeue();
cout << vertex[v] << " ";
Node* current = graph.adjacencyList[v].head;
while (current != nullptr) {
if (!visited[current->data]) {
visited[current->data] = true;
q.enqueue(current->data);
}
current = current->next;
}
}
cout << endl;
delete[] visited;
}
// Функция для преобразования символа в номер вершины
int charToInt(string vertex[], int size, string c) {
for (int i = 0; i < size; ++i) {
if (vertex[i] == c) {
return i;
}
}
return -1;
}
int main()
{
setlocale(0, "");
int str = 1, ind = 6, usl = 0, com;
Graph graph;
string filename = "file.txt";
int matrix[50][50];
string vertex[50];
bool correct = true;
int edge = 0, ver = 0;
while (usl == 0) {
system("cls");
cout << "КУРСОВАЯ РАБОТА: Алгоритм Крускала" << endl;
ver = readAdjacencyMatrix(filename, matrix, vertex, correct);
edge = isCorrectMatrix(matrix, ver, correct);
if (!ver || !edge || correct == false)
{
cout << endl << "Неверная запись в файле" << endl;
return 0;
}
else
{
cout << "Матрица смежности, полученная из файла:" << endl;
for (int i = 0; i < ver; i++)
{
cout << vertex[i] << " ";
}
cout << endl;
for (int i = 0; i < ver; i++)
{
for (int j = 0; j < ver; j++)
{
cout << matrix[i][j] << " ";
}
cout << endl;
}
graph.V = ver;
graph.E = edge;
graph.edges = new Edge[edge];
graph.adjacencyList = new List[ver];
int edgeIndex = 0;
for (int i = 0; i < graph.V; ++i)
{
for (int j = i + 1; j < graph.V; ++j)
{
if (matrix[i][j] != 0)
{
// Добавляем ребро в массив рёбер
graph.edges[edgeIndex].first = i;
graph.edges[edgeIndex].end = j;
graph.edges[edgeIndex].weight = matrix[i][j];
// Добавляем в список смежности для обеих вершин
graph.adjacencyList[i].push_back(j);
graph.adjacencyList[j].push_back(i);
++edgeIndex;
}
}
}
cout << '\n';
ShowMenu(str);
com = _getch();
if (com >= '1' && com <= '6') str = com - '0';
if (com == 224) {
com = _getch();
switch (com) {
case 72:
str = (str - 1 + 5) % 6 + 1;
break;
case 80:
str = (str - 1 + 1) % 6 + 1;
break;
}
}
if (com == 13)
{
switch (str)
{
case 1://Вывод списка смежности
{
system("cls");
printAdjacencyList(graph, vertex);
system("Pause");
break;
}
case 2://Вывод матрицы инцидентности
{
system("cls");
printIdentityMatrix(graph);
system("Pause");
break;
}
case 3://Обход в глубину
{
string startVertex;
cout << endl << "С какой вершины начать обход? (";
for (int i = 0; i < graph.V - 1; ++i) {
cout << vertex[i] << ", ";
}
cout << vertex[graph.V - 1] << "):"<<endl;
cin >> startVertex;
int startVertexIndex = charToInt(vertex, graph.V, startVertex);
if (startVertexIndex != -1)
{
// Вызов обхода в глубину (DFS)
depthFirstSearch(graph, startVertexIndex, vertex);
}
else
{
cout << "Неверный ввод" << endl;
}
system("Pause");
break;
}
case 4://Обход в ширину
{
string startVertex;
cout << endl << "С какой вершины начать обход? (";
for (int i = 0; i < graph.V - 1; ++i) {
cout << vertex[i] << ", ";
}
cout << vertex[graph.V - 1] << "):" << endl;
cin >> startVertex;
int startVertexIndex = charToInt(vertex, graph.V, startVertex);
if (startVertexIndex != -1)
{
breadthFirstSearch(graph, startVertexIndex, vertex);
}
else
{
cout << "Неверный ввод" << endl;
}
system("Pause");
break;
}
case 5://Алгоритм Крускала
{
system("cls");
AlgKruscal(graph, vertex);
system("pause");
break;
}
case 6://Выход
{
system("cls");
return 0;
break;
}
}
}
}
}
}
