210 likes | 308 Views
Stacks as an Abstract Data Type. CS1316: Representing Structure and Behavior. Story. Stack: Another useful kind of list An example of an Abstract Data Type (ADT): We can define the methods and the behavior apart from any implementation.
E N D
Stacks as an Abstract Data Type CS1316: Representing Structure and Behavior
Story • Stack: Another useful kind of list • An example of an Abstract Data Type (ADT): • We can define the methods and the behavior 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 Queue • 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! I got here second! I got here first! This is the top of the stack
New items go at the top I got here fourth! I got here third! I got here second! I got here first! This is the newtop of the stack
Items only get removed from the top I got here fourth! And now I’m outta here! I got here third! I got here second! I got here first! This is the new (er, old)top of the stack
What can we do with stacks? • push(anObject): Tack a new object onto the top of the queue • pop(): Pull the top (head) object off the queue. • peek(): Get the top of the queue, but don’t remove it from the queue. • size(): Return the size of the queue
Examplestack Welcome to DrJava. > Stack stack = new Stack() > stack.push("This") > stack.push("is") > stack.push("a") > stack.push("test") > stack.size() 4 > stack.peek() "test" > stack.pop() "test" > stack.pop() "a" > stack.pop() "is" > stack.pop() "This" > stack.pop() java.util.NoSuchElementException: Notice anything interesting about the order in and the order out?
Implementing a stack with a linked list import java.util.LinkedList; // Need for LinkedList public class Stack { /** Where we store the elements */ private LinkedList elements; /// Constructor //// public Stack() { elements = new LinkedList(); }
Stackmethods //// Methods /// public void push(Object element){ // New elements go at the front elements.addFirst(element); } public Object peek(){ return elements.getFirst(); } public Object pop(){ Object toReturn = this.peek(); elements.removeFirst(); return toReturn; } public int size(){return elements.size();}
A stack is a stack, no matter what lies beneath. • Our description of the stack minus the implementation is an example of an abstract data type (ADT). • 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 are 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 Stack2 { 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;
Stack2 = array + top index /// Constructor //// public Stack2() { elements = new Object[ARRAYSIZE]; top = 0; }
Stack2Methods //// Methods /// public void push(Object element){ // New elements go at the top elements[top]=element; // then add to the top 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 toReturn = this.peek(); top--; return toReturn; } /** Size is simply the top index */ public int size(){return top;}
TestingStack2 Welcome to DrJava. > Stack2 stack = new Stack2(); > stack.push("Matt") > stack.push("Katie") > stack.push("Jenny") > stack.size() 3 > stack.peek() "Jenny" > stack.pop() "Jenny" > stack.pop() "Katie" > stack.pop() "Matt" > stack.pop() Stack empty! null
What are stacks good for? • 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 • Recall our original implementation of reverse(). • 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().insertAfter()). • All told: For each node, we walk the whole list three times. • O(n*n2)=O(n3)
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.getNext() != 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 SoundListTest() public void reverseTest(){ FileChooser.setMediaPath("D:/cs1316/MediaSources/"); Sound s = null; // For copying in sounds s = new Sound(FileChooser.getMediaPath("guzdial.wav")); SoundNode root = new SoundNode(s); s = new Sound(FileChooser.getMediaPath("is.wav")); SoundNode one = new SoundNode(s); root.last().insertAfter(one); s = new Sound(FileChooser.getMediaPath("scritch-q.wav")); SoundNode two = new SoundNode(s); root.last().insertAfter(two); s = new Sound(FileChooser.getMediaPath("clap-q.wav")); SoundNode three = new SoundNode(s); two.insertAfter(three); //root.playFromMeOn(); SoundNode reversed = (SoundNode) root.reverse2(); reversed.playFromMeOn(); }
Stack-basedReverse /** * Reverse2: Push all the elements on * the stack, then pop all the elements * off the stack. **/ public LLNode reverse2() { LLNode reversed, current, popped; Stack stack = new Stack(); current=this; while (current != null) { stack.push(current); current = current.getNext(); } reversed = (LLNode) stack.pop(); current = reversed; while (stack.size()>0) { popped=(LLNode) stack.pop(); current.insertAfter(popped); current = popped; } return reversed; }
What’s the diff? Time • How often is each node touched in reverse2()? • Twice: Once going onto the stack, once coming off. • O(2*n) => O(n) • The stack-based reverse is faster than the original reverse.
What’s the diff? Space • How much space does reverse2() 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.