13. Определить количество связных компонент в графе и сформировать
множества вершин, образующих отдельные связные компоненты
Сделай подробное пояснение кода
Решение
```
import tkinter as tk
from tkinter import messagebox
from collections import defaultdict, deque
import matplotlib.pyplot as plt
import networkx as nx

class Graph:
    def __init__(self):
        self.graph = defaultdict(list)

    def add_edge(self, u, v):
        self.graph[u].append(v)
        self.graph[v].append(u)

    def bfs(self, start, visited):
        queue = deque([start])
        component = set()

        while queue:
            node = queue.popleft()
            if node not in visited:
                visited.add(node)
                component.add(node)
                for neighbor in self.graph[node]:
                    if neighbor not in visited:
                        queue.append(neighbor)
        
        return component

    def find_connected_components(self):
        visited = set()
        components = []

        for vertex in self.graph:
            if vertex not in visited:
                component = self.bfs(vertex, visited)
                components.append(component)

        return components

    def draw_graph(self):
        G = nx.Graph()

        for u in self.graph:
            for v in self.graph[u]:
                G.add_edge(u, v)

        pos = nx.spring_layout(G)
        nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=500, font_size=10, font_color='black', font_weight='bold')
        plt.show()


class Application(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Связные компоненты графа")
        self.geometry("500x350")

        self.graph = Graph()
        self.create_widgets()

    def create_widgets(self):
        self.input_label = tk.Label(self, text="Введите ребра (u, v):")
        self.input_label.pack()

        self.input_entry = tk.Entry(self)
        self.input_entry.pack()

        self.add_button = tk.Button(self, text="Добавить ребро", command=self.add_edge)
        self.add_button.pack()

        self.find_button = tk.Button(self, text="Найти связные компоненты", command=self.find_components)
        self.find_button.pack()

        self.draw_button = tk.Button(self, text="Нарисовать граф", command=self.draw_graph)
        self.draw_button.pack()

        self.result_text = tk.Text(self, height=10, width=50)
        self.result_text.pack()

    def add_edge(self):
        edges = self.input_entry.get()
        edge_list = edges.split(',')
        if len(edge_list) == 2:
            try:
                u = int(edge_list[0].strip())
                v = int(edge_list[1].strip())
                self.graph.add_edge(u, v)
                self.input_entry.delete(0, tk.END)
            except ValueError:
                messagebox.showerror("Ошибка", "Введите корректные значения!")
        else:
            messagebox.showerror("Ошибка", "Введите два значения через запятую!")

    def find_components(self):
        components = self.graph.find_connected_components()
        self.result_text.delete(1.0, tk.END)

        if components:
            self.result_text.insert(tk.END, f"Количество связных компонент: {len(components)}\n")
            for i, component in enumerate(components):
                self.result_text.insert(tk.END, f"Компонента {i + 1}: {component}\n")
        else:
            self.result_text.insert(tk.END, "Граф пуст.")

    def draw_graph(self):
        self.graph.draw_graph()


if __name__ == "__main__":
    app = Application()
    app.mainloop()
```