Generic Min Heap Implementation in Java












1














There is a Min-Heap implementation in Java. BubbleUp() and BubbleDown() could have been coded using recursion but I prefer without recursion. Would there be a major difference if had used recursion?



package heap;

import interfaces.queue.PriorityQueue;
import java.util.Arrays;
import java.util.stream.Collectors;

public class Heap<T extends Comparable<T>> implements
PriorityQueue<T>
{
private T elements;
private int size;
private int capacity;

public Heap()
{
this(500);
}

public Heap(int capacity)
{
this.capacity = capacity;
size = 0;
elements = (T) new Comparable[this.capacity];
}

public int size()
{
return size;
}

public boolean isEmpty()
{
return size() == 0;
}

@Override
public void add(T data) throws Exception
{
if(size() >= capacity) throw new Exception("Heap is full");
elements[size++] = data;
bubbleUp();
}

private void bubbleUp()
{
int child = size() - 1;
int parent = (child-1)/2;

while(parent >= 0 && elements[child].compareTo(elements[parent]) < 0)
{
swap(child, parent);
child = parent;
parent = (child-1)/2;
}
}

@Override
public T removeMin() throws Exception
{
if(isEmpty()) throw new Exception("Empty heap");
T root = elements[0];
swap(size()-1, 0);
elements[size()-1] = null;
size--;
bubbleDown();
return root;
}

private void bubbleDown()
{
int parent = 0;
int leftChild = 2*parent + 1;
int rightChild = 2*parent + 2;

int choice = compareAndPick(leftChild, rightChild);

while(choice != -1)
{
swap(choice, parent);
parent = choice;
choice = compareAndPick(2*choice+1, 2*choice+2);
}
}

private int compareAndPick(int leftChild, int rightChild)
{
if(leftChild >= capacity || elements[leftChild] == null) return -1;
if((elements[leftChild].compareTo(elements[rightChild]) <= 0)|| (elements[rightChild] == null))
return leftChild;
return rightChild;
}

private void swap(int child, int parent)
{
T t = elements[child];
elements[child] = elements[parent];
elements[parent] = t;
}

@Override
public String toString()
{
return Arrays.stream(elements)
.filter(element -> element != null)
.collect(Collectors.toList()).toString();
}
}









