PageRenderTime 58ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/CSharp/Sort/HeapSort/Heap/Sort/Heap.cs

https://bitbucket.org/cnhume/samples
C# | 329 lines | 164 code | 42 blank | 123 comment | 28 complexity | e7184b40611112b51e8413b55ce623f3 MD5 | raw file
  1. //
  2. // Copyright (C) 2020, Christopher N. Hume. All rights reserved.
  3. //
  4. // You should have received a copy of the MIT License along with this program.
  5. // If not, see https://opensource.org/licenses/MIT.
  6. //
  7. // 2017-03-19 CNHume Added Intellisense
  8. // 2017-03-18 CNHume Renamed IsInverted to IsAscending; simplified control flow
  9. // 2014-12-27 CNHume Renamed Sift() and Add() methods to SiftDown() and SiftUp()
  10. // 2012-03-31 CNHume Created Heap Class to implement Priority Queues
  11. //
  12. // References:
  13. //
  14. // This object oriented implementation descends from an earlier C implementation
  15. // written and refined over the course of 1988. The approach was informed by an
  16. // article on the Heap data structure written by Digital Equipment Corporation's
  17. // John Sopka and appearing in the Newsletter of the Merrimack Valley Chapter of
  18. // the ACM circa 1980. C was not yet in wide use; and John's implementation was
  19. // written in FORTRAN.
  20. //
  21. // Notes:
  22. //
  23. // A set of Entries and an Ordering Relation constitute a Heap when the Entries are in a
  24. // Tree such that the Ordering Relation holds between each Entry and any of its Children.
  25. // A Heap is a partially ordered Tree, where the relation does not hold between siblings.
  26. //
  27. // A Binary Tree is implemented over an array by adopting the convention that an Entry's
  28. // Children are maintained at the sequential pair of Indicies formed by doubling the one-
  29. // based Index of their Parent. Only indicies less than the array Length are considered.
  30. //
  31. // Sample Usage:
  32. //
  33. // var items = new[] { 101, 3, 44, 55, 8, 17, 6 };
  34. // var sorter = new Heap<Int32>(items);
  35. // sorter.Sort();
  36. //
  37. // Left to Right Unit Test:
  38. //
  39. // var items = new[] { 101, 3, 44, 55, 8, 17, 6 };
  40. // var sorter = new Heap<Int32>(items, 0);
  41. // sorter.Invert();
  42. //
  43. // for (var final = 0; final < items.Length; final++)
  44. // sorter.SiftUp(items[final]);
  45. //
  46. // sorter.Sort();
  47. //
  48. namespace Sort {
  49. using Exceptions;
  50. using System;
  51. using System.Collections; // For non-generic IEnumerable
  52. using System.Collections.Generic;
  53. public class Heap<T> : ICloneable, IEnumerable<T> where T : IComparable {
  54. #region Fields
  55. private T[] entries;
  56. protected Int32 counter;
  57. #endregion
  58. #region Constructors
  59. /// <summary>Heap Constructor</summary>
  60. /// <param name="entries">Entries array</param>
  61. /// <param name="count"># of entries to use in Heap</param>
  62. /// <param name="ascending">Initial Heap sense</param>
  63. public Heap(T[] entries, Int32 count, Boolean ascending = true) {
  64. //IsSorted = false;
  65. IsAscending = ascending;
  66. Entries = entries;
  67. Count = count; // This Count assignment triggers Build()
  68. }
  69. /// <summary>Heap Constructor</summary>
  70. /// <param name="entries">Entries array</param>
  71. public Heap(T[] entries)
  72. : this(entries, entries == null ? 0 : entries.Length) {
  73. }
  74. /// <summary>Heap Constructor</summary>
  75. public Heap()
  76. : this((T[])null) {
  77. }
  78. #endregion
  79. #region Interface Methods
  80. /// <summary>Deep Copy</summary>
  81. /// <param name="target">Target Heap</param>
  82. public void CopyTo(Heap<T> target) {
  83. if (Length > 0) {
  84. target.entries = new T[Length];
  85. Entries.CopyTo(target.entries, 0);
  86. }
  87. target.counter = Count;
  88. target.IsAscending = IsAscending;
  89. }
  90. /// <summary>Copy Constructor</summary>
  91. /// <param name="heap">Heap to copy</param>
  92. public Heap(Heap<T> heap) {
  93. heap.CopyTo(this);
  94. }
  95. /// <summary>Clone Heap</summary>
  96. /// <returns>Clone of current Heap</returns>
  97. public Object Clone() {
  98. return new Heap<T>(this);
  99. }
  100. #endregion
  101. #region Methods
  102. /// <summary>Index of parent</summary>
  103. /// <param name="child">Child index</param>
  104. protected Int32 Parent(Int32 child) {
  105. return (child - 1) / 2;
  106. }
  107. /// <summary>Index of left child</summary>
  108. /// <param name="parent">Parent index</param>
  109. /// <remarks>right = left + 1</remarks>
  110. protected Int32 Left(Int32 parent) {
  111. return parent * 2 + 1;
  112. }
  113. /// <summary>Used internally by Build() to add the entry at the Root Index.</summary>
  114. /// <remarks>O(n): Assumes both children are valid Heaps.</remarks>
  115. /// <param name="value">Value to add</param>
  116. /// <param name="root">Interim Root</param>
  117. protected void SiftDown(T value, Int32 root) {
  118. if (root < 0 || counter <= root)
  119. throw new IndexOutOfRangeException();
  120. //IsSorted = false;
  121. var left = Left(root);
  122. while (left < counter) {
  123. var right = left + 1; // Select greater child:
  124. var bRight = right < counter && entries[left].CompareTo(entries[right]) < 0 == IsAscending;
  125. var child = bRight ? right : left;
  126. if (entries[child].CompareTo(value) < 0 == IsAscending)
  127. break;
  128. // Sift Down
  129. entries[root] = entries[child];
  130. root = child; // Continue with Child Heap
  131. left = Left(root);
  132. }
  133. entries[root] = value;
  134. }
  135. /// <summary>Rearrange Entries into a Heap.</summary>
  136. /// <remarks>O(n)</remarks>
  137. protected void Build() { // aka, Heapify
  138. if (counter < 2) return;
  139. //
  140. // Calling SiftDown() proceeds from right to left and reduces
  141. // the time complexity of this method from O(n log n) to O(n).
  142. //
  143. // Half of the nodes are leaves; and the expected number of
  144. // ordering operations depends on the height of the Heap.
  145. //
  146. for (var final = counter - 1; final >= 0; final--)
  147. SiftDown(entries[final], final);
  148. }
  149. /// <summary>Invert Heap sense.</summary>
  150. /// <remarks>O(n)</remarks>
  151. public void Invert() {
  152. IsAscending = !IsAscending;
  153. Build();
  154. }
  155. /// <summary>Add new element to a Heap.</summary>
  156. /// <remarks>O(n): Not required for a HeapSort.</remarks>
  157. /// <param name="value">Value to add</param>
  158. public void SiftUp(T value) {
  159. if (Length <= counter)
  160. throw new HeapOverflowException();
  161. //IsSorted = false;
  162. var child = counter++; // Post-Increment Count
  163. while (child > 0) {
  164. var parent = Parent(child);
  165. if (value.CompareTo(entries[parent]) < 0 == IsAscending)
  166. break;
  167. // Sift Up:
  168. entries[child] = entries[parent];
  169. child = parent; // Continue with Parent Heap
  170. }
  171. entries[child] = value;
  172. }
  173. /// <summary>Remove root.</summary>
  174. /// <remarks>O(n)</remarks>
  175. /// <returns>Value of root entry</returns>
  176. public T Remove() { // Remove minimum entry from the root of the Heap
  177. if (counter < 1)
  178. throw new HeapUnderflowException();
  179. var value = entries[0];
  180. if (--counter > 0) // Pre-Decrement Count
  181. SiftDown(entries[counter], 0); // ReverseSort() overwrites this final Entry
  182. return value;
  183. }
  184. #endregion
  185. #region Sort Methods
  186. /// <summary>Perform HeapSort on the Entries array</summary>
  187. /// <remarks>O(n log n)</remarks>
  188. public void Sort() {
  189. foreach (var entry in this) ;
  190. }
  191. /// <summary>Perform Reverse HeapSort on the Entries array</summary>
  192. /// <remarks>O(n log n)</remarks>
  193. public void ReverseSort() {
  194. Sort();
  195. Reverse();
  196. }
  197. /// <summary>Reverse Entries and Invert Heap sense</summary>
  198. /// <remarks>O(n): May be used after Sort() to restore Heap sense</remarks>
  199. protected void Reverse() {
  200. IsAscending = !IsAscending;
  201. if (counter < 2) return;
  202. for (Int32 left = 0, right = counter - 1; left < right; left++, right--)
  203. Swap(Entries, left, right);
  204. }
  205. /// <summary>Swap entries at the left and right indicies.</summary>
  206. /// <param name="entries"></param>
  207. /// <param name="left">Left index</param>
  208. /// <param name="right">Right index</param>
  209. protected static void Swap(T[] entries, int left, int right) {
  210. var entry = entries[left];
  211. entries[left] = entries[right];
  212. entries[right] = entry;
  213. }
  214. #endregion
  215. #region Enumerator
  216. /// <summary>Generic Heap enumerator</summary>
  217. /// <remarks>
  218. /// This non-standard Enumerator allows efficient removal of elements in priority
  219. /// order. Entries are temporarily removed while the Enumeration is in progress.
  220. ///
  221. /// Once the final element has been removed, the count is reestablished and Heap Elements
  222. /// appear in Reverse Order. This improves performance; but IsAscending must be inverted
  223. /// to restore the original Heap sense.
  224. /// </remarks>
  225. /// <returns>Generic enumerator</returns>
  226. public IEnumerator<T> GetEnumerator() {
  227. var n = counter;
  228. while (counter > 0) { // Left-Hand Operand First:
  229. yield return entries[counter - 1] = Remove();
  230. }
  231. //IsSorted = true;
  232. IsAscending = !IsAscending;
  233. counter = n; // Prevent unecessary Build()
  234. }
  235. /// <summary>Get non-generic enumerator</summary>
  236. /// <remarks>Explicit implementation</remarks>
  237. /// <returns>Non-generic enumerator</returns>
  238. IEnumerator IEnumerable.GetEnumerator() {
  239. return GetEnumerator(); // Cast generic implementation to IEnumerable
  240. }
  241. #endregion
  242. #region Properties
  243. /// <summary>Heap sense</summary>
  244. public Boolean IsAscending { get; protected set; }
  245. //public Boolean IsSorted { get; protected set; }
  246. /// <summary>Entries array</summary>
  247. public T[] Entries {
  248. get {
  249. return entries;
  250. }
  251. set {
  252. counter = 0;
  253. entries = value;
  254. }
  255. }
  256. /// <summary>Length of Entries array</summary>
  257. /// <value>Entries array Length</value>
  258. public Int32 Length {
  259. get {
  260. return entries == null ? 0 : entries.Length;
  261. }
  262. }
  263. /// <summary>Count of entries currently in use</summary>
  264. /// <remarks>Count assignment performs Heap operations appropriate to the change in value</remarks>
  265. /// <value># of entries currently in use</value>
  266. public Int32 Count {
  267. get {
  268. return counter;
  269. }
  270. set {
  271. if (value < 0 || value > Length)
  272. throw new IndexOutOfRangeException();
  273. if (value <= counter)
  274. counter = value; // Truncate Heap
  275. else if (counter > 0) {
  276. while (counter < value) // Add new Entries
  277. SiftUp(entries[counter]);
  278. }
  279. else { // counter == 0
  280. counter = value; // Rebuild Heap
  281. Build();
  282. }
  283. }
  284. }
  285. #endregion
  286. }
  287. }