5
\$\begingroup\$

Sort a stack in ascending order. You may only use one additional stack to hold items.

Any comments on my solution? Worst case complexities: Memory complexity: O(n) Runtime complexity: O(n^2)

Also, I implemented the Stack class as having Object items and then cast these into Integers to be more general. Is this a good way of doing this?

import org.junit.Test;

public class Solution {
  // Sort a stack in ascending order
  // May only use one additional stack to hold items
  // Stack supports push, pop, peek, and isEmpty
  private Stack stack;

  private Stack auxiliaryStack;

  @Test
  void testStackIncreasingOrder() {
    stack = new Stack();
    stack.push(3);
    stack.push(2);
    stack.push(1);
    System.out.println("original stack");
    printStack();
    sortStack();
    System.out.println("Sorted stack");
    printStack();
  }

  @Test
  void testEmptyStack(){
    stack = new Stack();
    System.out.println("original stack");
    printStack();
    sortStack();
    System.out.println("Sorted stack");
    printStack();
  }

  @Test
  void testStackRandomOrder() {
    stack = new Stack();
    stack.push(3);
    stack.push(2);
    stack.push(1);
    stack.push(5);
    stack.push(1);
    stack.push(1);
    stack.push(12);
    stack.push(0);
    System.out.println("original stack");
    printStack();
    sortStack();
    System.out.println("Sorted stack");
    printStack();
  }

  public static void main(String[] args) {
    Solution e = new Solution();
    System.out.println("Test reverse sorted stack");
    e.testStackIncreasingOrder();
    System.out.println("Test empty stack");
    e.testEmptyStack();
    System.out.println("Test randomly ordered stack");
    e.testStackRandomOrder();
  }

  public void sortStack() {
    auxiliaryStack = new Stack();
    if (stack.isEmpty()) {
      return;
    }

    while (!stack.isEmpty()) {
      Object topOfStack = stack.pop();
      if (auxiliaryStack.top != null) {
        Object topOfAuxiliary = auxiliaryStack.peek();
        if ((Integer) topOfStack > (Integer) topOfAuxiliary) {
          emptyAuxiliary();
        }
      }
      auxiliaryStack.push(topOfStack);
    }
//    System.out.println("AuxiliaryStack");
//    printAuxiliaryStack();
    emptyAuxiliary();
  }

  public void emptyAuxiliary() {
    // Put all the elements of the auxiliary stack back in
    // the original one
    while (!auxiliaryStack.isEmpty()) {
      Object element = auxiliaryStack.pop();
      stack.push(element);
    }
  }

  public Stack getStack() {
    return stack;
  }

  public void printStack() {
    Node current = stack.top;
    while (current != null) {
      System.out.println(current.data);
      current = current.next;
    }
  }

  public void printAuxiliaryStack() {
    Node current = auxiliaryStack.top;
    while (current != null) {
      System.out.println(current.data);
      current = current.next;
    }
  }

}

class Stack {
  Node top;

  Object pop() {
    if (top != null) {
      Object item = top.data;
      top = top.next;
      return item;
    }
    return null;
  }

  void push(Object item) {
    Node t = new Node(item);
    t.next = top;
    top = t;
  }

  Object peek() {
    return top.data;
  }

  boolean isEmpty() {
    if (top == null) {
      return true;
    }
    return false;
  }
}

class Node {
  Object data;

  Node next;

  public Node(Object item) {
    data = item;
  }

  public Node(Object item, Node n) {
    data = item;
    next = n;
  }
}
\$\endgroup\$

1 Answer 1

6
\$\begingroup\$

Generics

Generics... you want them. In your solution, they would be relatively easy to apply too. Consider your Node class:

class Node {
  Object data;

  Node next;

  public Node(Object item) {
    data = item;
  }

  public Node(Object item, Node n) {
    data = item;
    next = n;
  }
}

Now, that Node only works on Object values, but you want it to be an Integer. Using generics, this is a treat (while we are at it, we will make data and next private and final, and add getters for them...):

class Node<T> {

