Structuri de Date Arborescente Implementate Dinamic – Arbore Parțial de Cost Minim


1. Obiectivele lecției:

  • Să înțeleagă conceptul de arbore parțial de cost minim (Minimum Spanning Tree – MST).
  • Să învețe algoritmii clasici pentru determinarea MST: Kruskal și Prim.
  • Să implementeze MST folosind structuri dinamice.

2. Ce este un arbore parțial de cost minim?

  1. Definiție:
    Un arbore parțial de cost minim este un subgraf al unui graf ponderat, conex și neorientat, care:
    • Include toate nodurile grafului.
    • Are cel mai mic cost total posibil (suma ponderilor muchiilor).
    • Este un arbore (nu conține cicluri).
  2. Proprietăți:
    • Un graf poate avea mai mulți MST cu același cost.
    • Numărul muchiilor dintr-un MST este întotdeauna V−1V – 1V−1, unde VVV este numărul de noduri.

3. Aplicații practice ale MST

  • Construcția rețelelor (electricitate, drumuri, internet).
  • Compresia datelor (algoritmul lui Huffman).
  • Optimizarea resurselor într-o organizație.

4. Algoritmi clasici pentru MST

  1. Algoritmul lui Kruskal:
    • Ideea principală: Selectează muchiile grafului în ordine crescătoare a costurilor, asigurându-se că nu se formează cicluri.
    • Pași:
      1. Sortează muchiile grafului după cost.
      2. Adaugă fiecare muchie (în ordine) dacă nu formează un ciclu.
  2. Algoritmul lui Prim:
    • Ideea principală: Construiește MST pornind de la un nod inițial și adăugând muchii de cost minim care conectează noduri noi.
    • Pași:
      1. Alege un nod de pornire.
      2. Selectează muchia de cost minim care leagă un nod inclus cu un nod neinclus.

5. Implementarea algoritmului lui Kruskal


Structura pentru reprezentarea grafului

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

struct Muchie {

    int sursa, destinatie, cost;

};

// Comparator pentru sortarea muchiilor după cost

bool comparaMuchii(const Muchie& a, const Muchie& b) {

    return a.cost < b.cost;

}


Reprezentarea Uniunii și Găsirii (Union-Find)

struct DisjointSet {

    vector<int> parinte, rang;

    DisjointSet(int n) {

        parinte.resize(n);

        rang.resize(n, 0);

        for (int i = 0; i < n; i++) {

            parinte[i] = i;

        }

    }

    int gaseste(int u) {

        if (u != parinte[u]) {

            parinte[u] = gaseste(parinte[u]);

        }

        return parinte[u];

    }

    void uneste(int u, int v) {

        u = gaseste(u);

        v = gaseste(v);

        if (u != v) {

            if (rang[u] < rang[v]) {

                parinte[u] = v;

            } else if (rang[u] > rang[v]) {

                parinte[v] = u;

            } else {

                parinte[v] = u;

                rang[u]++;

            }

        }

    }

};


Algoritmul lui Kruskal

int kruskalMST(vector<Muchie>& muchii, int numarNoduri) {

    sort(muchii.begin(), muchii.end(), comparaMuchii); // Sortează muchiile după cost

    DisjointSet ds(numarNoduri);

    int costTotal = 0;

    vector<Muchie> rezultat;

    for (const auto& muchie : muchii) {

        if (ds.gaseste(muchie.sursa) != ds.gaseste(muchie.destinatie)) {

            rezultat.push_back(muchie);

            costTotal += muchie.cost;

            ds.uneste(muchie.sursa, muchie.destinatie);

        }

    }

    cout << „Muchiile din MST:\n”;

    for (const auto& muchie : rezultat) {

        cout << muchie.sursa << ” – ” << muchie.destinatie << ” : ” << muchie.cost << endl;

    }

    return costTotal;

}


Exemplu de utilizare

