2011-11-15 16 views
16

Tworzę implementację sterty dla klasy informatycznej i zastanawiałem się, czy następująca funkcja rekursywna tworzy stertę z obiektu tablicy, który nie był już stertą. kod jest w następujący sposób:Czy poprawnie wprowadzam algorytm "Heapify"?

void Heap::Heapify(int i) 
{ 
    int temp, l, r, heapify; 
    l = LeftChild(i);// get the left child 
    r = RightChild(i);// get the right child 

    //if one of the children is bigger than the index 
    if((Data[i] < Data[l]) || (Data[i]< Data[r])) 
    { 
     //if left is the bigger child 
     if(Data[l] > Data[r]) 
     { 
      //swap parent with left child 
      temp = Data[i]; 
      Data[i] = Data[l]; 
      Data[l] = temp; 
      heapify = l; // index that was swapped 
     } 
     //if right is the bigger child 
     else 
     { 
      //swap parent with right child 
      temp = Data[i]; 
      Data[i] = Data[r]; 
      Data[r] = temp; 
      heapify = r; // index that was swapped 
     } 
     // do a recursive call with the index 
     //that was swapped 
     Heapify(heapify); 
    } 
} 

chodzi o to, że widać, jeśli dane w indeksie danej jest większa niż wszystkich jego dzieci. Jeśli tak, funkcja nie kończy problemu. W przeciwnym razie sprawdza, który jest największy (lewy lub prawy potomek), a następnie zamienia go z indeksem. Stertylizacja jest następnie wywoływana w indeksie, w którym nastąpiło zamiana.

życzenie Ildjarn za, jestem w tym moje pełne określaniu i urzeczywistnianiu klasa plików, aby ułatwić odpowiadanie na moje pytanie:
oto nagłówek pliku:

#ifndef HEAP_H 
#define HEAP_H 
//Programmer: Christopher De Bow 
//Date: november 15, 2011 

class Heap 
{ 
private: 
    int Data [100]; 
    int Parent(int); 
    int RightChild(int); 
    int LeftChild(int); 
    void Heapify(int); 
    void BuildHeap(); 

public: 
    Heap(); 
    void insert(); 
    void HeapSort(); 
    void ExtractMaximum(); 
    int Maximum(); 
    void PrintHeap(); 
    int heapsize; 
    void SetData(int[]); 
}; 

#endif 

a plik realizacja:

#include <iostream> 
#include "Heap.h" 
using namespace std; 
//Programmer: Christopher De Bow 
//Date: november 15, 2011 

