Списки, деревья, графы — в чём разница и где применяются
Список, дерево, граф — три кита, на которых стоит любая алгоритмическая логика.
Они встречаются везде: от браузеров и маршрутов до очередей, файлов и рекомендаций.Разберём их с нуля: как устроены, где применяются и как реализуются.
 Список (List)
Линейная структура: каждый элемент знает своего соседа.
- Очереди задач
 - Буферы сообщений
 - История действий
 
| Операция | Сложность |
|----------------|-----------|
| Поиск по значению | O( n ) |
| Добавление в начало | O(1) |
| Добавление в конец (односвязный) | O( n ) |
---
 Дерево (Tree)
Иерархическая структура: каждый узел может иметь несколько потомков.
Особый случай — двоичное дерево поиска (BST).
Пример BST:
		Код:
	
	      10
      /  \
     5    15
    / \     \
   2   7     20
	- Файловые системы
 - Словари и индексы
 - Сжатие данных (Huffman)
 - DOM-дерево
 
| Операция | Среднее | Худшее (несбаланс) |
|------------|--------- |------------------------|
| Поиск | O(log n) | O ( n ) |
| Вставка | O(log n) | O ( n ) |
| Удаление | O(log n) | O ( n ) |
---
 Граф (Graph)
Гибкая структура, где вершины могут быть связаны как угодно.
Может содержать циклы, направленные и ненаправленные связи.
Пример графа:
Граф — это множество вершин (nodes) и рёбер (edges), которые соединяют эти вершины.
Бывает:
- Ориентированный (стрелки: A → B)
 - Неориентированный (двусторонние связи: A — B)
 
- Навигация и маршруты
 - Социальные сети (связи)
 - Анализ зависимостей
 - Поиск путей (Dijkstra, A*)
 
| Операция | Сложность |
|------------------ |---------------- |
| Добавление вершины | O(1) |
| Добавление ребра | O(1) |
| Поиск соседей | O(k) (где k — степень вершины) |
 Сравнение структур
| Свойство | Список | Дерево | Граф | 
|---|---|---|---|
| Форма | Линейная | Иерархия | Сеть | 
| Связи | 1 на 1 | 1 ко многим | Произвольные | 
| Циклы | Нет | Нет | Да | 
| Поиск | O ( n ) | O(log n) | Зависит от алгоритма | 
| Применение | Очереди, undo | ФС, индексы | Маршруты, связи | 
---
 Вывод
 Списки — простые и линейные, отлично подходят для очередей и стеков
 Деревья — иерархии и быстрый поиск
 Графы — универсальный инструмент для сетей и навигации
⚙ Операции: поиск, вставка и удаление в списках, деревьях и графах
Теперь добавим в практику: не просто структура, а как с ней работать.Ниже — реализации трёх базовых операций для списка, дерева и графа на Python и C.
---
 Односвязный список
		Python:
	
	class Node:
    def __init__(self, val):
        self.val = val
        self.next = None
def insert(head, value):
    new_node = Node(value)
    new_node.next = head
    return new_node  # новый head
def find(head, value):
    while head:
        if head.val == value:
            return True
        head = head.next
    return False
def delete(head, value):
    dummy = Node(0)
    dummy.next = head
    prev, curr = dummy, head
    while curr:
        if curr.val == value:
            prev.next = curr.next
            break
        prev, curr = curr, curr.next
    return dummy.next
	
		C:
	
	typedef struct Node {
    int val;
    struct Node* next;
} Node;
Node* insert(Node* head, int value) {
    Node* n = malloc(sizeof(Node));
    n->val = value;
    n->next = head;
    return n;
}
int find(Node* head, int value) {
    while (head) {
        if (head->val == value) return 1;
        head = head->next;
    }
    return 0;
}
Node* delete(Node* head, int value) {
    Node dummy = {.next = head};
    Node* prev = &dummy;
    Node* curr = head;
    while (curr) {
        if (curr->val == value) {
            prev->next = curr->next;
            free(curr);
            break;
        }
        prev = curr;
        curr = curr->next;
    }
    return dummy.next;
}
	
 Двоичное дерево поиска (BST)
		Python:
	
	class Node:
    def __init__(self, key):
        self.key = key
        self.left = self.right = None