int main() {

    int numarNoduri = 4;

    vector<Muchie> muchii = {

        {0, 1, 10},

        {0, 2, 6},

        {0, 3, 5},

        {1, 3, 15},

        {2, 3, 4}

    };

    int costTotal = kruskalMST(muchii, numarNoduri);

    cout << „Costul total al MST: ” << costTotal << endl;

    return 0;

}


6. Implementarea algoritmului lui Prim


Structura pentru reprezentarea grafului

#include <iostream>

#include <vector>

#include <queue>

#include <utility>

using namespace std;

typedef pair<int, int> Pereche; // cost, nod


Algoritmul lui Prim

int primMST(vector<vector<Pereche>>& graf, int numarNoduri) {

    priority_queue<Pereche, vector<Pereche>, greater<Pereche>> pq;

    vector<bool> inclus(numarNoduri, false);

    int costTotal = 0;

    pq.push({0, 0}); // Pornim de la nodul 0 cu cost 0

    while (!pq.empty()) {

        int cost = pq.top().first;

        int nod = pq.top().second;

        pq.pop();

        if (inclus[nod]) continue;

        inclus[nod] = true;

        costTotal += cost;

        for (const auto& vecin : graf[nod]) {

            if (!inclus[vecin.second]) {

                pq.push(vecin);

            }

        }

    }

    return costTotal;

}


Exemplu de utilizare

int main() {

    int numarNoduri = 5;

    vector<vector<Pereche>> graf(numarNoduri);

    graf[0].push_back({2, 1});

    graf[0].push_back({3, 3});

    graf[1].push_back({2, 0});

    graf[1].push_back({5, 2});

    graf[1].push_back({8, 3});

    graf[2].push_back({5, 1});

    graf[2].push_back({7, 3});

    graf[3].push_back({3, 0});

    graf[3].push_back({8, 1});

    graf[3].push_back({7, 2});

    int costTotal = primMST(graf, numarNoduri);

    cout << „Costul total al MST: ” << costTotal << endl;

    return 0;

}


7. Activități practice pentru elevi

  1. Implementați o funcție care verifică dacă un graf are mai mulți arbori parțiali de cost minim.
  2. Adăugați opțiuni de a construi un graf utilizând matricea de adiacență și lista de adiacență.
  3. Implementați o aplicație care compară costurile MST folosind algoritmii lui Kruskal și Prim.

8. Scheme logice

  1. Algoritmul lui Kruskal:
    • Sortează muchiile -> Verifică ciclurile -> Adaugă muchiile valide în MST.
  2. Algoritmul lui Prim:
    • Selectează un nod inițial -> Găsește muchia de cost minim -> Adaugă nodul conectat.

9. Concluzie

  • Arborii parțiali de cost minim sunt esențiali pentru optimizarea resurselor în grafuri.
  • Algoritmii lui Kruskal și Prim sunt eficienți pentru rezolvarea MST.
  • Practica ajută la înțelegerea algoritmilor și la aplicarea lor în situații reale.

Structuri de Date Arborescente Implementate Dinamic – Arbori Binari


1. Obiectivele lecției:

  • Să înțeleagă conceptul de arbore binar și proprietățile acestuia.
  • Să învețe cum să implementeze un arbore binar dinamic.
  • Să implementeze operații comune pe arbori binari: inserare, căutare, traversare (în ordine, preordine, postordine).

2. Ce este un arbore binar?

  1. Definiție:
    Un arbore binar este o structură de date arborescentă în care fiecare nod are cel mult doi copii:
    • Subarborele stâng
    • Subarborele drept
  2. Proprietăți:
    • Arborele binar poate fi complet, perfect sau degenerat.
    • Într-un arbore binar, numărul maxim de noduri la nivelul hhh este 2h2^h2h.
    • Înălțimea arborelui este dată de lungimea drumului de la rădăcină la cea mai adâncă frunză.
  3. Tipuri de arbori binari:
    • Arbore binar complet: Fiecare nivel, cu excepția ultimului, este complet populat.
    • Arbore binar de căutare (BST): Subarborele stâng conține valori mai mici, iar subarborele drept conține valori mai mari decât rădăcina.

