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 */