SearchHeap.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /**
  2. * SearchHeap:
  3. * Implementation of a Search Heap of type T.
  4. *
  5. * Designed and written by Jonathan E. Landrum.
  6. *
  7. * Copyright (c) 2014-2015 Jonathan E. Landrum. All Rights Reserved.
  8. */
  9. // Warnings are suppressed for easier compilation
  10. @SuppressWarnings("unchecked")
  11. public class SearchHeap<T> {
  12. // Root element
  13. private Node<T> root = new Node<T>();
  14. // Insertion point initially set to root
  15. private Node<T> ins = root;
  16. // Children and level counts
  17. private int numLevels = -1;
  18. /*
  19. * Constructors
  20. */
  21. public SearchHeap() { }
  22. public SearchHeap(Node<T> r) {
  23. root = r;
  24. numLevels = 0;
  25. }
  26. /*
  27. * Getters
  28. */
  29. public int getNumLevels() { return numLevels; }
  30. public int getNumElements() { return root.getNumElements(); }
  31. public T getMin() { return root.getMin(); }
  32. public T getMax() { return root.getMax(); }
  33. /*
  34. * getNthMin():
  35. * This method returns the nth minimum element in the heap.
  36. *
  37. * Parameter:
  38. * int n - The order of the element to be returned.
  39. *
  40. * Note:
  41. * This function expects a natural number for input. Non-
  42. * natural input will throw an exception. An input of "1" will
  43. * return the smallest element in the collection, functionally
  44. * equivalent to calling getMin().
  45. */
  46. public T getNthMin(int n) {
  47. // Define a current node that initially points to root
  48. Node<T> current = root;
  49. // Short circuit for erroneous input
  50. if (n > current.getNumElements() || n < 1) {
  51. throw new IndexOutOfBoundsException(n);
  52. }
  53. do {
  54. // Check if we're at the correct node
  55. if (n == 1) { return current.getMin(); }
  56. if (n - current.getNumElements() == 0) {
  57. if (current.hasMax()) {
  58. return current.getMax();
  59. } else {
  60. // We only get here if the algorithm selects this node,
  61. // but it shouldn't have
  62. throw new UnknownErrorException();
  63. }
  64. }
  65. // Recur through the tree to find the correct node
  66. if ((current.hasLeftChild()) &&
  67. (n <= current.getLeftChild().getNumElements() + 1)) {
  68. /*
  69. * This block says our target is not current's min
  70. * (the +1 part), but our target is in current's left subtree
  71. * (the current.getLeftChild().getNumElements() part).
  72. */
  73. // Subtract 1 from n to represent passing up current.getMin() as an option
  74. --n;
  75. // Change what current points to
  76. current = current.getLeftChild();
  77. } else if (current.hasRightChild()) {
  78. /*
  79. * This block says our target is in current's right subtree.
  80. */
  81. // Subtract the total number of elements in the left subtree and middle subtree + 1 from n
  82. // to represent passing up the left and middle subtrees as an option
  83. n = n - current.getLeftChild().getNumElements() - 1;
  84. // Change what current points to
  85. current = current.getRightChild();
  86. } else {
  87. /*
  88. * We only get here if current has no children (is a leaf),
  89. * but n > 2, an obvious error, given the checks at the
  90. * beginning of the loop
  91. */
  92. throw new UnknownErrorException();
  93. }
  94. } while (n > 0);
  95. // We only get here if something goes completely awry
  96. throw new UnknownErrorException();
  97. }
  98. /*
  99. * getNthMax():
  100. * This method returns the nth maximum element in the heap.
  101. *
  102. * Parameter:
  103. * int n - The order of the element to be returned.
  104. *
  105. * Note:
  106. * This function expects a natural number for input. Non-
  107. * natural input will throw an exception. An input of "1" will
  108. * return the largest element in the collection, functionally
  109. * equivalent to calling getMax().
  110. */
  111. public T getNthMax(int n) {
  112. // Define a current node that initially points to root
  113. Node<T> current = root;
  114. // Short circuit for erroneous input
  115. if (n > current.getNumElements() || n < 1) { throw new IndexOutOfBoundsException(n); }
  116. do {
  117. // Check if we're at the correct node
  118. if (n == 1) { return current.getMax(); }
  119. if (n - current.getNumElements() == 0) {
  120. if (current.hasMin()) {
  121. return current.getMin();
  122. } else {
  123. // We only get here if the algorithm selects this node, but it shouldn't have
  124. throw new UnknownErrorException();
  125. }
  126. }
  127. // Recur through the tree to find the correct node
  128. if ((current.hasRightChild()) && (n <= current.getRightChild().getNumElements() + 1)) {
  129. /*
  130. * This block says our target is not current's max (the +1 part),
  131. * but our target is in current's right subtree (the current.getRightChild().getNumElements() part).
  132. */
  133. // Subtract 1 from n to represent passing up current.getMax() as an option
  134. --n;
  135. // Change what current points to
  136. current = current.getRightChild();
  137. } else if (current.hasMidChild()) {
  138. // We have to be careful here, because we can't assume current has a right child
  139. // in the same way we could assume current has a left child in getNthMin().
  140. if ((current.hasRightChild()) && (n <= current.getRightChild().getNumElements() + current.getMidChild().getNumElements() + 1)) {
  141. /*
  142. * This block says our target is not current's max (the +1 part),
  143. * and our target is not in current's right subtree (the current.getRightChild().getNumElements() part),
  144. * but our target *is* in current's middle subtree (the current.getMidChild().getNumElements() part).
  145. */
  146. // Subtract the total number of elements in the right subtree + 1 from n
  147. // to represent passing up current.getMax() as well as current's right subtree as an option
  148. n = n - current.getRightChild().getNumElements() - 1;
  149. } else {
  150. // Otherwise, if current doesn't have a right child, just subtract 1 to represent passing up current.getMax().
  151. --n;
  152. }
  153. // Change what current points to
  154. current = current.getMidChild();
  155. } else if (current.hasLeftChild()) {
  156. /*
  157. * This block says we've exhausted our options, and our target has to be in current's left subtree.
  158. */
  159. if (current.hasMidChild()) {
  160. // We have to be careful here, too, just as above
  161. if (current.hasRightChild()) {
  162. // Subtract the total number of elements in the right and middle subtrees + 1 from n
  163. // to represent passing up current.getMax() as well as current's right and middle subtrees as options
  164. n = n - current.getRightChild().getNumElements() - current.getMidChild().getNumElements() - 1;
  165. } else {
  166. // Otherwise, if current doesn't have a right child, subtract the total number of elements
  167. // from the middle subtree + 1 to represent passing up the middle subtree and current.getMax().
  168. n = n - current.getMidChild().getNumElements() - 1;
  169. }
  170. // Otherwise, if there is no middle child and no right child,
  171. // just subtract 1 to represent passing up current.getMax().
  172. --n;
  173. }
  174. // Change what current points to
  175. current = current.getLeftChild();
  176. } else {
  177. /*
  178. * We only get here if current has no children (is a leaf), but n != 1 and n != 2,
  179. * an obvious error, given the checks at the beginning of the loop
  180. */
  181. throw new UnknownErrorException();
  182. }
  183. } while (n > 0);
  184. // We only get here if something goes completely awry
  185. throw new UnknownErrorException();
  186. }
  187. /*
  188. * insert():
  189. * This method adds a new element to the heap.
  190. *
  191. * Calls:
  192. * reheap()
  193. *
  194. * //////////////////\\\\\\\\\\\\\\\\\\
  195. * TO DO: Implement This
  196. * \\\\\\\\\\\\\\\\\\//////////////////
  197. *
  198. * Parameter:
  199. * T element - The element to be inserted into the heap.
  200. *
  201. * Note :
  202. * The structure of this method forms a natural less-than-
  203. * or-equal-to relation. As the collection has a least element,
  204. * it thus forms a total ordering.
  205. */
  206. public void insert(T element) {
  207. // Define a current node that initially references the insertion point
  208. Node<T> current = ins;
  209. // Insert the element
  210. if (!current.hasMin()) {
  211. /*
  212. * The node is completely empty; insert the element as current's min.
  213. */
  214. current.setMin(element);
  215. } else if (!current.hasMax()) {
  216. /*
  217. * The node is partially empty; determine which value goes in which slot.
  218. */
  219. if (current.getMin().compareTo(element) > 0) {
  220. // Current's min is greater than the element, so
  221. // it becomes max and the element becomes min.
  222. current.setMax(current.getMin());
  223. current.setMin(element);
  224. } else {
  225. // Current's min is less than or equal to the
  226. // element, so the element becomes the max.
  227. current.setMax(element);
  228. }
  229. } else {
  230. /*
  231. * The insertion point is full. Create a new node to hold
  232. * the element, and determine where to put the new node.
  233. */
  234. Node<T> newNode = new Node<T>(element);
  235. // Check to see where the new node goes.
  236. if (current.hasParent()) {
  237. /*
  238. * Avoids null pointer exceptions when current points to root.
  239. */
  240. if (current.isLeftChild()) {
  241. // Current is a left child, so newNode becomes the middle child.
  242. // Point the three nodes at each other.
  243. current.setRightSibling(newNode);
  244. current.getParent().setMidChild(newNode);
  245. newNode.setLeftSibling(current);
  246. newNode.setParent(current.getParent());
  247. } else if (current.isMidChild()) {
  248. // Current is a middle child, so newNode becomes the right child.
  249. // Point the three nodes at each other.
  250. current.setRightSibling(newNode);
  251. current.getParent().setRightChild(newNode);
  252. newNode.setLeftSibling(current);
  253. newNode.setParent(current.getParent());
  254. } else {
  255. /*
  256. * Current is a right child. Determine if we need to
  257. * move to a right subtree or create a new level.
  258. */
  259. // Iterate current up the tree until a right sibling is found. If no right sibling
  260. // is found, current is an external node, and a new level should be created.
  261. while (!current.hasRightSibling()) {
  262. if (current.hasParent()) {
  263. // Move current to the parent node.
  264. current = current.getParent();
  265. // Check to see if the new current has a right sibling.
  266. if (current.hasRightSibling()) {
  267. // Move current into adjacent subtree and terminate the loop.
  268. current = current.getRightSibling();
  269. break;
  270. }
  271. } else {
  272. // Current is the root node. Increment the
  273. // number of levels and terminate the loop.
  274. ++numLevels;
  275. break;
  276. }
  277. }
  278. // Iterate through the tree until the bottom row is reached.
  279. while (current.hasLeftChild()) {
  280. current = current.getLeftChild();
  281. }
  282. // Point current and the new node at each other.
  283. current.setLeftChild(newNode);
  284. newNode.setParent(current);
  285. }
  286. } else {
  287. /*
  288. * This case only happens when the insertion point is the root.
  289. */
  290. // Point the two nodes at each other.
  291. current.setLeftChild(newNode);
  292. newNode.setParent(current);
  293. }
  294. // Point ins at the new node.
  295. ins = newNode;
  296. }
  297. }
  298. }