Heap::Heap() 
{ 
    int init [10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
    heapsize = 10; 
    SetData(init); 
} 

int Heap::Parent(int index) 
{ 
    int Rval; 
    if(index%2 == 0)// if the index is even 
    { 
     Rval = ((index-1)/2); 
    } 
    else// if the index is odd 
    { 
     Rval = (index/2); 
    } 
    return Rval; 
} 

int Heap::RightChild(int arrplace) 
{ 
    int ret; 
    ret = ((2*arrplace)+2); //rightchild is index times 2 plus 2 
    return ret; 
} 

int Heap::LeftChild(int i) 
{ 
    int rval; 
    rval = ((2*i)+1); //leftchild is index times 2 plus 1 
    return rval; 
} 

void Heap::Heapify(int i) 
{ 
    int temp, l, r, heapify; 

    l = LeftChild(i); // get the left child 
    r = RightChild(i); // get the right child 

    if((l <= heapSize) && (data[l] > data[i])) 
    { 
     heapify = l; 
    { 
    else 
    { 
     heapfiy = i; 
    } 
    if((r <= heapSize) && (data[r] > data[heapify])) 
    { 
     heapify = r; 
    } 
    if(heapify != i) // one of the two child nodes has proved 
    {    // larger than Data[i], so interchange values 
     //swap parent with left child 
     temp = Data[i]; 
     Data[i] = Data[heapify]; 
     Data[heapify] = temp; 
     Heapify(heapify); 
    } 
} 

void Heap::BuildHeap() 
{ 
    // we do not have a heap 
    // we will make a heap 
    // by calling heapify starting at the lowest 
    // internal node in the heap 
    for(int i = heapsize; i >= 1; i--) 
    { 
     Heapify(i-1); 
    } 
} 

void Heap::insert() 
{ 
    int insert; 
    heapsize = (heapsize + 1); 
    //getting data from the user 
    cout<<"what data would you like to insert?"<<endl; 
    cin>>insert; 
    Data[heapsize] = insert; 
    BuildHeap(); //call BuildHeap on array 
    cout<<"done"<<endl; 
} 

void Heap::PrintHeap() 
{ 
    BuildHeap(); 
    for(int count = 0; count < (heapsize-1); count++) 
    { 
     cout<<Data[count];// print out every element in heap 
    } 
    cout<<endl<<endl; 
} 

void Heap::HeapSort() 
{ 
    BuildHeap(); 
    int temp; 
    // do this for every elem in heap: 
    for(int i = 0; i < heapsize; i++) 
    { 
     temp = Data[heapsize-1]; 
     Data[heapsize-1] = Data[0]; 
     Data[0] = temp; 
     heapsize--; 
     BuildHeap(); 
    } 
    PrintHeap(); 
} 

void Heap::ExtractMaximum() 
{ 
    BuildHeap(); 
    //assign last thing in heap to first thing in heap 
    Data[0] = Data[heapsize]; 
    heapsize --; // decrease heapsize by one 
    Heapify(0); // heapify from the top 
} 

int Heap::Maximum() 
{ 
    int Rval; 
    BuildHeap();// make sure we have a heap 
    Rval = Data[0]; 
    return Rval; // return top thing 
} 

//initialize the elements in the "Data" array 
void Heap::SetData(int x[]) 
{ 
    for(int i = 0; i <= (heapsize); i++) 
    { 
     Data[i] = x[i]; 
    } 
} 
+19

Yay! Pytanie o zadanie domowe z dowodami wysiłku!+1 – vcsjones

+1

D'aww, dziękuję bardzo! –

+0

@vcsjones: Rzeczywiście, niestety rzadkość. – ildjarn

Odpowiedz

8

Twój algorytm działa. Problem polega na tłumaczeniu algorytmu na kod. Załóżmy, że zadeklarowane dane, jak:

int Data[7]; 

i wypełnić go z wartości początkowych {0, 1, 2, 3, 4, 5, 6}.Zakładając definicje LeftChild(i) i RightChild(i) być coś takiego:

#define LeftChild(i) ((i << 1) + 1) 
#define RightChild(i) ((i << 1) + 2) 

wówczas funkcja BuildHeap(), co powinno być coś takiego:

void Heap::BuildHeap() 
{ 
    for(int i = (7 >> 1); i >= 1; i--) // in general, replace 7 with 
             // (sizeof(Data)/sizeof(int)), presuming 
             // you have an array of int's. if not, 
             // replace int with the relevant data type 
    Heapify(i-1); 
} 

rozpocznie proces Heapify na dolnym prawym najbardziej sub- korzeń drzewa. W tym przypadku jest to indeks tablicy 2, z lewym dzieckiem 5 i prawym dzieckiem 6. Heapify poprawnie wymieniają 2 i 6 i rekurencyjnie wywołują Heapify(6).

Tutaj wszystko może osiadać na mieliźnie! Obecnie drzewo wygląda następująco:

      0 
        1   2 
       3  4 5  6 
      u n d e f i n e d s p a c e 

więc wywołanie Heapify(6) będzie sumiennie porównać wartości Data[6] z Data[13] i Data[14] (niebezpieczeństw C++ i jego brak granic array egzekwowania, w przeciwieństwie do Javy). Oczywiście te ostatnie dwie wartości mogą być dowolnymi resztkami w pamięci RAM. Jednym z rozwiązań tutaj, brzydkim, ale działającym, jest dodanie 8 elementów w deklaracji danych i zainicjowanie ich wszystkich do pewnej wartości niższej niż dowolny element tablicy. Lepszym rozwiązaniem jest dodanie zmiennej heapSize do swojej klasy i ustawić go równa długości swojej tablicy:

heapSize = (sizeof(Data)/sizeof(int)); 

Następnie zintegrować logiki porównać tylko węzłów potomnych, jeśli są one ważne liście drzewa. Sprawny realizacja tego jest:

void Heap::Heapify(int i) 
{ 
    int temp, l, r, heapify; 

    l = LeftChild(i); // get the left child 
    r = RightChild(i); // get the right child 

    if((l <= heapSize) && (Data[l] > Data[i])) 
     heapify = l; 
    else heapfiy = i; 
    if((r <= heapSize) && (Data[r] > Data[heapify])) 
     heapify = r; 
    if(heapify != i) // one of the two child nodes has proved 
        // larger than Data[i], so interchange values 
    { 
     //swap parent with left child 
     temp = Data[i]; 
     Data[i] = Data[heapify]; 
     Data[heapify] = temp; 

     Heapify(heapify); 
    } 
} 

więc podsumować, rozwiązanie jest tak proste, jak dodanie logiki, aby upewnić się, że węzły potomne są ważne liście drzewa, a główną funkcją będzie miał coś takiego:

Heap heap; 

// initialize Data here  

heap.BuildHeap(); 

Nadzieję, że pomaga.

1

Twój kod jak tutaj podano na pewno czuje się prawy; ale nie ma to jak pisanie kilku przypadków testowych, aby zobaczyć, jak to działa. Pamiętaj, aby przetestować przeciwko kupie z 1, 2, 3, 4 i dziesiątkami elementów. (Spodziewam się, że wariant podstawowy być tam, gdzie ten kawałek daleki - jak to obsłużyć gdy i ma Testing Bez dzieci ?. małych hałd powinien pokazać w pośpiechu.)

Niektóre małe rady dla tego kawałka:

if(Data[l] > Data[r]) 
{ 
    //swap parent with left child 
    temp = Data[i]; 
    Data[i] = Data[l]; 
    Data[l] = temp; 
    heapify = l; // index that was swapped 
} 
//if right is the bigger child 
else 
    { //swap parent with right child 
    temp = Data[i]; 
    Data[i] = Data[r]; 
    Data[r] = temp; 
    heapify = r; // index that was swapped 
} 

można prawdopodobnie zdobyć czytelność przez ustawienie tylko indeksw if bloków:

if(Data[l] > Data[r]) { 
    swapme = l; 
} else { 
    swapme = r; 
} 

temp = Data[i]; 
Data[i] = Data[swapme]; 
Data[swapme] = temp; 
heapify = swapme; 
+0

Przepuściłem go kilka razy i nie zadziała. To dosłownie nie robi nic na kupę. Używam jednak innej funkcji do wywoływania go, ale cała ta funkcja wywołuje najniższy węzeł wewnętrzny, a następnie wywołuje stamtąd wywołanie. Zapytam jutro mojego profesora, ale po prostu nie rozumiem @ _ @ –

+1

@ Chris: Czy możesz zaktualizować swoje pytanie z pełną definicją klasy? Problem może leżeć gdzie indziej, np. w semantyce 'LeftChild' lub' RightChild' lub w sposób zadeklarowany 'Data'. – ildjarn

8

nr na drzewie

 1 
    /\ 
    / \ 
/ \ 
    2  3 
/\ /\ 
6 7 4 5 

wyjście będzie

 3 
    /\ 
    / \ 
/ \ 
    2  5 
/\ /\ 
6 7 4 1 

który ma kilka naruszeń sterty. (Przyjmuję, że Data[l] i są minus nieskończoność, jeśli odpowiednie elementy podrzędne nie istnieją.Możesz potrzebować dodatkowej logiki, aby to zapewnić.)

Funkcja służy do naprawienia drzewa, które może nie być stertą, ale którego Podgrunty lewy i prawy są stertami. Musisz wywołać go na każdym węźle, w postorder (tj. Dla i od n - 1 do 0), aby dzieci i były stertami, gdy wywoływany jest Heapify (i).

+1

nie musisz wywoływać, jeśli dla węzłów liści. –

4

Twój kod pomyślnie buduje stertę. Wystąpiła tylko jedna wada konceptualna: pozostałe to błędy indeksowania off-by-one. Jedyny błąd fundamentalny był w BuildHeap: miałeś

for(int i = heapSize; i >= 1; i--) 
{ 
    Heapify(i-1); 
} 

podczas gdy powinno to być

for(int i = (heapSize/2); i >= 1; i--) 
{ 
    Heapify(i-1); 
} 

To jest bardzo ważne, należy zauważyć, że Heapify jest zawsze nazywany na korzeniu drzewa, i (jest to naprawdę fajne) można łatwo znaleźć ostatni element główny drzewa w tablicy w indeksie ((heapSize/2) - 1) (jest to dla C++ i stylu Java, gdzie pierwszy indeks == 0). Sposób, w jaki został napisany twój kod o nazwie Heapify na ostatnim liściu drzewa, co jest błędem.

Poza tym dodałem komentarze, aby oznaczyć błędy typu "jeden po drugim". Umieściłem je na lewo, abyś mógł je łatwo znaleźć. Mam nadzieję, że uzyskasz lepsze zrozumienie algorytmów i struktur danych! :-)

Twój plik nagłówka:

#ifndef HEAP_H 
#define HEAP_H 
//Programmer: Christopher De Bow 
//Date: november 15, 2011 

class Heap 
{ 
private: 
    int Data [100]; 
    int Parent(int); 
    int RightChild(int); 
    int LeftChild(int); 
    void Heapify(int); 
    void BuildHeap(); 
// SO added heapSize 
    int heapSize; 

public: 
    Heap(); 
    void insert(); 
    void HeapSort(); 
    void ExtractMaximum(); 
    int Maximum(); 
    void PrintHeap(); 
    int heapsize; 
    void SetData(int[]); 
}; 

#endif 

Twój plik cpp:

#include <iostream> 
#include "Heap.h" 
using namespace std; 
//Programmer: Christopher De Bow 
//Date: november 15, 2011 

Heap::Heap() 
{ 
    int init [10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
    heapSize = 10; 
    SetData(init); 
} 

int Heap::Parent(int index) 
{ 
    int Rval; 
    if(index%2 == 0)// if the index is even 
    { 
     Rval = ((index-1)/2); 
    } 
    else// if the index is odd 
    { 
     Rval = (index/2); 
    } 
    return Rval; 
} 

int Heap::RightChild(int arrplace) 
{ 
    int ret; 
    ret = ((2*arrplace)+2); //rightchild is index times 2 plus 2 
    return ret; 
} 

int Heap::LeftChild(int i) 
{ 
    int rval; 
    rval = ((2*i)+1); //leftchild is index times 2 plus 1 
    return rval; 
} 

void Heap::Heapify(int i) 
{ 
    int temp, l, r, heapify; 

    l = LeftChild(i); // get the left child 
    r = RightChild(i); // get the right child 

// you have to compare the index to (heapSize - 1) because we are working 
// with C++ and the first array index is 0 : l and r are direct indices 
// into the array, so the maximum possible index is the heapSize'th 
// element, which is at heapSize-1. this was kind of nasty as it let the 
// heapify index get too large and led to a swap with memory beyond the 
// last element of the array (again, C++ doesn't enforce array boundaries 
// as Java does). 
    if((l <= (heapSize-1)) && (Data[l] > Data[i])) 
     heapify = l; 
    else 
     heapify = i; 
// you have to compare the index to (heapSize - 1) because we are working 
// with C++ and the first array index is 0 : l and r are direct indices 
// into the array, so the maximum possible index is the heapSize'th 
// element, which is at heapSize-1. this was kind of nasty as it let the 
// heapify index get too large and led to a swap with memory beyond the 
// last element of the array (again, C++ doesn't enforce array boundaries 
// as Java does). 
    if((r <= (heapSize-1)) && (Data[r] > Data[heapify])) 
     heapify = r; 
    if(heapify != i) // one of the two child nodes has proved 
    {    // larger than Data[i], so interchange values 
     //swap parent with left child 
     temp = Data[i]; 
     Data[i] = Data[heapify]; 
     Data[heapify] = temp; 
     Heapify(heapify); 
    } 
} 

void Heap::BuildHeap() 
{ 
    // we do not have a heap 
    // we will make a heap 
    // by calling heapify starting at the lowest 
    // internal node in the heap 
// i must be initialized to (heapsize/2), please see my 
// post for an explanation 
    for(int i = heapSize/2; i >= 1; i--) 
    { 
     Heapify(i-1); 
    } 
} 

void Heap::insert() 
{ 
    int insert; 
    heapSize = (heapSize + 1); 
    //getting data from the user 
    cout<<"what data would you like to insert?"<<endl; 
    cin>>insert; 
    Data[heapSize] = insert; 
    BuildHeap(); //call BuildHeap on array 
    cout<<"done"<<endl; 
} 

void Heap::PrintHeap() 
{ 
    BuildHeap(); 
// the array indices are from 0 through (heapSize-1), so 
// count must be less than _or equal to_ (heapSize-1). another 
// way of phrasing this (which i applied in this function) 
// is (count < heapSize). you'll get better boundary conditions 
// with practice. 
    for(int count = 0; count < heapSize; count++) 
    { 
// added an endl to the output for clarity 
     cout << Data[count] << endl;// print out every element in heap 
    } 
    cout<<endl<<endl; 
} 

void Heap::HeapSort() 
{ 
    BuildHeap(); 
    int temp; 
    // do this for every elem in heap: 
    for(int i = 0; i < heapSize; i++) 
    { 
     temp = Data[heapSize-1]; 
     Data[heapSize-1] = Data[0]; 
     Data[0] = temp; 
     heapSize--; 
     BuildHeap(); 
    } 
    PrintHeap(); 
} 

void Heap::ExtractMaximum() 
{ 
    BuildHeap(); 
    //assign last thing in heap to first thing in heap 
    Data[0] = Data[heapSize]; 
    heapSize--; // decrease heapSize by one 
    Heapify(0); // heapify from the top 
} 

int Heap::Maximum() 
{ 
    int Rval; 
    BuildHeap();// make sure we have a heap 
    Rval = Data[0]; 
    return Rval; // return top thing 
} 

//initialize the elements in the "Data" array 
void Heap::SetData(int x[]) 
{ 
// the array indices are from 0 through (heapSize-1), so 
// count must be less than _or equal to_ (heapSize-1). another 
// way of phrasing this (which i applied in this function) 
// is (i < heapSize). you'll get better boundary conditions 
// with practice. 
    for(int i = 0; i < heapSize; i++) 
    { 
     Data[i] = x[i]; 
    } 
} 

// basic confirmation function 
int main() 
{ 
    Heap heap; 
    heap.PrintHeap(); 

    return 0; 
}