# binaryheap.hpp

Go to the documentation of this file.

    00001 /* $Id: binaryheap.hpp 17248 2009-08-21 20:21:05Z rubidium $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #ifndef  BINARYHEAP_HPP
    00013 #define  BINARYHEAP_HPP
    00014
    00033 template <class Titem_>
    00034 class CBinaryHeapT {
    00035 public:
    00036   typedef Titem_ *ItemPtr;
    00037 private:
    00038   int                     m_size;
    00039   int                     m_max_size;
    00040   ItemPtr                *m_items;
    00041
    00042 public:
    00043   explicit CBinaryHeapT(int max_items = 102400)
    00044     : m_size(0)
    00045     , m_max_size(max_items)
    00046   {
    00047     m_items = new ItemPtr[max_items + 1];
    00048   }
    00049
    00050   ~CBinaryHeapT()
    00051   {
    00052     Clear();
    00053     delete [] m_items;
    00054     m_items = NULL;
    00055   }
    00056
    00057 public:
    00060   FORCEINLINE int Size() const {return m_size;};
    00061
    00064   FORCEINLINE bool IsEmpty() const {return (m_size == 0);};
    00065
    00068   FORCEINLINE bool IsFull() const {return (m_size >= m_max_size);};
    00069
    00072   FORCEINLINE Titem_& GetHead() {assert(!IsEmpty()); return *m_items[1];}
    00073
    00076   bool Push(Titem_& new_item);
    00077
    00079   FORCEINLINE Titem_& PopHead() {Titem_& ret = GetHead(); RemoveHead(); return ret;};
    00080
    00082   void RemoveHead();
    00083
    00085   void RemoveByIdx(int idx);
    00086
    00088   int FindLinear(const Titem_& item) const;
    00089
    00092   void Clear() {m_size = 0;};
    00093
    00095   void CheckConsistency();
    00096 };
    00097
    00098
    00099 template <class Titem_>
    00100 FORCEINLINE bool CBinaryHeapT<Titem_>::Push(Titem_& new_item)
    00101 {
    00102   if (IsFull()) return false;
    00103
    00104   /* make place for new item */
    00105   int gap = ++m_size;
    00106   /* Heapify up */
    00107   for (int parent = gap / 2; (parent > 0) && (new_item < *m_items[parent]); gap = parent, parent /= 2)
    00108     m_items[gap] = m_items[parent];
    00109   m_items[gap] = &new_item;
    00110   CheckConsistency();
    00111   return true;
    00112 }
    00113
    00114 template <class Titem_>
    00115 FORCEINLINE void CBinaryHeapT<Titem_>::RemoveHead()
    00116 {
    00117   assert(!IsEmpty());
    00118
    00119   /* at index 1 we have a gap now */
    00120   int gap = 1;
    00121
    00122   /* Heapify down:
    00123    *   last item becomes a candidate for the head. Call it new_item. */
    00124   Titem_& new_item = *m_items[m_size--];
    00125
    00126   /* now we must maintain relation between parent and its children:
    00127    *   parent <= any child
    00128    * from head down to the tail */
    00129   int child  = 2; // first child is at [parent * 2]
    00130
    00131   /* while children are valid */
    00132   while (child <= m_size) {
    00133     /* choose the smaller child */
    00134     if (child < m_size && *m_items[child + 1] < *m_items[child])
    00135       child++;
    00136     /* is it smaller than our parent? */
    00137     if (!(*m_items[child] < new_item)) {
    00138       /* the smaller child is still bigger or same as parent => we are done */
    00139       break;
    00140     }
    00141     /* if smaller child is smaller than parent, it will become new parent */
    00142     m_items[gap] = m_items[child];
    00143     gap = child;
    00144     /* where do we have our new children? */
    00145     child = gap * 2;
    00146   }
    00147   /* move last item to the proper place */
    00148   if (m_size > 0) m_items[gap] = &new_item;
    00149   CheckConsistency();
    00150 }
    00151
    00152 template <class Titem_>
    00153 inline void CBinaryHeapT<Titem_>::RemoveByIdx(int idx)
    00154 {
    00155   /* at position idx we have a gap now */
    00156   int gap = idx;
    00157   Titem_& last = *m_items[m_size];
    00158   if (idx < m_size) {
    00159     assert(idx >= 1);
    00160     m_size--;
    00161     /* and the candidate item for fixing this gap is our last item 'last'
    00162      * Move gap / last item up: */
    00163     while (gap > 1)
    00164     {
    00165       /* compare [gap] with its parent */
    00166       int parent = gap / 2;
    00167       if (last < *m_items[parent]) {
    00168         m_items[gap] = m_items[parent];
    00169         gap = parent;
    00170       } else {
    00171         /* we don't need to continue upstairs */
    00172         break;
    00173       }
    00174     }
    00175
    00176     /* Heapify (move gap) down: */
    00177     while (true) {
    00178       /* where we do have our children? */
    00179       int child  = gap * 2; // first child is at [parent * 2]
    00180       if (child > m_size) break;
    00181       /* choose the smaller child */
    00182       if (child < m_size && *m_items[child + 1] < *m_items[child])
    00183         child++;
    00184       /* is it smaller than our parent? */
    00185       if (!(*m_items[child] < last)) {
    00186         /* the smaller child is still bigger or same as parent => we are done */
    00187         break;
    00188       }
    00189       /* if smaller child is smaller than parent, it will become new parent */
    00190       m_items[gap] = m_items[child];
    00191       gap = child;
    00192     }
    00193     /* move parent to the proper place */
    00194     if (m_size > 0) m_items[gap] = &last;
    00195   } else {
    00196     assert(idx == m_size);
    00197     m_size--;
    00198   }
    00199   CheckConsistency();
    00200 }
    00201
    00202 template <class Titem_>
    00203 inline int CBinaryHeapT<Titem_>::FindLinear(const Titem_& item) const
    00204 {
    00205   if (IsEmpty()) return 0;
    00206   for (ItemPtr *ppI = m_items + 1, *ppLast = ppI + m_size; ppI <= ppLast; ppI++) {
    00207     if (*ppI == &item) {
    00208       return ppI - m_items;
    00209     }
    00210   }
    00211   return 0;
    00212 }
    00213
    00214 template <class Titem_>
    00215 FORCEINLINE void CBinaryHeapT<Titem_>::CheckConsistency()
    00216 {
    00217   /* enable it if you suspect binary heap doesn't work well */
    00218 #if 0
    00219   for (int child = 2; child <= m_size; child++) {
    00220     int parent = child / 2;
    00221     assert(!(m_items[child] < m_items[parent]));
    00222   }
    00223 #endif
    00224 }
    00225
    00226
    00227 #endif /* BINARYHEAP_HPP */