  private final T data;
  private final Node<T> next;

  public Node(T item) {
    data = item;
  }

  public Node(T item, Node<T> n) {
    data = item;
    next = n;
  }

  public T getData() {
    return data;
  }

  public Node<T> getNext() {
    return next;
  }

}

There, now Node is self contained, immutable, and generic. It can contain any object type, just specify the type you want when you construct it, for example:

Node<Integer> node = new Node<>(10);

Applying the same logic to your Stack class, we get:

class Stack<T> {
  Node<T> top;

  T pop() {
    if (top != null) {
      T item = top.getData();
      top = top.getNext();
      return item;
    }
    return null;
  }

  void push(T item) {
    Node t = new Node(item, top);
    // commented out, make Node immutable, pass in as constructor
    // t.next = top;
    top = t;
  }

  T peek() {
    return top.getData();
  }

  boolean isEmpty() {
    if (top == null) {
      return true;
    }
    return false;
  }
}

Right, that makes it generic, there are still a bunch of problems... but, it can be easy to use with:

Stack<Integer> stack = new Stack<>();

With the above stack, you should not need any casts from Object to Integer

Simplifications

Now, looking at some other parts of your code, let's simplify...

Chained constructors: whenever possible, have just one constructor that "does stuff" for your class, and have your other constructors call the main one. Your constructors look like:

  public Node(Object item) {
    data = item;
  }

  public Node(Object item, Node n) {
    data = item;
    next = n;
  }

Note that they both access the data = item item directly. It would be better to change your first constructor to be:

  public Node(T item) {
      this(item, null);
  }

The above constructor calls the second one with a null value.

Boolean expressions are boolean. If statements test a boolean condition. Why do both?

  boolean isEmpty() {
    if (top == null) {
      return true;
    }
    return false;
  }

This is a good one, it can be simply:

boolean isEmpty() {
  return top == null;
}

The push can be simplified too:

  void push(T item) {
    Node t = new Node(item, top);
    // commented out, make Node immutable, pass in as constructor
    // t.next = top;
    top = t;
  }

That can be simply:

  void push(T item) {
    top = new Node(item, top);
  }

Note that there is no need for the one-argument constructor now in the Node class.

Code duplication is always a red flag. Consider the following methods:

  public void printStack() {
    Node current = stack.top;
    while (current != null) {
      System.out.println(current.data);
      current = current.next;
    }
  }

  public void printAuxiliaryStack() {
    Node current = auxiliaryStack.top;
    while (current != null) {
      System.out.println(current.data);
      current = current.next;
    }
  }

Why have the method twice, when instead you can have just:

  public void printStack(Node<T> current) {
    while (current != null) {
      System.out.println(current.getData());
      current = current.getNext();
    }
  }

Now, you can call that method with either the stack or the auxilliaryStack top nodes... but, that method really belongs on the stack itself, not outside the stack. Move it to the stack, and it becomes:

  public void printStack() {
    Node<T> current = top;
    while (current != null) {
      System.out.println(current.getData());
      current = current.getNext();
    }
  }

Now you can simply do:

stack.printStack();
auxilliaryStack.printStack();

Bugs

The following is a bug:

  Object peek() {
    return top.data;
  }

What if the stack is empty? You will get a NullPointerException!

Instead, do:

  T peek() {
      return top == null ? null : top.getData();
  }

Sorting

Your algorithm looks sane, but the interface is ... cumbersome. You have a stack, and auxilliaryStack declared as members of the Solution class. Now, the rules of object orientation indicate that the data should be encapsulated. There are two logical ways to implement the sort that I can see. The one is to have a static method that takes a Stack as input, and sorts it in place (creating a temporary, and method-private auxStack to sort with). The other solution adds the sort method to the Stack class itself (and also has an internal auxStack). The second route is the better one.

public void sort() {
    if (isEmpty()) {
        return;
    }
    Stack<T> auxStack = new Stack<>();
    .....
}

I recommend you make the changes and see how it works out for you. You will find that the logic and flow of the structure is neater, and more manageable.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.