Indeksēts binārais koks

Indeksēts binārais koks

Es saskāros ar Ŕāda veida problēmu. Ir nepiecieÅ”ams ieviest datu uzglabāŔanas konteineru, kas nodroÅ”ina Ŕādas funkcijas:

  • ievietot jaunu elementu
  • noņemt elementu pēc sērijas numura
  • iegÅ«t elementu pēc kārtas skaitļa
  • dati tiek glabāti sakārtotā veidā

Dati tiek nepārtraukti pievienoti un noņemti, struktÅ«rai jānodroÅ”ina ātrs darbÄ«bas ātrums. Sākumā es mēģināju ieviest Ŕādu lietu, izmantojot standarta konteinerus no std. Å is ceļŔ nav vainagojies panākumiem un nāca sapratne, ka vajag kaut ko Ä«stenot paÅ”am. VienÄ«gais, kas ienāca prātā, bija izmantot bināro meklēŔanas koku. Jo tas atbilst prasÄ«bai par ātru datu ievietoÅ”anu, dzēŔanu un glabāŔanu sakārtotā veidā. Atliek tikai izdomāt, kā indeksēt visus elementus un pārrēķināt indeksus, kad koks mainās.

struct node_s {    
    data_t data;

    uint64_t weight; // вес узла

    node_t *left;
    node_t *right;

    node_t *parent;
};

Rakstā bÅ«s vairāk attēlu un teorijas nekā koda. Kodu var apskatÄ«t zemāk esoÅ”ajā saitē.

Svars

Lai to panāktu, koks tika nedaudz pārveidots, tika pievienota papildu informācija svaru mezgls. Mezgla svars ir Ŕī mezgla pēcnācēju skaits + 1 (viena elementa svars).

Funkcijas mezgla svara iegūŔanai:

uint64_t bntree::get_child_weight(node_t *node) {
    if (node) {
        return node->weight;
    }

    return 0;
}

Loksnes svars ir attiecīgi vienāds ar 0.

Tālāk pāriesim pie Ŕāda koka piemēra vizuāla attēlojuma. Melns mezgla atslēga tiks parādÄ«ta krāsā (vērtÄ«ba netiks rādÄ«ta, jo tas nav nepiecieÅ”ams), sarkans - mezgla svars, zaļŔ — mezglu indekss.

Kad mūsu koks ir tukŔs, tā svars ir 0. Pievienosim tam saknes elementu:

Indeksēts binārais koks

Koka svars kļūst par 1, saknes elementa svars kļūst par 1. Saknes elementa svars ir koka svars.

Pievienosim vēl dažus elementus:

Indeksēts binārais koks
Indeksēts binārais koks
Indeksēts binārais koks
Indeksēts binārais koks

Katru reizi, kad tiek pievienots jauns elements, mēs ejam uz leju pa mezgliem un palielinām katra nodotā ​​mezgla svara skaitÄ«tāju. Kad tiek izveidots jauns mezgls, tam tiek pieŔķirts svars 1. Ja mezgls ar Ŕādu atslēgu jau pastāv, mēs pārrakstÄ«sim vērtÄ«bu un atgriezÄ«simies lÄ«dz saknei, atceļot visu mÅ«su nodoto mezglu svaru izmaiņas.
Ja mezgls tiek noņemts, mēs ejam uz leju un samazinām nodoto mezglu svaru.

Indeksi

Tagad pāriesim pie mezglu indeksēŔanas. Mezgli nepārprotami neuzglabā savu indeksu, tas tiek aprēķināts, pamatojoties uz mezglu svaru. Ja viņi saglabātu savu indeksu, tad tas bÅ«tu vajadzÄ«gs O (n) laiks atjaunināt visu mezglu indeksus pēc katrām izmaiņām kokā.
Pāriesim pie vizuālā attēlojuma. MÅ«su koks ir tukÅ”s, pievienosim tam 1. mezglu:

Indeksēts binārais koks

Pirmajā mezglā ir indekss 0, un tagad ir iespējami 2 gadījumi. Pirmajā saknes elementa indekss mainīsies, otrajā tas nemainīsies.

Indeksēts binārais koks

Saknē kreisais apakÅ”koks sver 1.

Otrais gadījums:

Indeksēts binārais koks

Saknes indekss nemainījās, jo tās kreisā apakŔkoka svars palika 0.