3. Avantaje și dezavantaje

AvantajeDezavantaje
Structură flexibilă pentru gestionarea datelor ierarhice.Gestionarea dinamică necesită atenție pentru integritate.
Traversare eficientă pentru căutare, inserare și ștergere.Performanța poate scădea în cazuri de arbori dezechilibrați.
Este baza pentru alte structuri complexe, cum ar fi heap-uri.

4. Structura unui nod

struct Nod {

    int valoare;       // Valoarea stocată în nod

    Nod* stanga;       // Pointer către subarborele stâng

    Nod* dreapta;      // Pointer către subarborele drept

};


5. Operații comune pe arbori binari


1. Crearea unui nod

Nod* creeazaNod(int valoare) {

    Nod* nod = new Nod();

    nod->valoare = valoare;

    nod->stanga = nullptr;

    nod->dreapta = nullptr;

    return nod;

}


2. Inserarea unui nod într-un arbore binar de căutare (BST)

Nod* insereazaNod(Nod* radacina, int valoare) {

    if (radacina == nullptr) {

        return creeazaNod(valoare);

    }

    if (valoare < radacina->valoare) {

        radacina->stanga = insereazaNod(radacina->stanga, valoare);

    } else if (valoare > radacina->valoare) {

        radacina->dreapta = insereazaNod(radacina->dreapta, valoare);

    }

    return radacina;

}


3. Traversări ale arborelui

  1. Traversare în ordine (in-order):
    Vizitează nodurile în ordinea: stânga → rădăcină → dreapta.

void traversareInOrdine(Nod* radacina) {

    if (radacina == nullptr) return;

    traversareInOrdine(radacina->stanga);

    cout << radacina->valoare << ” „;

    traversareInOrdine(radacina->dreapta);

}

  1. Traversare preordine (pre-order):
    Vizitează nodurile în ordinea: rădăcină → stânga → dreapta.

void traversarePreOrdine(Nod* radacina) {

    if (radacina == nullptr) return;

    cout << radacina->valoare << ” „;

    traversarePreOrdine(radacina->stanga);

    traversarePreOrdine(radacina->dreapta);

}

  1. Traversare postordine (post-order):
    Vizitează nodurile în ordinea: stânga → dreapta → rădăcină.

void traversarePostOrdine(Nod* radacina) {

    if (radacina == nullptr) return;

    traversarePostOrdine(radacina->stanga);

    traversarePostOrdine(radacina->dreapta);

    cout << radacina->valoare << ” „;

}


4. Căutarea unui element

bool cautaValoare(Nod* radacina, int valoare) {

    if (radacina == nullptr) {

        return false;

    }

    if (valoare == radacina->valoare) {

        return true;

    } else if (valoare < radacina->valoare) {

        return cautaValoare(radacina->stanga, valoare);

    } else {

        return cautaValoare(radacina->dreapta, valoare);

    }

}


5. Ștergerea unui nod

Nod* stergeNod(Nod* radacina, int valoare) {

    if (radacina == nullptr) return radacina;

    if (valoare < radacina->valoare) {

        radacina->stanga = stergeNod(radacina->stanga, valoare);

    } else if (valoare > radacina->valoare) {

        radacina->dreapta = stergeNod(radacina->dreapta, valoare);

    } else {

        if (radacina->stanga == nullptr) {

            Nod* temp = radacina->dreapta;

            delete radacina;

            return temp;

        } else if (radacina->dreapta == nullptr) {

            Nod* temp = radacina->stanga;

            delete radacina;

            return temp;

        }

        Nod* temp = radacina->dreapta;

        while (temp->stanga != nullptr) {

            temp = temp->stanga;

        }

        radacina->valoare = temp->valoare;

        radacina->dreapta = stergeNod(radacina->dreapta, temp->valoare);

    }

    return radacina;

}