def insert(root, key):
    if not root:
        return Node(key)
    if key < root.key:
        root.left = insert(root.left, key)
    else:
        root.right = insert(root.right, key)
    return root
def search(root, key):
    if not root or root.key == key:
        return root
    return search(root.left, key) if key < root.key else search(root.right, key)
def delete(root, key):
    if not root:
        return None
    if key < root.key:
        root.left = delete(root.left, key)
    elif key > root.key:
        root.right = delete(root.right, key)
    else:
        if not root.left:
            return root.right
        if not root.right:
            return root.left
        temp = root.right
        while temp.left:
            temp = temp.left
        root.key = temp.key
        root.right = delete(root.right, temp.key)
    return root
	
		C:
	
	typedef struct Node {
    int key;
    struct Node *left, *right;
} Node;
Node* new_node(int key) {
    Node* node = malloc(sizeof(Node));
    node->key = key;
    node->left = node->right = NULL;
    return node;
}
Node* insert(Node* root, int key) {
    if (!root) return new_node(key);
    if (key < root->key)
        root->left = insert(root->left, key);
    else
        root->right = insert(root->right, key);
    return root;
}
Node* search(Node* root, int key) {
    if (!root || root->key == key) return root;
    return (key < root->key) ? search(root->left, key) : search(root->right, key);
}
Node* min_value_node(Node* node) {
    while (node->left) node = node->left;
    return node;
}
Node* delete(Node* root, int key) {
    if (!root) return NULL;
    if (key < root->key)
        root->left = delete(root->left, key);
    else if (key > root->key)
        root->right = delete(root->right, key);
    else {
        if (!root->left) {
            Node* temp = root->right;
            free(root); return temp;
        } else if (!root->right) {
            Node* temp = root->left;
            free(root); return temp;
        }
        Node* temp = min_value_node(root->right);
        root->key = temp->key;
        root->right = delete(root->right, temp->key);
    }
    return root;
}
	---
 Граф (ориентированный, список смежности)
		Python:
	
	graph = {}
def add_vertex(v):
    if v not in graph:
        graph[v] = []
def add_edge(u, v):
    graph[u].append(v)
def remove_edge(u, v):
    if v in graph[u]:
        graph[u].remove(v)
def has_edge(u, v):
    return v in graph.get(u, [])
	
		C:
	
	#define MAX 100
typedef struct Node {
    int vertex;
    struct Node* next;
} Node;
Node* adj[MAX];
void add_edge(int u, int v) {
    Node* n = malloc(sizeof(Node));
    n->vertex = v;
    n->next = adj[u];
    adj[u] = n;
}
int has_edge(int u, int v) {
    Node* cur = adj[u];
    while (cur) {
        if (cur->vertex == v) return 1;
        cur = cur->next;
    }
    return 0;
}
void remove_edge(int u, int v) {
    Node** cur = &adj[u];
    while (*cur) {
        if ((*cur)->vertex == v) {
            Node* tmp = *cur;
            *cur = (*cur)->next;
            free(tmp);
            return;
        }
        cur = &((*cur)->next);
    }
}
	Пример работы с графом:
		C:
	
	#include <stdio.h>
#include <stdlib.h>
#define N 4 // кол-во вершин: A, B, C, D
typedef struct Node {
    int vertex;
    struct Node* next;
} Node;
Node* graph[N]; // массив смежности
void add_edge(int from, int to) {
    Node* n = malloc(sizeof(Node));
    n->vertex = to;
    n->next = graph[from];
    graph[from] = n;
}
void print_graph() {
    char labels[] = {'A', 'B', 'C', 'D'};
    for (int i = 0; i < N; i++) {
        printf("%c -> ", labels[i]);
       Node* temp = graph[i];
       while (temp) {
           printf("%c ", labels[temp->vertex]);
           temp = temp->next;
      }
      printf("\n");
   }
}
int main() {
    add_edge(0, 1); // A → B
    add_edge(0, 2); // A → C
    add_edge(1, 3); // B → D
    add_edge(2, 3); // C → D
    print_graph();
    return 0;
}
	---
 Вывод
Знать структуру — мало. Нужно уметь с ней работать:
добавлять, искать, удалять.
 Список — простейшая динамика
 Дерево — база для логарифмичного поиска- ⚙ Граф — мощный инструмент моделирования отношений
 
	