CSCI 2720 Lists II - University of Georgia

advertisement
CSCI 2720
Lists II
Eileen Kraemer
University of Georgia
September 13, 2005
Stack : Linked Memory
• Push X0
• Push X1
• Push X2
X2
X1
X0
Stack: Linked Memory
• function MakeEmptyStack():ptr
return 
• function IsEmptyStack(ptr L):bool
return L = 
• Function Top(ptr L):info
if IsEmptyStack(L) then error
else return Info(L)
Stack: Linked Memory
• Function Pop(locative L):info
if IsEmptyStack(L) then error
else
X <- Top(L)
L <= Next(L)
return x
Stack:Linked Memory
• Procedure Push(info x, locative L):
P <- NewCell(Node)
Info(P) <- x
Next(P) <- L
L <= P
What is a locative?
• A “new data type that makes the
coding of algorithms smoother”
• Elegant to write
• Difficult to implement
What is a locative??
• Like a pointer variable (most of the
time)
• Key(P), Next(P) ..
• In assignments, keeps track of value
assigned, and the place in memory that
it came from …
• P <= Q … updates value of P, and what
used to point to P now points to Q …
• In practice, we do this by using trailing
pointers
Queue: Linked Memory
• Function MakeEmptyQueue():ptr
L <- NewCell(Queue)
Front(L) <- Back(L) <- 
return L
• Function IsEmptyQueue(ptr L):boolean
return Front(L) = 
Queue: Linked Memory
• Function Enqueue(info x, ptr L):
P <- NewCell(Node)
Info(P) <- x
Next(P) <- 
If IsEmptyQueue(L) then Front(L) <- P
else Next(Back(L)) <- P
Back(L) <- P
Queue: Linked Memory
• Function Dequeue(ptr L):info
If IsEmptyQueue(L) then error
else
X <- Info(Front(L))
Front(L) <- Next(Front(L))
If Front(L) = then
Back (L) <= 
return x
Queue: Linked Memory
• Function Front(ptr L): info
if IsEmptyQueue(L) then error
else return Info(Front(L))
Stacks and Recursion
• Recursion
• Pro: expressive power
• Cost: overhead = time + memory
• Stacks used in implementing recursion
• Works because subprogram invocations
end in the opposite order from their
beginning (LIFO property)
Classical Example of Recursive Algorithm:
The Towers of Hanoi
• The Legend. In an ancient city in India, so the
legend goes, monks in a temple have to move a
pile of 64 sacred disks from one location to
another. The disks are fragile; only one can be
carried at a time. A disk may not be placed on
top of a smaller, less valuable disk. And, there
is only one other location in the temple
(besides the original and destination locations)
sacred enough that a pile of disks can be placed
there.
.jedi
Source: http://www.math.toronto.edu/mathnet/games/towers.html
The Towers of Hanoi
Source: http://www.mathematik.uni-muenchen.de/~hinz/tower.jpg
The Towers of Hanoi
• How should the monks proceed?
• Will they make it?
The Towers of Hanoi
•
Recursive solution!
1.
2.
For N = 0 do nothing
Move the top N-1 disks from Src to
Aux (using Dst as an intermediary
peg)
Move the bottom disks from Src to
Dst
Move N-1 disks from Aux to Dst
(using Src as an intermediary peg)
3.
4.
•
The first call:
Solve(3, 1, 2, 3)
The Towers of Hanoi
1.
2.
3.
4.
5.
6.
7.
Move
Move
Move
Move
Move
Move
Move
from Src to Dst
from Src to Aux
from Dst to Aux
from Src to Dst
from Aux to Src
from Aux to Dst
from Src to Dst
Towers of Hanoi applet
• http://www.mazeworks.com/hanoi
/
The Towers of Hanoi
• How much time will monks spend? ¹
• For one disk, only one move is
necessary
• For two disks, we need three moves
• For n disks???
The Towers of Hanoi
• Let Tn denote the number of moves
needed to move n disks.
• T1 = 1
• T2 = 3
• Tn = 2*Tn-1 + 1
The Towers of Hanoi
• Let Tn denote the number of moves
needed to move n disks.
• T1 = 1
• T2 = 3
• Tn = 2*Tn-1 + 1
• Tn = 2n - 1
• How to prove it?
Induction!
• Base case:
T1 = 1 = 21 - 1. OK!
• Inductive hypothesis:
Tn = 2n -1
• Inductive step: we show that:
Tn+1=2n+1-1.
Tn+1=2*Tn+1=2*(2n-1)+1=
=2n+1-2 + 1= 2n+1-1.
QED!
Towers of Hanoi
• Suppose it takes one minute for a monk
to move a disk. The whole task hence
would take 264-1 minutes
= (210)6*24-1 minutes
≈ (103)6*15 minutes
≈ 25*1016 hours
≈ 1016 days
= 1000000000000000 days
≈ the age of universe
Recursion
• Sierpiński triangle
• Wacław Sierpiński – Polish
mathematician 1882-1969
Sierpiński Triangle
• Draw a black triangle
• Draw a white triangle in the middle of
the triangle.
• Call the procedure for three left black
triangles
Tail recursion
• A special form of recursion in which the
last operation of a function is a recursive
call.
• The recursion may be optimized away by
executing the call in the current stack
frame and returning its result rather than
creating a new stack frame.
Recursive form
int max_list(list l, int max_so_far) {
if (null == l)
return max_so_far;
if (max_so_far < head(l))
return max_list(tail(l), head(l));
else
return max_list(tail(l), max_so_far);
}
Note ….
• The return value of the current
invocation is just the return value of
the recursive call.
• A compiler could optimize it so it
doesn't allocate new space for l and
max_so_far on each invocation or
tear down the stack on the returns.
Iterative form
int max_list(list l, int max_so_far) {
for (;;) {
if (null == l) return max_so_far;
if (max_so_far < head(l)) {
max_so_far = head(l);
l = tail(l); }
else {
max_so_far = max_so_far;
l = tail(l);
}
}}
Tail recursion
• Now no need to allocate new memory for
the parameters or get rid of it during the
returns, so this will run faster.
• This example simple enough to do by
hand ---much harder for complex
recursive data structures, such as trees.
• If compiler is good enough to find and
rewrite tail recursion, it will also
• collapse the loop test
• eliminate the assignment of max_so_far to
itself,
• hoist the assignment of l after the test
Final form …
int max_list(list l, int max_so_far) {
while (null != l){
if (max_so_far < head(l)) {
max_so_far = head(l);
}
l = tail(l);
}
return max_so_far; }
SLL -- Singly Linked Lists
• Pro:
• Insertion: (1)
• Access(L,I) -- can’t do it in O(1) …
• What is the running time of access??? (more later)
• … but given an item in the list, can get to
the next item in O(1) … which can give us a
traversal (visit each item once) in O(n)
Simple List traversal
Procedure Traverse(ptr P):
// visit nodes of SLL, starting at P
While P != {
Visit(Key(P))
P <- Next(P)
}
// if list is already in lexicographic order,
// will give in-order traversal
“Trickier Traversals”
Example (zig-zag scan)
<canary, cat, chickadee, coelacanth, collie, corn, cup>
K = crabapple
• Want to find, given word w, the last
word in L that alphabetically precedes
w and ends with same letter as w
One approach …
• Keep both forward and back pointers:
canary <=>cat <=>chickadee <=> coelacanth
<=>collie <=>corn <=>cup
• Pro: (1) from any element to predecessor
• forward to cup, backward to collie
• Con: 2x the pointer memory, all the time
Another approach
• Use trailing pointers
Function FindLast(ptr L, key w): key
// find last word in list L ending w/same letter as w
// return  f there is no such word
P <- L
Q <- 
While P !=  and Key(P) < w do
if Key(P) ends with same letter as w then Q <- P
P <- Next(P)
If Q =  then return  else return Key(Q)
Back ptrs v. Trailing ptrs
• For our example, trailing ptrs saves
memory
• One extra pointer, only while
searching
• With other search conditions, can
be too complex, code too
specialized …
Stack based …
• Start at beginning of list
• Stack pointers to all cells during
forward traversal
• Pop pointers from stack to do
backward traversal
Link inversion traversal
• Idea: actually place stack into the
list itself
• “turn around” the next pointers
• Temporarily destroys linked list
Link Inversion Traversal
StartTraversal(L):
P  
   
Q L 
Forward(P,Q):

P
 Q


 

Q

Next(Q)

 


 


Next(Q)
P

 

Back(P,Q):
P
 Next(P)

 

Next(P) Q


 

Q
P

 

Doubly Linked Lists
• Coming soon ….
Download