Sei sulla pagina 1di 21

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
first!
I got here
second!
I got here
third!
This is the top
of the stack
New items go at the top
I got here
first!
I got here
second!
I got here
third!
This is the
new top of the
stack
I got here
fourth!
Items only get removed from the
top
I got here
first!
I got here
second!
I got here
third!
This is the
new (er, old)
top of the
stack
I got here
fourth! And
now Im outta
here!
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
dont remove it from the queue.
size(): Return the size of the queue
Example
stack
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();
}
Stack
methods
//// 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;
}

Stack2
Methods
//// 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;}
Testing
Stack2
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*n
2
)=O(n
3
)
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-based
Reverse
/**
* 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;
}
Whats 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.
Whats 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.

Potrebbero piacerti anche