6. Exemplu complet

#include <iostream>

using namespace std;

struct Nod {

    int valoare;

    Nod* stanga;

    Nod* dreapta;

};

Nod* creeazaNod(int valoare) {

    Nod* nod = new Nod();

    nod->valoare = valoare;

    nod->stanga = nullptr;

    nod->dreapta = nullptr;

    return nod;

}

Nod* insereazaNod(Nod* radacina, int valoare) {

    if (radacina == nullptr) {

        return creeazaNod(valoare);

    }

    if (valoare < radacina->valoare) {

        radacina->stanga = insereazaNod(radacina->stanga, valoare);

    } else if (valoare > radacina->valoare) {

        radacina->dreapta = insereazaNod(radacina->dreapta, valoare);

    }

    return radacina;

}

void traversareInOrdine(Nod* radacina) {

    if (radacina == nullptr) return;

    traversareInOrdine(radacina->stanga);

    cout << radacina->valoare << ” „;

    traversareInOrdine(radacina->dreapta);

}

int main() {

    Nod* radacina = nullptr;

    radacina = insereazaNod(radacina, 50);

    insereazaNod(radacina, 30);

    insereazaNod(radacina, 70);

    insereazaNod(radacina, 20);

    insereazaNod(radacina, 40);

    insereazaNod(radacina, 60);

    insereazaNod(radacina, 80);

    cout << „Traversare în ordine: „;

    traversareInOrdine(radacina);

    cout << endl;

    return 0;

}


6. Activități practice pentru elevi

  1. Implementați o funcție care calculează înălțimea unui arbore binar.
  2. Scrieți un program care determină dacă un arbore este echilibrat.
  3. Implementați o funcție pentru numărarea nodurilor frunză dintr-un arbore binar.

7. Scheme logice

  1. Inserare:
    • Verifică dacă nodul curent este gol -> Creează un nou nod -> Compară valoarea și inserează în stânga sau dreapta.
  2. Traversare:
    • Utilizează recursivitate pentru a parcurge nodurile în ordinea specificată.
  3. Căutare:
    • Compară valoarea căutată cu nodul curent -> Explorează subarborele corespunzător.

8. Concluzie

  • Arborii binari sunt structuri de date versatile utilizate în multe aplicații informatice.
  • Implementarea dinamică permite gestionarea eficientă a memoriei și flexibilitatea dimensiunii arborelui.

Structuri de Date Arborescente Implementate Dinamic – Arbore Binar Heap


1. Obiectivele lecției:

  • Să înțeleagă conceptul de heap și proprietățile acestuia.
  • Să învețe cum să implementeze un heap binar.
  • Să implementeze operații comune pe heap: inserare, eliminare, max-heapify/min-heapify.

2. Ce este un heap binar?

  1. Definiție:
    Un heap binar este un arbore binar complet care respectă una dintre următoarele proprietăți:
    • Max-Heap: Valoarea oricărui nod este mai mare sau egală decât valorile copiilor săi.
    • Min-Heap: Valoarea oricărui nod este mai mică sau egală decât valorile copiilor săi.
  2. Proprietăți:
    • Este un arbore complet (toate nivelurile, cu excepția ultimului, sunt complet populate).
    • Este utilizat frecvent pentru implementarea cozilor de priorități.

3. Avantaje și dezavantaje

AvantajeDezavantaje
Eficient pentru accesarea elementelor de maxim/minim.Inserarea și eliminarea implică reorganizarea arborelui.
Structura este compactă datorită proprietății de arbore complet.Performanța scade dacă arborele devine dezechilibrat.
Util pentru aplicații precum algoritmii de sortare și cozi de priorități.

4. Reprezentarea unui heap

Heap-urile sunt reprezentate în mod obișnuit folosind:

  1. Vectori: Copilul stâng și copilul drept ale unui nod se află la pozițiile 2i+12i + 12i+1 și 2i+22i + 22i+2, respectiv.
  2. Structuri dinamice: Fiecare nod are pointeri către copiii săi.

