Website Summary Files Commits Branches @commit @diff @stats
path: root / Practicum / pract08 / readme.md
blob: c383a5e89a53ca1650810a815148e7a57a37dadf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# Практикум №8 по Структури от данни, спец. ИС

На семинари вече сте разгледали балансирани дървета. Едни много известни балансирани дървета са именно т.нар. Red-Black Trees. Те работят по малко по-различен начил от AVL. 
Балансираността тук не е дефинирана чрез balance factor, а чрез редица свойства, които винаги трябва да са в сила (така наречения **инвариант**).

Инвариантът на Red-Black Tree гласи:
1) Всеки възел има цвят - черен или червен

2) Всеки NIL възел (празен възел) е черен

3) Червен възел не може да има червен наследник

4) Всеки път от произволен връх до всички NIL елементи в поддървото му трябва да преминава през равен брой черни върхове

5) Коренът винаги е черен (не е задължително, но често се прилага)

* Следствие: Ако произволен връх има точно 1 наследник, то той е червен

Балансирането отново се извършва по време на самите операции за добавяне и премахване. Отново се разчита на принципа на ротациите, но също така има случаи в които вместо ротация се предпочита обикновенно преоцветяване на върховете.

[Визуализация на RB tree](https://ds2-iiith.vlabs.ac.in/exp/red-black-tree/red-black-tree-oprations/simulation/redblack.html)

На базата на червено-черното дърво са имплеметирани 2 от най-използваните структури от данни - std::set и std::map.

## `std::set`
std::set представлява дърво от уникални стойности, наречени ключове. 
При създаване на обект от тип `std::set`, могат да се зададат три типа параметри:

```c++
template<
    class Key,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<Key>
> class set;
```
- **Key**  
  Типът на обектите, които ще се съхраняват в множеството.

- **Compare**  
  Функция или обект за сравнение, която определя критерия за сравнение между елементите. Това влияе върху начина, по който множеството подрежда елементите.  
  *По подразбиране:* Използва се стандартният оператор за сравнение за съответния тип (`std::less<Key>`).

- **Allocator**  
  Механизъм за управление на паметта, който определя как ще се разпределя и освобождава паметта за елементите в множеството.  
  *По подразбиране:* Използва се стандартният алокатор `std::allocator<Key>`.
  
Поддържа следните методи:
- **Добавяне на елементи**: `set.insert(value)` - O(logN) (връща pair<iterator, bool>);
- **Премахване на елементи**: `set.erase(value)`- O(logN) (връща колко елементи са били премахнати);
- **Премахване на елементи чрез итератор**: `set.erase(iterator)`- Θ(1) / O(logN) (връща итератор към следващия елемент);
- **Търсене на елементи**: `set.find(value);` - O(logN) (връща итератор или `set.end()`, ако не е намерен);
- **Проверка на размера**: `set.size()` - O(1)

```c++
template<
    class Key,
    class T,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<std::pair<const Key, T>>

> class map;
```

## `std::map`
std::map представлява дърво от двойки от ключ и стойност. Всеки ключ е уникален. 

- **Key**  
  Типът на обектите, които ще се съхраняват в множеството.

- **T**  
  Типът на обектите, които ще се съхраняват в множеството.

- **Compare**  
  Обект или функция за сравнение, която определя как ключовете се подреждат в контейнера.
  *По подразбиране:* Използва се стандартният оператор за сравнение за съответния тип на ключа (`std::less<Key>`).

- **Allocator**  
  Механизъм за управление на паметта, който определя как ще се разпределя и освобождава паметта за елементите в множеството.  
  *По подразбиране:* Използва се стандартният алокатор `std::allocator<std::pair<const Key, T>>`.

  Поддържа следните операции:
- **Добавяне на елементи**: `map[key] = value`- O(logN)
- **Достъп до елементи**: `map[key]` - O(logN)
- **Премахване на елементи**: `map.erase(key)` - O(logN) (връща колко елементи са били премахнати)
- **Премахване на елементи чрез итератор**: `map.erase(iterator)` - Θ(1) / O(logN) (връща итератор към следващия елемент)
- **Търсене на елементи**: `map.find(key)` - O(logN) (връща итератор или `map.end()`, ако не е намерен)
- **Проверка на размера**: `map.size()` - O(1)

---
[Линк към задачите](https://leetcode.com/problem-list/a6culvq1/)