410 likes | 533 Views
CSCI 2720 Lists II. Eileen Kraemer University of Georgia September 13, 2005. Stack : Linked Memory. X2. Push X0 Push X1 Push X2. X1. X0. Stack: Linked Memory. function MakeEmptyStack():ptr return function IsEmptyStack(ptr L):bool return L = Function Top(ptr L):info
E N D
CSCI 2720Lists II Eileen Kraemer University of Georgia September 13, 2005
Stack : Linked Memory X2 • Push X0 • Push X1 • Push 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 .jedi .jedi .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! • 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) • The first call: Solve(3, 1, 2, 3)
The Towers of Hanoi • Move from Src to Dst • Move from Src to Aux • Move from Dst to Aux • Move from Src to Dst • Move from Aux to Src • Move from Aux to Dst • Move 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
StartTraversal(L): Forward(P,Q): Back(P,Q): Link Inversion Traversal
Doubly Linked Lists • Coming soon ….