5. Structura unui nod pentru un heap

struct Nod {

    int valoare;

    Nod* stanga;  // Pointer către copilul stâng

    Nod* dreapta; // Pointer către copilul drept

};


6. Implementarea unui Max-Heap

1. Crearea unui nod

Nod* creeazaNod(int valoare) {

    Nod* nod = new Nod();

    nod->valoare = valoare;

    nod->stanga = nullptr;

    nod->dreapta = nullptr;

    return nod;

}


2. Inserarea unui element în heap

Inserarea se face astfel:

  1. Adăugăm elementul într-un loc valid (pentru a păstra proprietatea de arbore complet).
  2. Reorganizăm arborele pentru a menține proprietatea de heap (numită heapify-up).

void heapifyUp(vector<int>& heap, int index) {

    int parinte = (index – 1) / 2;

    while (index > 0 && heap[index] > heap[parinte]) {

        swap(heap[index], heap[parinte]);

        index = parinte;

        parinte = (index – 1) / 2;

    }

}

void inserare(vector<int>& heap, int valoare) {

    heap.push_back(valoare); // Adăugăm elementul la final

    heapifyUp(heap, heap.size() – 1); // Reorganizăm arborele

}


3. Eliminarea elementului maxim

Eliminarea elementului maxim (rădăcina) implică:

  1. Înlocuirea rădăcinii cu ultimul element.
  2. Eliminarea ultimului element.
  3. Reorganizarea arborelui pentru a menține proprietatea de heap (numită heapify-down).

void heapifyDown(vector<int>& heap, int index) {

    int stanga = 2 * index + 1;

    int dreapta = 2 * index + 2;

    int celMaiMare = index;

    if (stanga < heap.size() && heap[stanga] > heap[celMaiMare]) {

        celMaiMare = stanga;

    }

    if (dreapta < heap.size() && heap[dreapta] > heap[celMaiMare]) {

        celMaiMare = dreapta;

    }

    if (celMaiMare != index) {

        swap(heap[index], heap[celMaiMare]);

        heapifyDown(heap, celMaiMare);

    }

}

void eliminareMax(vector<int>& heap) {

    if (heap.empty()) {

        cout << „Heap-ul este gol.” << endl;

        return;

    }

    heap[0] = heap.back(); // Înlocuim rădăcina cu ultimul element

    heap.pop_back(); // Eliminăm ultimul element

    heapifyDown(heap, 0); // Reorganizăm arborele

}


4. Afișarea elementelor din heap

void afisareHeap(const vector<int>& heap) {

    for (int valoare : heap) {

        cout << valoare << ” „;

    }

    cout << endl;

}


7. Exemplu complet pentru Max-Heap

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

void heapifyUp(vector<int>& heap, int index) {

    int parinte = (index – 1) / 2;

    while (index > 0 && heap[index] > heap[parinte]) {

        swap(heap[index], heap[parinte]);

        index = parinte;

        parinte = (index – 1) / 2;

    }

}

void inserare(vector<int>& heap, int valoare) {

    heap.push_back(valoare); // Adăugăm elementul la final

    heapifyUp(heap, heap.size() – 1); // Reorganizăm arborele

}

void heapifyDown(vector<int>& heap, int index) {

    int stanga = 2 * index + 1;

    int dreapta = 2 * index + 2;

    int celMaiMare = index;

    if (stanga < heap.size() && heap[stanga] > heap[celMaiMare]) {

        celMaiMare = stanga;

    }

    if (dreapta < heap.size() && heap[dreapta] > heap[celMaiMare]) {

        celMaiMare = dreapta;

    }

    if (celMaiMare != index) {

        swap(heap[index], heap[celMaiMare]);

        heapifyDown(heap, celMaiMare);

    }

}