share|improve this question



























    1














    There is a Min-Heap implementation in Java. BubbleUp() and BubbleDown() could have been coded using recursion but I prefer without recursion. Would there be a major difference if had used recursion?



    package heap;

    import interfaces.queue.PriorityQueue;
    import java.util.Arrays;
    import java.util.stream.Collectors;

    public class Heap<T extends Comparable<T>> implements
    PriorityQueue<T>
    {
    private T elements;
    private int size;
    private int capacity;

    public Heap()
    {
    this(500);
    }

    public Heap(int capacity)
    {
    this.capacity = capacity;
    size = 0;
    elements = (T) new Comparable[this.capacity];
    }

    public int size()
    {
    return size;
    }

    public boolean isEmpty()
    {
    return size() == 0;
    }

    @Override
    public void add(T data) throws Exception
    {
    if(size() >= capacity) throw new Exception("Heap is full");
    elements[size++] = data;
    bubbleUp();
    }

    private void bubbleUp()
    {
    int child = size() - 1;
    int parent = (child-1)/2;

    while(parent >= 0 && elements[child].compareTo(elements[parent]) < 0)
    {
    swap(child, parent);
    child = parent;
    parent = (child-1)/2;
    }
    }

    @Override
    public T removeMin() throws Exception
    {
    if(isEmpty()) throw new Exception("Empty heap");
    T root = elements[0];
    swap(size()-1, 0);
    elements[size()-1] = null;
    size--;
    bubbleDown();
    return root;
    }

    private void bubbleDown()
    {
    int parent = 0;
    int leftChild = 2*parent + 1;
    int rightChild = 2*parent + 2;

    int choice = compareAndPick(leftChild, rightChild);

    while(choice != -1)
    {
    swap(choice, parent);
    parent = choice;
    choice = compareAndPick(2*choice+1, 2*choice+2);
    }
    }

    private int compareAndPick(int leftChild, int rightChild)
    {
    if(leftChild >= capacity || elements[leftChild] == null) return -1;
    if((elements[leftChild].compareTo(elements[rightChild]) <= 0)|| (elements[rightChild] == null))
    return leftChild;
    return rightChild;
    }

    private void swap(int child, int parent)
    {
    T t = elements[child];
    elements[child] = elements[parent];
    elements[parent] = t;
    }

    @Override
    public String toString()
    {
    return Arrays.stream(elements)
    .filter(element -> element != null)
    .collect(Collectors.toList()).toString();
    }
    }









    share|improve this question

























      1












      1








      1







      There is a Min-Heap implementation in Java. BubbleUp() and BubbleDown() could have been coded using recursion but I prefer without recursion. Would there be a major difference if had used recursion?



      package heap;

      import interfaces.queue.PriorityQueue;
      import java.util.Arrays;
      import java.util.stream.Collectors;

      public class Heap<T extends Comparable<T>> implements
      PriorityQueue<T>
      {
      private T elements;
      private int size;
      private int capacity;

      public Heap()
      {
      this(500);
      }

      public Heap(int capacity)
      {
      this.capacity = capacity;
      size = 0;
      elements = (T) new Comparable[this.capacity];
      }

      public int size()
      {
      return size;
      }

      public boolean isEmpty()
      {
      return size() == 0;
      }

      @Override
      public void add(T data) throws Exception
      {
      if(size() >= capacity) throw new Exception("Heap is full");
      elements[size++] = data;
      bubbleUp();
      }

      private void bubbleUp()
      {
      int child = size() - 1;
      int parent = (child-1)/2;

      while(parent >= 0 && elements[child].compareTo(elements[parent]) < 0)
      {
      swap(child, parent);
      child = parent;
      parent = (child-1)/2;
      }
      }

      @Override
      public T removeMin() throws Exception
      {
      if(isEmpty()) throw new Exception("Empty heap");
      T root = elements[0];
      swap(size()-1, 0);
      elements[size()-1] = null;
      size--;
      bubbleDown();
      return root;
      }

      private void bubbleDown()
      {
      int parent = 0;
      int leftChild = 2*parent + 1;
      int rightChild = 2*parent + 2;

      int choice = compareAndPick(leftChild, rightChild);

      while(choice != -1)
      {
      swap(choice, parent);
      parent = choice;
      choice = compareAndPick(2*choice+1, 2*choice+2);
      }
      }

      private int compareAndPick(int leftChild, int rightChild)
      {
      if(leftChild >= capacity || elements[leftChild] == null) return -1;
      if((elements[leftChild].compareTo(elements[rightChild]) <= 0)|| (elements[rightChild] == null))
      return leftChild;
      return rightChild;
      }

      private void swap(int child, int parent)
      {
      T t = elements[child];
      elements[child] = elements[parent];
      elements[parent] = t;
      }

      @Override
      public String toString()
      {
      return Arrays.stream(elements)
      .filter(element -> element != null)
      .collect(Collectors.toList()).toString();
      }
      }









      share|improve this question













      There is a Min-Heap implementation in Java. BubbleUp() and BubbleDown() could have been coded using recursion but I prefer without recursion. Would there be a major difference if had used recursion?



      package heap;

      import interfaces.queue.PriorityQueue;
      import java.util.Arrays;
      import java.util.stream.Collectors;

      public class Heap<T extends Comparable<T>> implements
      PriorityQueue<T>
      {
      private T elements;
      private int size;
      private int capacity;

      public Heap()
      {
      this(500);
      }

      public Heap(int capacity)
      {
      this.capacity = capacity;
      size = 0;
      elements = (T) new Comparable[this.capacity];
      }

      public int size()
      {
      return size;
      }

      public boolean isEmpty()
      {
      return size() == 0;
      }

      @Override
      public void add(T data) throws Exception
      {
      if(size() >= capacity) throw new Exception("Heap is full");
      elements[size++] = data;
      bubbleUp();
      }

      private void bubbleUp()
      {
      int child = size() - 1;
      int parent = (child-1)/2;

      while(parent >= 0 && elements[child].compareTo(elements[parent]) < 0)
      {
      swap(child, parent);
      child = parent;
      parent = (child-1)/2;
      }
      }

      @Override
      public T removeMin() throws Exception
      {
      if(isEmpty()) throw new Exception("Empty heap");
      T root = elements[0];
      swap(size()-1, 0);
      elements[size()-1] = null;
      size--;
      bubbleDown();
      return root;
      }

      private void bubbleDown()
      {
      int parent = 0;
      int leftChild = 2*parent + 1;
      int rightChild = 2*parent + 2;

      int choice = compareAndPick(leftChild, rightChild);

      while(choice != -1)
      {
      swap(choice, parent);
      parent = choice;
      choice = compareAndPick(2*choice+1, 2*choice+2);
      }
      }

      private int compareAndPick(int leftChild, int rightChild)
      {
      if(leftChild >= capacity || elements[leftChild] == null) return -1;
      if((elements[leftChild].compareTo(elements[rightChild]) <= 0)|| (elements[rightChild] == null))
      return leftChild;
      return rightChild;
      }

      private void swap(int child, int parent)
      {
      T t = elements[child];
      elements[child] = elements[parent];
      elements[parent] = t;
      }

      @Override
      public String toString()
      {
      return Arrays.stream(elements)
      .filter(element -> element != null)
      .collect(Collectors.toList()).toString();
      }
      }






      java heap priority-queue






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Dec 15 at 0:02









      Hamidur Rahman

      536




      536






















          2 Answers
          2






          active

          oldest

          votes


















          2














          Exception handling



          Throwing Exception is not recommended because it's too generic.
          It doesn't give a clue to the caller as to what went wrong.
          It's recommended to use specific exceptions.



          When implementing a collection abstract data type, it's good to take a look at a similar interface in the standard library, for example Deque:




          • How do they handle when an element cannot be added due to capacity restrictions? They throw IllegalArgumentException.

          • How do they handle when an element is requested but the collection is empty? They throw NoSuchElementException.


          As you see, suitable specific exceptions already exist.
          Also notice that these exceptions are unchecked.
          It means that callers of these methods don't have to catch them.
          And that seems an appropriate decision,
          since the situations in which these exceptions can be thrown are quite unexpected, and should not happen under normal circumstances.



          Redundant capacity variable



          The member variable capacity is redundant.
          The same information already exists in elements.length.



          Make members final when possible



          Since elements is never reassigned, it would be good to make it final,
          so that you cannot reassign by mistake.



          Overriding toString



          Keep in mind that toString is not intended for "pretty-printing".



          And for printing the content of the heap,
          this implementation doesn't look useful to me.
          With the null values removed,
          the structure of the heap is not visible,
          and without the structure, the ordering of the elements is meaningless,
          which can be misleading.



          For printing the content of the heap I would suggest adding a dedicated method,
          keep the nulls, and print values of the first size elements.






          share|improve this answer





























            1














            I do not endorse recursion when a clean and simple iterative iterative solution is readily available. You did the right thing.



            The only problem I have is with compareAndPick implementation. First, rightChild is not tested against capacity, and may cause an out-of-bounds access. Second, testing elements[rightChild] against null looks too late (how does compareTo(null) behave?). Finally, there is really no need to test both an index against capacity and an object against nullness: index < size guarantees both.



            You may consider renaming compareAndPick to selectSmallestChild (and choice to child).



            Also, I recommend to leave the children computation to compareAndPick, and have a terser version of bubbleDown loop:



                while ((child = selectSmallestChild(parent)) != -1) {
            swap(child, parent);
            parent = child;
            }





            share|improve this answer





















              Your Answer





              StackExchange.ifUsing("editor", function () {
              return StackExchange.using("mathjaxEditing", function () {
              StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
              StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
              });
              });
              }, "mathjax-editing");

              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "196"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: false,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














              draft saved

              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209701%2fgeneric-min-heap-implementation-in-java%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              2














              Exception handling



              Throwing Exception is not recommended because it's too generic.
              It doesn't give a clue to the caller as to what went wrong.
              It's recommended to use specific exceptions.



              When implementing a collection abstract data type, it's good to take a look at a similar interface in the standard library, for example Deque:




              • How do they handle when an element cannot be added due to capacity restrictions? They throw IllegalArgumentException.

              • How do they handle when an element is requested but the collection is empty? They throw NoSuchElementException.


              As you see, suitable specific exceptions already exist.
              Also notice that these exceptions are unchecked.
              It means that callers of these methods don't have to catch them.
              And that seems an appropriate decision,
              since the situations in which these exceptions can be thrown are quite unexpected, and should not happen under normal circumstances.



              Redundant capacity variable



              The member variable capacity is redundant.
              The same information already exists in elements.length.



              Make members final when possible



              Since elements is never reassigned, it would be good to make it final,
              so that you cannot reassign by mistake.



              Overriding toString



              Keep in mind that toString is not intended for "pretty-printing".



              And for printing the content of the heap,
              this implementation doesn't look useful to me.
              With the null values removed,
              the structure of the heap is not visible,
              and without the structure, the ordering of the elements is meaningless,
              which can be misleading.



              For printing the content of the heap I would suggest adding a dedicated method,
              keep the nulls, and print values of the first size elements.






              share|improve this answer


























                2














                Exception handling



                Throwing Exception is not recommended because it's too generic.
                It doesn't give a clue to the caller as to what went wrong.
                It's recommended to use specific exceptions.



                When implementing a collection abstract data type, it's good to take a look at a similar interface in the standard library, for example Deque:




                • How do they handle when an element cannot be added due to capacity restrictions? They throw IllegalArgumentException.

                • How do they handle when an element is requested but the collection is empty? They throw NoSuchElementException.


                As you see, suitable specific exceptions already exist.
                Also notice that these exceptions are unchecked.
                It means that callers of these methods don't have to catch them.
                And that seems an appropriate decision,
                since the situations in which these exceptions can be thrown are quite unexpected, and should not happen under normal circumstances.



                Redundant capacity variable



                The member variable capacity is redundant.
                The same information already exists in elements.length.



                Make members final when possible



                Since elements is never reassigned, it would be good to make it final,
                so that you cannot reassign by mistake.



                Overriding toString



                Keep in mind that toString is not intended for "pretty-printing".



                And for printing the content of the heap,
                this implementation doesn't look useful to me.
                With the null values removed,
                the structure of the heap is not visible,
                and without the structure, the ordering of the elements is meaningless,
                which can be misleading.



                For printing the content of the heap I would suggest adding a dedicated method,
                keep the nulls, and print values of the first size elements.






                share|improve this answer
























                  2












                  2








                  2






                  Exception handling



                  Throwing Exception is not recommended because it's too generic.
                  It doesn't give a clue to the caller as to what went wrong.
                  It's recommended to use specific exceptions.



                  When implementing a collection abstract data type, it's good to take a look at a similar interface in the standard library, for example Deque:




                  • How do they handle when an element cannot be added due to capacity restrictions? They throw IllegalArgumentException.

                  • How do they handle when an element is requested but the collection is empty? They throw NoSuchElementException.


                  As you see, suitable specific exceptions already exist.
                  Also notice that these exceptions are unchecked.
                  It means that callers of these methods don't have to catch them.
                  And that seems an appropriate decision,
                  since the situations in which these exceptions can be thrown are quite unexpected, and should not happen under normal circumstances.



                  Redundant capacity variable



                  The member variable capacity is redundant.
                  The same information already exists in elements.length.



                  Make members final when possible



                  Since elements is never reassigned, it would be good to make it final,
                  so that you cannot reassign by mistake.



                  Overriding toString



                  Keep in mind that toString is not intended for "pretty-printing".



                  And for printing the content of the heap,
                  this implementation doesn't look useful to me.
                  With the null values removed,
                  the structure of the heap is not visible,
                  and without the structure, the ordering of the elements is meaningless,
                  which can be misleading.



                  For printing the content of the heap I would suggest adding a dedicated method,
                  keep the nulls, and print values of the first size elements.






                  share|improve this answer












                  Exception handling



                  Throwing Exception is not recommended because it's too generic.
                  It doesn't give a clue to the caller as to what went wrong.
                  It's recommended to use specific exceptions.



                  When implementing a collection abstract data type, it's good to take a look at a similar interface in the standard library, for example Deque:




                  • How do they handle when an element cannot be added due to capacity restrictions? They throw IllegalArgumentException.

                  • How do they handle when an element is requested but the collection is empty? They throw NoSuchElementException.


                  As you see, suitable specific exceptions already exist.
                  Also notice that these exceptions are unchecked.
                  It means that callers of these methods don't have to catch them.
                  And that seems an appropriate decision,
                  since the situations in which these exceptions can be thrown are quite unexpected, and should not happen under normal circumstances.



                  Redundant capacity variable



                  The member variable capacity is redundant.
                  The same information already exists in elements.length.



                  Make members final when possible



                  Since elements is never reassigned, it would be good to make it final,
                  so that you cannot reassign by mistake.



                  Overriding toString



                  Keep in mind that toString is not intended for "pretty-printing".



                  And for printing the content of the heap,
                  this implementation doesn't look useful to me.
                  With the null values removed,
                  the structure of the heap is not visible,
                  and without the structure, the ordering of the elements is meaningless,
                  which can be misleading.



                  For printing the content of the heap I would suggest adding a dedicated method,
                  keep the nulls, and print values of the first size elements.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Dec 15 at 9:57









                  janos

                  97.1k12124350




                  97.1k12124350

























                      1














                      I do not endorse recursion when a clean and simple iterative iterative solution is readily available. You did the right thing.



                      The only problem I have is with compareAndPick implementation. First, rightChild is not tested against capacity, and may cause an out-of-bounds access. Second, testing elements[rightChild] against null looks too late (how does compareTo(null) behave?). Finally, there is really no need to test both an index against capacity and an object against nullness: index < size guarantees both.



                      You may consider renaming compareAndPick to selectSmallestChild (and choice to child).



                      Also, I recommend to leave the children computation to compareAndPick, and have a terser version of bubbleDown loop:



                          while ((child = selectSmallestChild(parent)) != -1) {
                      swap(child, parent);
                      parent = child;
                      }





                      share|improve this answer


























                        1














                        I do not endorse recursion when a clean and simple iterative iterative solution is readily available. You did the right thing.



                        The only problem I have is with compareAndPick implementation. First, rightChild is not tested against capacity, and may cause an out-of-bounds access. Second, testing elements[rightChild] against null looks too late (how does compareTo(null) behave?). Finally, there is really no need to test both an index against capacity and an object against nullness: index < size guarantees both.



                        You may consider renaming compareAndPick to selectSmallestChild (and choice to child).



                        Also, I recommend to leave the children computation to compareAndPick, and have a terser version of bubbleDown loop:



                            while ((child = selectSmallestChild(parent)) != -1) {
                        swap(child, parent);
                        parent = child;
                        }





                        share|improve this answer
























                          1












                          1








                          1






                          I do not endorse recursion when a clean and simple iterative iterative solution is readily available. You did the right thing.



                          The only problem I have is with compareAndPick implementation. First, rightChild is not tested against capacity, and may cause an out-of-bounds access. Second, testing elements[rightChild] against null looks too late (how does compareTo(null) behave?). Finally, there is really no need to test both an index against capacity and an object against nullness: index < size guarantees both.



                          You may consider renaming compareAndPick to selectSmallestChild (and choice to child).



                          Also, I recommend to leave the children computation to compareAndPick, and have a terser version of bubbleDown loop:



                              while ((child = selectSmallestChild(parent)) != -1) {
                          swap(child, parent);
                          parent = child;
                          }





                          share|improve this answer












                          I do not endorse recursion when a clean and simple iterative iterative solution is readily available. You did the right thing.



                          The only problem I have is with compareAndPick implementation. First, rightChild is not tested against capacity, and may cause an out-of-bounds access. Second, testing elements[rightChild] against null looks too late (how does compareTo(null) behave?). Finally, there is really no need to test both an index against capacity and an object against nullness: index < size guarantees both.



                          You may consider renaming compareAndPick to selectSmallestChild (and choice to child).



                          Also, I recommend to leave the children computation to compareAndPick, and have a terser version of bubbleDown loop:



                              while ((child = selectSmallestChild(parent)) != -1) {
                          swap(child, parent);
                          parent = child;
                          }






                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Dec 15 at 1:01









                          vnp

                          38.5k13097




                          38.5k13097






























                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Code Review Stack Exchange!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              Use MathJax to format equations. MathJax reference.


                              To learn more, see our tips on writing great answers.





                              Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                              Please pay close attention to the following guidance:


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209701%2fgeneric-min-heap-implementation-in-java%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              Сан-Квентин

                              Алькесар

                              Josef Freinademetz