Mezgla indekss tiek aprēķināts kā tā kreisā apakÅ”koka svars + no vecāka nodotais skaitlis. Kāds ir Å”is skaitlis?, Å is ir indeksa skaitÄ«tājs, sākotnēji tas ir vienāds ar 0, jo saknei nav vecāku. Tad viss ir atkarÄ«gs no tā, kur mēs nolaižamies pie kreisā bērna vai labā. Ja pa kreisi, tad skaitÄ«tājam nekas netiek pievienots. Ja mēs pievienojam paÅ”reizējā mezgla indeksu pareizajam mezglam.

Indeksēts binārais koks

Piemēram, kā tiek aprēķināts elementa indekss ar atslēgu 8 (saknes labais bērns). Tas ir "Saknes indekss" + "mezgla kreisā apakÅ”koka svars ar atslēgu 8" + "1" == 3 + 2 + 1 == 6
Elementa indekss ar taustiņu 6 būs "Saknes indekss" + 1 == 3 + 1 == 4

AttiecÄ«gi ir nepiecieÅ”ams laiks, lai iegÅ«tu un izdzēstu elementu pēc indeksa O (log n), jo, lai iegÅ«tu vēlamo elementu, vispirms tas ir jāatrod (no saknes jāiet uz leju lÄ«dz Å”im elementam).

Dziļums

Pamatojoties uz svaru, varat arÄ« aprēķināt koka dziļumu. NepiecieÅ”ams lÄ«dzsvaroÅ”anai.
Lai to izdarÄ«tu, paÅ”reizējā mezgla svars ir jānoapaļo lÄ«dz pirmajam skaitlim lÄ«dz pakāpei 2, kas ir lielāks vai vienāds ar doto svaru, un jāņem no tā binārais logaritms. Tas dos mums koka dziļumu, pieņemot, ka tas ir lÄ«dzsvarots. Koks tiek lÄ«dzsvarots pēc jauna elementa ievietoÅ”anas. Es nesniegÅ”u teoriju par to, kā lÄ«dzsvarot kokus. Avota kodi nodroÅ”ina lÄ«dzsvaroÅ”anas funkciju.

Kods svara pārvērÅ”anai dziļumā.

/*
 * Возвращает первое число в степени 2, которое больше или ровно x
 */
uint64_t bntree::cpl2(uint64_t x) {
    x = x - 1;
    x = x | (x >> 1);
    x = x | (x >> 2);
    x = x | (x >> 4);
    x = x | (x >> 8);
    x = x | (x >> 16);
    x = x | (x >> 32);

    return x + 1;
}

/*
 * Двоичный логарифм от числа
 */
long bntree::ilog2(long d) {
    int result;
    std::frexp(d, &result);
    return result - 1;
}

/*
 * Вес Šŗ глубине
 */
uint64_t bntree::weight_to_depth(node_t *p) {
    if (p == NULL) {
        return 0;
    }

    if (p->weight == 1) {
        return 1;
    } else if (p->weight == 2) {
        return 2;
    }

    return this->ilog2(this->cpl2(p->weight));
}

Rezultāti

  • notiek jauna elementa ievietoÅ”ana O (log n)
  • elementa dzēŔana pēc sērijas numura notiek O (log n)
  • elementa iegūŔana pēc sērijas numura notiek O (log n)

Ātrums O (log n) Mēs maksājam par to, ka visi dati tiek glabāti sakārtotā veidā.

Es nezinu, kur Ŕāda struktÅ«ra varētu bÅ«t noderÄ«ga. Tikai mÄ«kla, lai vēlreiz saprastu, kā darbojas koki. Paldies par jÅ«su uzmanÄ«bu.

atsauces

Projektā ir testa dati, lai pārbaudÄ«tu darbÄ«bas ātrumu. Koks piepildās 1000000 elementi. Un notiek elementu secÄ«ga dzēŔana, ievietoÅ”ana un izgūŔana 1000000 vienreiz. Tas ir 3000000 operācijas. Rezultāts izrādÄ«jās diezgan labs ~ 8 sekundes.

Avots: www.habr.com

Iegādājieties uzticamu mitināŔanu vietnēm ar DDoS aizsardzÄ«bu, VPS VDS serveriem šŸ”„ Iegādājieties uzticamu tÄ«mekļa vietņu mitināŔanu ar DDoS aizsardzÄ«bu, VPS VDS serveriem | ProHoster