void eliminareMax(vector<int>& heap) {

    if (heap.empty()) {

        cout << „Heap-ul este gol.” << endl;

        return;

    }

    heap[0] = heap.back(); // Înlocuim rădăcina cu ultimul element

    heap.pop_back(); // Eliminăm ultimul element

    heapifyDown(heap, 0); // Reorganizăm arborele

}

void afisareHeap(const vector<int>& heap) {

    for (int valoare : heap) {

        cout << valoare << ” „;

    }

    cout << endl;

}

int main() {

    vector<int> heap;

    inserare(heap, 10);

    inserare(heap, 20);

    inserare(heap, 15);

    inserare(heap, 30);

    inserare(heap, 40);

    cout << „Heap după inserări: „;

    afisareHeap(heap);

    eliminareMax(heap);

    cout << „Heap după eliminarea maximului: „;

    afisareHeap(heap);

    return 0;

}


8. Activități practice pentru elevi

  1. Implementați un Min-Heap folosind aceleași metode, modificând condițiile din funcțiile heapifyUp și heapifyDown.
  2. Scrieți o funcție care sortează un vector utilizând algoritmul Heap Sort.
  3. Realizați o aplicație care implementează o coadă de priorități utilizând un heap.

9. Scheme logice

  1. Inserare:
    • Adăugăm elementul la sfârșitul arborelui -> Reorganizăm pentru a menține proprietatea de heap.
  2. Eliminare:
    • Înlocuim rădăcina cu ultimul element -> Eliminăm ultimul element -> Reorganizăm arborele.

10. Concluzie

  • Heap-urile binare sunt structuri esențiale pentru optimizarea accesului la valorile maxime sau minime.
  • Implementarea dinamică permite gestionarea eficientă a resurselor.
  • Practica este cheia pentru a înțelege și aplica aceste structuri în probleme complexe.

Ștergerea nodului rădăcină în Min-Heap/Max-Heap


1. Concept general:

Ștergerea nodului rădăcină dintr-un heap presupune eliminarea elementului care respectă proprietățile heap-ului:

  • Max-Heap: Nodul rădăcină este cel mai mare element.
  • Min-Heap: Nodul rădăcină este cel mai mic element.

Pașii generali pentru ștergerea rădăcinii:

  1. Înlocuim rădăcina cu ultimul element din heap.
  2. Eliminăm ultimul element din vector sau structură.
  3. Aplicăm procesul de reorganizare a heap-ului, numit heapify-down, pentru a restabili proprietatea de heap.

2. Implementarea pentru Max-Heap


Heapify-Down în Max-Heap

Această funcție rearanjează elementele astfel încât proprietatea Max-Heap să fie restabilită după ștergerea rădăcinii.

void heapifyDown(vector<int>& heap, int index) {

    int stanga = 2 * index + 1;

    int dreapta = 2 * index + 2;

    int celMaiMare = index;

    if (stanga < heap.size() && heap[stanga] > heap[celMaiMare]) {

        celMaiMare = stanga;

    }

    if (dreapta < heap.size() && heap[dreapta] > heap[celMaiMare]) {

        celMaiMare = dreapta;

    }

    if (celMaiMare != index) {

        swap(heap[index], heap[celMaiMare]);

        heapifyDown(heap, celMaiMare);

    }

}


Ștergerea rădăcinii din Max-Heap

  1. Înlocuim rădăcina cu ultimul element din vector.
  2. Eliminăm ultimul element.
  3. Reorganizăm arborele folosind heapifyDown.

void stergeRadacinaMaxHeap(vector<int>& heap) {

    if (heap.empty()) {

        cout << „Heap-ul este gol. Nu există elemente de șters.” << endl;

        return;

    }

    heap[0] = heap.back(); // Înlocuim rădăcina cu ultimul element

    heap.pop_back();       // Eliminăm ultimul element

    heapifyDown(heap, 0);  // Reorganizăm arborele

}


3. Implementarea pentru Min-Heap


