Stacks as an Abstract Data Type CS 1316
Stacks as an Abstract Data Type CS 1316: Representing Structure and Behavior
Story l l Stack: Another useful kind of list An example of an Abstract Data Type (ADT): • We can define the methods and the behavior • l apart from any implementation. There are multiple implementations, some better than others. Can use a stack to reverse a list in less time, but more space. • A classic tradeoff! Space for time.
Key idea #1: Introducing a Stack l Last-In-First-Out List • • First item in the list is the last one out. Last one in is first one out. I got here third! This is the top of the stack I got here second! I got here first!
New items go at the top I got here fourth! This is the new top of the stack I got here third! I got here second! I got here first!
Items only get removed from the top I got here fourth! And now I’m outta here! I got here third! This is the new (er, old) top of the stack I got here second! I got here first!
What can we do with stacks? l l push(an. Object): Tack a new object onto the top of the stack pop(): Pull the top (head) object off the stack. peek(): Get the top of the stack, but don’t remove it from the stack. size(): Return the size of the stack
Example stack Notice anything interesting about the order in and the order out? Welcome to Dr. Java. > Stack stack = new Stack() > stack. push("This") > stack. push("a") > stack. push("test") > stack. size() 4 > stack. peek() "test" > stack. pop() "a" > stack. pop() "is" > stack. pop() "This" > stack. pop() java. util. No. Such. Element. Exception:
Implementing a stack with a linked list import java. util. Linked. List; // Need for Linked. List public class Stack { /** Where we store the elements */ private Linked. List elements; /// Constructor //// public Stack() { elements = new Linked. List(); }
Stack methods //// Methods /// public void push(Object element){ // New elements go at the front elements. add. First(element); } public Object peek(){ return elements. get. First(); } public Object pop(){ Object to. Return = this. peek(); elements. remove. First(); return to. Return; } public int size(){return elements. size(); }
A stack is a stack, no matter what lies beneath. l Our description of the stack minus the implementation is an example of an abstract data type (ADT). • l An abstract type is a description of the methods that a data structure knows and what the methods do. We can actually write programs that use the abstract data type without specifying the implementation. • • There actually many implementations that will work for the given ADT. Some are better than others.
Building a Stack from an Array /** * Implementation of a stack as an array **/ public class Stack 2 { private static int ARRAYSIZE = 20; /** Where we'll store our elements */ private Object[] elements; /** Index where the top of the stack is */ private int top;
Stack 2 = array + top index /// Constructor //// public Stack 2() { elements = new Object[ARRAYSIZE]; top = 0; }
Stack 2 Methods //// Methods /// public void push(Object element){ // New elements go at the top elements[top]=element; // then add to the top++; if (top==ARRAYSIZE){ System. out. println("Stack overflow!"); } } public Object peek(){ if (top==0){ System. out. println("Stack empty!"); return null; } else{ return elements[top-1]; } } public Object pop(){ Object to. Return = this. peek(); top--; return to. Return; } /** Size is simply the top index */ public int size(){return top; }
Testing Stack 2 Welcome to Dr. Java. > Stack 2 stack = new Stack 2(); > stack. push("Matt") > stack. push("Katie") > stack. push("Jenny") > stack. size() 3 > stack. peek() "Jenny" > stack. pop() "Katie" > stack. pop() "Matt" > stack. pop() Stack empty! null
What are stacks good for? l l The algorithm for converting an equation into a tree uses a stack. As new functions get called, position in old functions get pushed on a stack. • So you always return to the last function you • • were in. If an error occurs, you get a stack trace. If your recursion goes into an infinite loop, what error do you get? Stack overflow!
A Stack Example: New Reverse l Recall our original implementation of reverse(). • • • l We go to the end of the original list to find the last(). We then remove() it (which involves walking the list until we find the one before last()) We then insert it at the end of the new list (via add(), which does last(). insert. After()). All told: For each node, we walk the whole list three times. • O(n*(3 n))=O(n 2)
Original Reverse /** * Reverse the list starting at this, * and return the last element of the list. * The last element becomes the FIRST element * of the list, and THIS goes to null. **/ public LLNode reverse() { LLNode reversed, temp; // Handle the first node outside the loop reversed = this. last(); this. remove(reversed); while (this. get. Next() != null) { temp = this. last(); this. remove(temp); reversed. add(temp); }; // Now put the head of the old list on the end of // the reversed list. reversed. add(this); // At this point, reversed // is the head of the list return reversed; }
Testing Reverse in Sound. List. Test() public void reverse. Test(){ Sound s = null; // For copying in sounds s = new Sound(File. Chooser. get. Media. Path("guzdial. wav")); Sound. Node root = new Sound. Node(s); s = new Sound(File. Chooser. get. Media. Path("is. wav")); Sound. Node one = new Sound. Node(s); root. last(). insert. After(one); s = new Sound(File. Chooser. get. Media. Path("scritch-q. wav")); Sound. Node two = new Sound. Node(s); root. last(). insert. After(two); s = new Sound(File. Chooser. get. Media. Path("clap-q. wav")); Sound. Node three = new Sound. Node(s); two. insert. After(three); //root. play. From. Me. On(); Sound. Node reversed = (Sound. Node) root. reverse 2(); reversed. play. From. Me. On(); }
Stack-based Reverse /** * Reverse 2: Push all the elements on * the stack, then pop all the elements * off the stack. **/ public LLNode reverse 2() { LLNode reversed, current, popped; Stack stack = new Stack(); current=this; while (current != null) { stack. push(current); current = current. get. Next(); } reversed = (LLNode) stack. pop(); current = reversed; while (stack. size()>0) { popped=(LLNode) stack. pop(); current. insert. After(popped); current = popped; } return reversed; }
What’s the diff? Time l How often is each node touched in reverse 2()? • Twice: Once going onto the stack, once • l coming off. O(2*n) => O(n) The stack-based reverse is faster than the original reverse.
What’s the diff? Space l l l How much space does reverse 2() take? • Whatever space the stack takes. How much additional space does reverse() take? None Very common tradeoff: Space for time. • • You can make an algorithm go faster, by using more space. If you need to fit into less memory, you have to do more processing, which takes more time.
- Slides: 21