2011-11-15 24 views
16

私はコンピュータサイエンスクラスのヒープ実装を作成していますが、次の再帰関数がまだヒープではない配列オブジェクトからヒープを作成するかどうかは疑問でした。次のように コードは次のとおりです。"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); 
    } 
} 

アイデアは指定されたインデックスのデータは、それがすべての子供のよりも大きい場合、あなたが見ていることです。そうであれば、関数は問題なく終了します。それ以外の場合は、どちらが最大であるかを確認し(左または右の子)、インデックスとスワップします。スワッピングが起こったインデックスでheapifyが呼び出されます。 ildjarnの要請によって

、私は私の質問の解答を支援するために私の完全なクラス定義と実装ファイルを含めています:

#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 

と実装ファイル:


ここにヘッダファイルですここ 権利を感じていることを確認書かれたよう
#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

イェーイ!宿題に努力の証拠がある!+1 – vcsjones

+1

D'aww、本当にありがとう! –

+0

@vcsjones:確かに、悲しいことに、まれです。 – ildjarn

答えて

8

アルゴリズムが機能します。問題は、アルゴリズムをコードに変換することです。

int Data[7]; 

、あなたが初期値{0, 1, 2, 3, 4, 5, 6}を移入:あなたは、データを宣言したと言います。以下のようなものであることをLeftChild(i)RightChild(i)の定義を仮定:

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

、その後のようなものでなければなりませんあなたの関数BuildHeap()、:

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

は下の一番右のサブにHeapifyプロセスを開始しますツリールート。この場合、これは配列インデックス2で、左の子が5、右の子が6である。Heapifyは正しく2と6を交換し、再帰的にHeapify(6)を呼び出します。

ここでは全てがうまく走れる!現時点では、あなたのツリーは次のようになります。

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

のでコールHeapify(6)は律儀Data[13]Data[14]で(C++やJavaとは異なり、配列の境界の執行の欠如、の危険を)Data[6]の値を比較します。明らかに、後者の2つの値は、RAMに残っているジャンクです。 1つの解決策は、醜いが動作するパッチは、Dataの宣言に8つの要素を追加し、それらをすべて配列の要素よりも低い値に初期化することです。より良い解決策は、あなたのクラスにheapSize変数を追加し、配列の長さと、それが等しくなるように設定することです:

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

その後、彼らは木の有効な葉である場合にのみ、子ノードを比較するためのロジックを統合します。これの効率的な実装は次のとおりです。

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

そう要約するために、解決策は、子ノードがツリーの有効な葉であることを確認するためにロジックを追加するのと同じくらい簡単です、そして、あなたの主な機能は次のようなものがあります:

Heap heap; 

// initialize Data here  

heap.BuildHeap(); 

希望があれば

1

あなたのコード。それがどのように実行されるかを見るためにいくつかのテストケースを書くのと全く同じことは何もありません。 1、2、3、4、および数十の要素でヒープをテストするようにしてください。 (基本的なケースは、このピースが不足している場所になると思います。iに子供がいない場合、どのように対処しますか?)小さなヒープのテストは急いで行ってください。

この作品の小アドバイス:

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 
} 

はおそらくifブロックにのみインデックスを設定することで、いくつかの読みやすさを得ることができます:

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

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

私はそれを数回走らせてしまい、動作しません。それは文字通りヒープに何もしません。しかし、私はそれを呼び出すために別の関数を使用していますが、そのすべての関数は最低の内部ノードを呼び出し、そこからheapifyを呼び出します。私は明日教授に尋ねますが、@ _ @ –

+1

@Chris:フルクラス定義で質問を更新できますか?この問題は他の場所にある可能性があります。 'LeftChild'や' RightChild'のセマンティクスや、 'Data'が宣言される方法で、 – ildjarn

8

番号をツリー上で

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

出力は、いくつかのヒープ違反があり

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

になるだろう。 (私はData[l]Data[r]は、対応する子供たちが存在しない場合は、マイナス無限大であると仮定しています。あなたはこれを確実にするために、余分なロジックが必要な場合があります。)

は何あなたの関数が行うことは、ヒープではないかもしれない木を固定しているが、その左右のサブツリーはヒープです。 Heapify(i)が呼び出されたときに、iの子がヒープになるように、すべてのノードで、ポストオーダーで呼び出す必要があります(つまり、n - 1から0まで)。

+1

リーフノードのifを呼び出す必要はありません。 –

4

コードでヒープが正常に作成されました。概念上の欠陥は1​​つしかありませんでした。残りは、1つずつの索引付けエラーでした。 1つの基本的なエラーがBuildHeapにあった:あなたは

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

を持っていた、これは

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

である必要があり、一方、これは本当に重要であり、あなたはHeapifyは常にツリーのルートで呼び出され、そして(これがあることを確認しなければなりません(heapSize/2) - 1)(これはC++およびJavaスタイルの場合、最初のインデックス== 0)の配列の最後のツリールートを簡単に見つけることができます。それはあなたのコードが最後にの葉に間違っているHeapifyと書かれた方法です。

それ以外は、off-by-oneエラーのフラグを立てるためのコメントを追加しました。私はそれらを簡単に見つけることができるように、それらを左に置いた。あなたはアルゴリズムとデータ構造のsuberbの理解を得ることを望みます! :-)

あなたのヘッダファイル:

#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 

あなたの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; 
} 
関連する問題