Heapify-Down în Min-Heap

Funcția rearanjează elementele astfel încât proprietatea Min-Heap să fie restabilită după ștergerea rădăcinii.

void heapifyDownMin(vector<int>& heap, int index) {

    int stanga = 2 * index + 1;

    int dreapta = 2 * index + 2;

    int celMaiMic = index;

    if (stanga < heap.size() && heap[stanga] < heap[celMaiMic]) {

        celMaiMic = stanga;

    }

    if (dreapta < heap.size() && heap[dreapta] < heap[celMaiMic]) {

        celMaiMic = dreapta;

    }

    if (celMaiMic != index) {

        swap(heap[index], heap[celMaiMic]);

        heapifyDownMin(heap, celMaiMic);

    }

}


Ștergerea rădăcinii din Min-Heap

  1. Înlocuim rădăcina cu ultimul element din vector.
  2. Eliminăm ultimul element.
  3. Reorganizăm arborele folosind heapifyDownMin.

void stergeRadacinaMinHeap(vector<int>& heap) {

    if (heap.empty()) {

        cout << „Heap-ul este gol. Nu există elemente de șters.” << endl;

        return;

    }

    heap[0] = heap.back(); // Înlocuim rădăcina cu ultimul element

    heap.pop_back();       // Eliminăm ultimul element

    heapifyDownMin(heap, 0); // Reorganizăm arborele

}


4. Exemplu complet

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

// Heapify-Down pentru Max-Heap

void heapifyDown(vector<int>& heap, int index) {

    int stanga = 2 * index + 1;

    int dreapta = 2 * index + 2;

    int celMaiMare = index;

    if (stanga < heap.size() && heap[stanga] > heap[celMaiMare]) {

        celMaiMare = stanga;

    }

    if (dreapta < heap.size() && heap[dreapta] > heap[celMaiMare]) {

        celMaiMare = dreapta;

    }

    if (celMaiMare != index) {

        swap(heap[index], heap[celMaiMare]);

        heapifyDown(heap, celMaiMare);

    }

}

// Ștergerea rădăcinii pentru Max-Heap

void stergeRadacinaMaxHeap(vector<int>& heap) {

    if (heap.empty()) {

        cout << „Heap-ul este gol. Nu există elemente de șters.” << endl;

        return;

    }

    heap[0] = heap.back();

    heap.pop_back();

    heapifyDown(heap, 0);

}

// Funcție pentru afișarea Heap-ului

void afisareHeap(const vector<int>& heap) {

    for (int val : heap) {

        cout << val << ” „;

    }

    cout << endl;

}

int main() {

    vector<int> maxHeap = {40, 30, 20, 10, 15, 5, 25};

    cout << „Heap inițial: „;

    afisareHeap(maxHeap);

    stergeRadacinaMaxHeap(maxHeap);

    cout << „Heap după ștergerea rădăcinii: „;

    afisareHeap(maxHeap);

    return 0;

}


5. Activități practice pentru elevi

  1. Implementați ștergerea rădăcinii pentru un Min-Heap.
  2. Extindeți funcțiile pentru a implementa Heap Sort utilizând un Max-Heap.
  3. Realizați un program care implementează o coadă de priorități utilizând Min-Heap.

6. Scheme logice

  1. Ștergerea rădăcinii:
    • Înlocuiți rădăcina cu ultimul element din vector.
    • Eliminați ultimul element.
    • Reorganizați arborele cu heapify-down pentru a menține proprietatea heap-ului.

7. Concluzie

  • Ștergerea rădăcinii este o operație esențială pentru gestionarea heap-urilor și este utilizată frecvent în algoritmi precum Heap Sort sau cozi de priorități.
  • Implementarea funcției heapifyDown este crucială pentru a menține proprietățile heap-ului.
  • Practica cu Max-Heap și Min-Heap ajută la înțelegerea utilizării eficiente a acestor structuri de date.

Similar Posts

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *