Дърво и кореново дърво
Дървото е свързан граф без цикли. То е специален, но изключително важен вид граф — среща се навсякъде: файловата система, DOM на уеб страница, организационна структура, родословно дърво, дървета за търсене.
Често един от върховете се обявява за специален — наричаме го корен, а дървото — кореново дърво. Тогава можем да говорим за родител и деца: коренът е най-отгоре, а всеки друг връх има точно един родител (този по пътя към корена).
Рекурсивна дефиниция (както е в учебника):
- Един връх
rе кореново дърво с коренr. - Ако
T₁, …, Tₖса коренови дървета с корениr₁, …, rₖ, добавяме нов връхrи ребра от него към всеки коренrᵢ. Получаваме ново кореново дърво с коренr.
Свойства на кореновите дървета
Представяне на коренови дървета
Понеже всеки връх има точно един родител, най-простото представяне е списък на родителите — масив p, в който p[i] е родителят на връх i. Коренът няма родител — записваме му 0.
За дървото от Фиг. 1:
| i (връх) | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| p[i] (родител) | 0 | 1 | 1 | 2 | 2 | 3 |
Двоични дървета
Двоично дърво е кореново дърво, в което всеки връх има най-много две деца — ляво и дясно. Тук редът на децата има значение (лявото е различно от дясното).
Представя се удобно с два масива: left[i] и right[i] — номерата на лявото и дясното дете на връх i (или 0, ако няма).
Двоичните дървета са основата на дърветата за търсене (BST), купищата (heap) и много други структури, които ще срещнеш по-нататък.
Покриващо дърво на граф
Покриващо дърво (spanning tree) на свързан граф е негов подграф, който е дърво и включва всички върхове. С други думи — оставяме само толкова ребра, колкото да държат графа свързан, без нито един цикъл.
Идея за построяване: докато в графа има цикъл, премахни едно ребро от него. Графът остава свързан (цикълът има друг път), но един цикъл изчезва. Когато не остане цикъл — имаме покриващо дърво.
Задача от учебника — листа на дървото
Лист е всеки връх, който не е родител на друг връх. Затова е достатъчно при прочитането на ребрата да отбелязваме кои върхове са родители, а накрая да изведем тези, които НЕ са:
static void Main()
{
int n = int.Parse(Console.ReadLine());
bool[] isParent = new bool[n + 1];
// n-1 ребра: всеки ред е "родител", после "дете"
for (int i = 1; i < n; i++)
{
int u = int.Parse(Console.ReadLine()); // родител
isParent[u] = true;
int v = int.Parse(Console.ReadLine()); // дете (пропускаме)
}
// листата са върховете, които не са родители
for (int i = 1; i <= n; i++)
if (!isParent[i]) Console.Write(i + " ");
Console.WriteLine();
}Какво трябва да запомниш
- Дървото = свързан граф без цикли. Кореновото дърво има отделен корен.
- Дърво с n върха има точно n − 1 ребра.
- Между всеки два върха в дърво има единствен път.
- Представяне: списък на родителите
p[i]— цяло дърво в един масив. - Двоично дърво — всеки връх има ляво и дясно дете; редът има значение.
- Покриващо дърво — свързва всички върхове без цикъл; граф е свързан ⟺ има покриващо дърво.