2011-11-15 12 views
16

Estoy creando una implementación de montón para una clase de ciencias de la computación, y me preguntaba si la siguiente función recursiva crearía un montón de un objeto de matriz que ya no era un montón. el código es el siguiente:¿Estoy implementando correctamente el algoritmo "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); 
    } 
} 

la idea es que a ver si los datos en el índice dado es más grande que todo eso de los niños. Si lo es, la función no termina ningún problema. De lo contrario, verifica cuál es el más grande (hijos izquierdos o derechos) y luego lo intercambia con el índice. El heapify se llama en el índice donde sucedió el intercambio.

por la petición de Ildjarn, Estoy incluyendo mis archivos completos de definición de clase y de implementación para ayudar en el contestador de mi pregunta:
aquí está el archivo de cabecera:

#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 

y el archivo de implementación:

#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! ¡Una pregunta de tarea con evidencia de esfuerzo!+1 – vcsjones

+1

D'aww, ¡muchas gracias! –

+0

@vcsjones: De hecho, lamentablemente una rareza. – ildjarn

Respuesta

8

Tu algoritmo funciona. El problema está en la traducción de algoritmo a código. Digamos que los datos declarados como:

int Data[7]; 

y rellena con los valores iniciales {0, 1, 2, 3, 4, 5, 6}.Suponiendo que las definiciones de LeftChild(i) y RightChild(i) a ser algo así como:

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

entonces su función BuildHeap(), que debe ser algo como:

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); 
} 

comenzará el proceso Heapify en la parte inferior de derecha más sub raíz del arbol. En este caso, este es el índice de matriz 2, con un hijo izquierdo de 5 y un hijo derecho de 6. Heapify intercambiará correctamente 2 y 6 y recursivamente llamará a Heapify(6).

¡Aquí todo puede quedarse varado! En la actualidad su árbol se parece a:

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

lo que la llamada Heapify(6) diligentemente comparará los valores de Data[6] con Data[13] y Data[14] (los peligros de C++ y su falta de cumplimiento de los límites de la matriz, a diferencia de Java). Obviamente, los dos últimos valores pueden ser cualquier basura que quede en la RAM. Una solución aquí, fea pero con un parche de trabajo, es agregar 8 elementos en la declaración de datos e inicializarlos a un valor inferior a cualquier elemento de la matriz. La mejor solución es añadir una variable heapSize a su clase y configurarlo igual a la longitud de la matriz:

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

luego integrar la lógica para comparar sólo los nodos secundarios si son válidos hojas del árbol. La realización eficiente de esto es:

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); 
    } 
} 

Entonces, para resumir, la solución es tan sencillo como añadir lógica para asegurarse de que los nodos secundarios son hojas válidas del árbol, y su función principal tendrán algo como:

Heap heap; 

// initialize Data here  

heap.BuildHeap(); 

Espero que ayude.

1

Su código como está escrito aquí seguro se siente derecho; pero no hay nada como escribir algunos casos de prueba para ver cómo funciona. Asegúrese de realizar pruebas en un montón con 1, 2, 3, 4 y docenas de elementos. (Espero que el caso base sea donde esta pieza se queda corta, ¿cómo se maneja cuando i no tiene hijos? Las pruebas en montones pequeños deben mostrar con prisa.)

Algunos consejos pequeños para esta pieza:

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 
} 

que probablemente podría ganar un poco de legibilidad mediante el establecimiento de sólo el índiceen los bloques if:

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

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

Lo he ejecutado varias veces, y no funcionará. Literalmente no hace nada al montón. Sin embargo, estoy usando una función diferente para invocarlo, pero lo único que hace es llamar al nodo interno más bajo y luego llamar a heapify desde allí. Le preguntaré a mi profesor mañana, pero simplemente no entiendo @ _ @ –

+1

@Chris: ¿Puedes actualizar tu pregunta con tu definición de clase completa? El problema puede estar en otra parte, p. en la semántica de 'LeftChild' o' RightChild', o en la forma en que se declara 'Data'. – ildjarn

8

No. por el árbol

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

la salida va a ser

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

que tiene varios violaciónes montón. (Estoy suponiendo que Data[l] y Data[r] son infinito menos si no existen los niños correspondientes. Es posible que tenga lógica adicional para asegurar esto.)

Lo que su función no hace es fijar un árbol que puede no ser un montón, pero cuya Los subárboles izquierdo y derecho son montones. Debe invocarlo en cada nodo, en postorder (es decir, para i desde n - 1 hasta 0) para que los hijos de i sean montones cuando se llame a Heapify (i).

+1

no tiene que llamar si es para nodos hoja. –

4

Su código ahora construye con éxito un montón. Solo había una falla conceptual: el resto eran errores de indexación uno a uno. El único error fundamental estaba en BuildHeap: tenías

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

mientras que esto debería ser

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

Esto es realmente importante, que hay que ver que Heapify siempre se llama la raíz de un árbol, y (esto es realmente genial) puede encontrar fácilmente la última raíz del árbol en la matriz en el índice ((heapSize/2) - 1) (esto es para el estilo C++ y Java donde el primer índice == 0). La forma en que se escribió el código llamado Heapify en la última hoja del árbol, que es un error.

Aparte de eso, he añadido comentarios para marcar los errores uno por uno. Los coloqué al ras a la izquierda para que puedas encontrarlos fácilmente. Espero que entiendas mejor los algoritmos y las estructuras de datos. :-)

Su archivo de cabecera:

#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 

Su archivo 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; 
} 
Cuestiones relacionadas