Presentation is loading. Please wait.

Presentation is loading. Please wait.

Lecture 02 Stacks, Queues, and Recursion

Similar presentations


Presentation on theme: "Lecture 02 Stacks, Queues, and Recursion"— Presentation transcript:

1 Lecture 02 Stacks, Queues, and Recursion
Topics Stacks Queues Linked Lists Recursion Recursive Data Structures

2 Recursive Data Structures
Stacks An abstract data type (ADT) is an abstraction of a data structure An ADT specifies: Data stored Operations on the data Error conditions associated with operations Example: ADT modeling a simple stock trading system The data stored are buy/sell orders The operations supported are order buy(stock, shares, price) order sell(stock, shares, price) void cancel(order) Error conditions: Buy/sell a nonexistent stock Cancel a nonexistent order Recursive Data Structures

3 Recursive Data Structures
Stacks The Stack ADT stores arbitrary objects Insertions and deletions follow the last-in first-out scheme Think of a spring-loaded plate dispenser Main stack operations: push(object): inserts an element object pop(): removes and returns the last inserted element Auxiliary stack operations: object top(): returns the last inserted element without removing it integer size(): returns the number of elements stored boolean isEmpty(): indicates whether no elements are stored Recursive Data Structures

4 Recursive Data Structures
Stacks Exceptions handling Attempting the execution of an operation of ADT may sometimes cause an error condition, called an exception Exceptions are said to be “thrown” by an operation that cannot be executed In the Stack ADT, operations pop and top cannot be performed if the stack is empty Attempting the execution of pop or top on an empty stack throws an EmptyStackException Recursive Data Structures

5 Recursive Data Structures
Stacks Direct applications Webpage-visited history in a Web browser Undo sequence in a text editor Chain of method calls in the Java Virtual Machine Indirect applications Auxiliary data structure for algorithms Component of other data structures Recursive Data Structures

6 Recursive Data Structures
Stacks Algorithm size() return t + 1 Algorithm pop() if isEmpty() then throw EmptyStackException else t  t  1 return S[t + 1] A simple way of implementing the Stack ADT uses an array We add elements from left to right A variable keeps track of the index of the top element S 1 2 t Recursive Data Structures

7 Recursive Data Structures
Stacks The array storing the stack elements may become full A push operation will then throw a FullStackException Algorithm push(o) if t = S.length  1 then throw FullStackException else t  t + 1 S[t]  o S 1 2 t Recursive Data Structures

8 Recursive Data Structures
Stacks Performance Let n be the number of elements in the stack The space used is O(n) Each operation runs in time O(1) Limitations The maximum size of the stack must be defined a priori and cannot be changed Trying to push a new element into a full stack causes an implementation-specific exception Recursive Data Structures

9 Recursive Data Structures
Stacks Example application 1: The Java Virtual Machine (JVM) keeps track of the chain of active methods with a stack When a method is called, the JVM pushes on the stack a frame containing Local variables and return value Program counter, keeping track of the statement being executed When a method ends, its frame is popped from the stack and control is passed to the method on top of the stack Allows for recursion main() { int i = 5; foo(i); } foo(int j) { int k; k = j+1; bar(k); } bar(int m) { … } bar PC = 1 m = 6 foo PC = 3 j = 5 k = 6 main PC = 2 i = 5 Recursive Data Structures

10 Recursive Data Structures
Stacks public interface Stack { public int size(); public boolean isEmpty(); public Object top() throws EmptyStackException; public void push(Object o); public Object pop() throws EmptyStackException; } Java interface corresponding to our Stack ADT Requires the definition of class EmptyStackException Different from the built-in Java class java.util.Stack Recursive Data Structures

11 Recursive Data Structures
Stacks public class ArrayStack implements Stack { // holds the stack elements private Object S[ ]; // index to top element private int top = -1; // constructor public ArrayStack(int capacity) { S = new Object[capacity]); } public Object pop() throws EmptyStackException { if isEmpty() throw new EmptyStackException (“Empty stack: cannot pop”); Object temp = S[top]; // facilitates garbage collection S[top] = null; top = top – 1; return temp; } Recursive Data Structures

12 Recursive Data Structures
Stacks Example application 2: HTML Tag Matching For fully-correct HTML, each <name> should pair with a matching </name> <body> <center> <h1> The Little Boat </h1> </center> <p> The storm tossed the little boat like a cheap sneaker in an old washing machine. The three drunken fishermen were used to such treatment, of course, but not the tree salesman, who even as a stowaway now felt that he had overpaid for the voyage. </p> <ol> <li> Will the salesman die? </li> <li> What color is the boat? </li> <li> And what about Naomi? </li> </ol> </body> The Little Boat The storm tossed the little boat like a cheap sneaker in an old washing machine. The three drunken fishermen were used to such treatment, of course, but not the tree salesman, who even as a stowaway now felt that he had overpaid for the voyage. 1. Will the salesman die? 2. What color is the boat? 3. And what about Naomi? Recursive Data Structures

13 Recursive Data Structures
Stacks Tag Matching import java.util.StringTokenizer; import datastructures.Stack; import datastructures.NodeStack; import java.io.*; /** Simpli.ed test of matching tags in an HTML document. */ public class HTML { /** Nested class to store simple HTML tags */ public static class Tag { String name; // The name of this tag boolean opening; // Is true i. this is an opening tag public Tag() { // Default constructor name = ""; opening = false; } public Tag(String nm, boolean op) { // Preferred constructor name = nm; opening = op; /** Is this an opening tag? */ public boolean isOpening() { return opening; } /** Return the name of this tag */ public String getName() {return name; } /** Test if every opening tag has a matching closing tag. */ public boolean isHTMLMatched(Tag[ ] tag) { Stack S = new NodeStack(); // Stack for matching tags for (int i=0; (i<tag.length) && (tag[i] != null); i++) { if (tag[i].isOpening()) S.push(tag[i].getName()); // opening tag; push its name on the stack else { if (S.isEmpty()) // nothing to match return false; if (!((String) S.pop()).equals(tag[i].getName())) // wrong match if (S.isEmpty()) return true; // we matched everything return false; // we have some tags that never were matched Recursive Data Structures

14 Recursive Data Structures
Stacks public final static int CAPACITY = 1000; // Tag array size upper bound /* Parse an HTML document into an array of html tags */ public Tag[ ] parseHTML(BufferedReader r) throws IOException { String line; // a line of text boolean inTag = false ; // true iff we are in a tag Tag[ ] tag = new Tag[CAPACITY]; // our tag array (initially all null) int count = 0 ; // tag counter while ((line = r.readLine()) != null) { // Create a string tokenizer for HTML tags (use < and > as delimiters) StringTokenizer st = new StringTokenizer(line,"<> \t",true); while (st.hasMoreTokens()) { String token = (String) st.nextToken(); if (token.equals("<")) // opening a new HTML tag inTag = true; else if (token.equals(">")) // ending an HTML tag inTag = false; else if (inTag) { // we have a opening or closing HTML tag if ( (token.length() == 0) | | (token.charAt(0) != ’/’) ) tag[count++] = new Tag(token, true); // opening tag else // ending tag tag[count++] = new Tag(token.substring(1), false); // skip the } // Note: we ignore anything not in an HTML tag } return tag; // our array of tags /** Tester method */ public static void main(String[ ] args) throws IOException { BufferedReader stdr; // Standard Input Reader stdr = new BufferedReader(new InputStreamReader(System.in)); HTML tagChecker = new HTML(); if (tagChecker.isHTMLMatched(tagChecker.parseHTML(stdr))) System.out.println("The input file is a matched HTML document."); else System.out.println("The input file is not a matched HTML document."); Recursive Data Structures

15 Recursive Data Structures
Queues The Queue ADT stores arbitrary objects Insertions and deletions follow the first-in first-out scheme Insertions are at the rear of the queue and removals are at the front of the queue Main queue operations: enqueue(object): inserts an element at the end of the queue object dequeue(): removes and returns the element at the front of the queue Auxiliary queue operations: object front(): returns the element at the front without removing it integer size(): returns the number of elements stored boolean isEmpty(): indicates whether no elements are stored Exceptions Attempting the execution of dequeue or front on an empty queue throws an EmptyQueueException Recursive Data Structures

16 Recursive Data Structures
Queues Operation Output Queue enqueue(5) – (5) enqueue(3) – (5, 3) dequeue() 5 (3) enqueue(7) – (3, 7) dequeue() 3 (7) front() 7 (7) dequeue() 7 () dequeue() “error” () isEmpty() true () enqueue(9) – (9) enqueue(7) – (9, 7) size() 2 (9, 7) enqueue(3) – (9, 7, 3) enqueue(5) – (9, 7, 3, 5) dequeue() 9 (7, 3, 5) Recursive Data Structures

17 Recursive Data Structures
Queues Direct applications Waiting lists, bureaucracy Access to shared resources (e.g., printer) Multiprogramming (process scheduling) Indirect applications Auxiliary data structure for algorithms Component of other data structures Recursive Data Structures

18 Queues Implementing queues using an array of size N in a circular fashion Two variables keep track of the front and rear f index of the front element r index immediately past the rear element normal configuration Q 1 2 r f wrapped-around configuration Q 1 2 f r Recursive Data Structures

19 Recursive Data Structures
Queues We use the modulo operator (remainder of division) Algorithm size() return (N - f + r) mod N Algorithm isEmpty() return (f = r) Q 1 2 r f Q 1 2 f r Recursive Data Structures

20 Recursive Data Structures
Queues Algorithm enqueue(o) if size() = N  1 then throw FullQueueException else Q[r]  o r  (r + 1) mod N Operation enqueue throws an exception if the array is full This exception is implementation-dependent Q 1 2 r f Q 1 2 f r Recursive Data Structures

21 Recursive Data Structures
Queues Algorithm dequeue() if isEmpty() then throw EmptyQueueException else o  Q[f] f  (f + 1) mod N return o Operation dequeue throws an exception if the queue is empty This exception is specified in the queue ADT Q 1 2 r f Q 1 2 f r Recursive Data Structures

22 Recursive Data Structures
Queues Example application 1: Implement a round robin scheduler using a queue, Q, by repeatedly performing the following steps: e = Q.dequeue() Service element e Q.enqueue(e) The Queue Shared Service 1 . Deque the next element 3 Enqueue the serviced element 2 Service the Recursive Data Structures

23 Recursive Data Structures
Queues public interface Queue { public int size(); public boolean isEmpty(); public Object front() throws EmptyQueueException; public void enqueue(Object o); public Object dequeue() throws EmptyQueueException; } Java interface corresponding to our Queue ADT Requires the definition of class EmptyQueueException No corresponding built-in Java class Recursive Data Structures

24 Recursive Data Structures
Linked Lists A singly linked list is a concrete data structure consisting of a sequence of nodes Each node stores element link to the next node next element node tail head A B C D Recursive Data Structures

25 Recursive Data Structures
Linked Lists Inserting at the Head Allocate a new node Insert new element Have new node point to old head Update head to point to new node Algorithm Insert_head(object) Create a new node o for object o.next  head head o Recursive Data Structures

26 Recursive Data Structures
Linked Lists Removing at the Head Update head to point to next node in the list Allow garbage collector to reclaim the former first node Algorithm Remove_head() head head.next Recursive Data Structures

27 Recursive Data Structures
Linked Lists Inserting at the Tail Allocate a new node Insert new element Have old last node point to new node Have new node point to null Algorithm Insert_tail(object) Create a new node o for object p  head while p.next ≠ NULL do p  p.next p.next  o o.next NULL Recursive Data Structures

28 Recursive Data Structures
Linked Lists Removing at the tail of a singly linked list is not efficient! There is no constant-time way to update the tail to point to the previous node Algorithm Remove_tail(o) p  head q p.next while q.next ≠ NULL do p  q q  q.next p.next  NULL Recursive Data Structures

29 Recursive Data Structures
Linked Lists We can implement a stack with a singly linked list The top element is stored at the first node of the list The space used is O(n) and each operation of the Stack ADT takes O(1) time nodes t elements Recursive Data Structures

30 Recursive Data Structures
Linked Lists We can implement a queue with a singly linked list The front element is stored at the first node The rear element is stored at the last node The space used is O(n) and each operation of the Queue ADT takes O(1) time r nodes f elements Recursive Data Structures

31 Recursive Data Structures
Linked Lists public class Node { // Instance variables: private Object element; private Node next; /** Creates a node with null references to its element and next node. */ public Node() { this(null, null); } /** Creates a node with the given element and next node. */ public Node(Object e, Node n) { element = e; next = n; // Accessor methods: public Object getElement() { return element; public Node getNext() { return next; // Modifier methods: public void setElement(Object newElem) { element = newElem; public void setNext(Node newNext) { next = newNext; Recursive Data Structures

32 Recursive Data Structures
Recursion Recursion: when a method calls itself Classic example--the factorial function: n! = 1· 2· 3· ··· · (n-1)· n Recursive definition: Algorithm Factorial (n) Input: Integer n Output: n! if ( n = 0) then return 1 // base case else return n * Factorial (n -1 ) // recursive case Recursive Data Structures

33 Recursive Data Structures
Recursion Two important factors Base case(s). Values of the input variables for which we perform no recursive calls are called base cases (there should be at least one base case). Every possible chain of recursive calls must eventually reach a base case. Recursive calls. Calls to the current method. Each recursive call should be defined so that it makes progress towards a base case. Recursive Data Structures

34 Recursive Data Structures
Recursion Visualizing recursion trace A box for each recursive call An arrow from each caller to callee An arrow from each callee to caller showing return value Example recursion trace: recursiveFactorial ( 4 ) 3 2 1 return call * = 6 24 final answer Recursive Data Structures

35 Recursive Data Structures
Recursion Linear recursion Single recursive calls Binary recursion Two recursive calls Multiple recursion Many recursive calls Recursive Data Structures

36 Recursive Data Structures
Linear Recursion Test for base cases. Begin by testing for a set of base cases (there should be at least one). Every possible chain of recursive calls must eventually reach a base case, and the handling of each base case should not use recursion. Recur once. Perform a single recursive call. (This recursive step may involve a test that decides which of several possible recursive calls to make, but it should ultimately choose to make just one of these calls each time we perform this step.) Define each possible recursive call so that it makes progress towards a base case. Recursive Data Structures

37 Recursive Data Structures
Linear Recursion Calculating Sum: Example Linear Recursion 1 Algorithm LinearSum(A, n): Input: A integer array A and an integer n = 1, such that A has at least n elements Output: The sum of the first n integers in A if n = 1 then return A[0] else return LinearSum(A, n - 1) + A[n - 1] LinearSum ( A , 5 ) 1 2 3 4 call return [ ] = + = 7 6 13 15 20 Recursive Data Structures

38 Recursive Data Structures
Linear Recursion Reversing an array: Example Linear Recursion 2 Algorithm ReverseArray(A, i, j): Input: An array A and nonnegative integer indices i and j Output: The reversal of the elements in A starting at index i and ending at j if i < j then Swap A[i] and A[ j] ReverseArray(A, i + 1, j - 1) return Recursive Data Structures

39 Recursive Data Structures
Linear Recursion Defining Arguments for Recursion In creating recursive methods, it is important to define the methods in ways that facilitate recursion. This sometimes requires we define additional parameters that are passed to the method. For example, we defined the array reversal method as ReverseArray(A, i, j), not ReverseArray(A). Recursive Data Structures

40 Recursive Data Structures
Linear Recursion Computing Powers: Example Linear Recursion 3 The power function, p(x,n)=xn, can be defined recursively: This leads to an power function that runs in O(n) time (for we make n recursive calls). Algorithm Power(x, n) Input: Integer n Output: n! if ( n = 0) then return 1 // base case else return x * Power(x, n -1 ) // recursive case Recursive Data Structures

41 Recursive Data Structures
Linear Recursion We can derive a more efficient linearly recursive algorithm by using repeated squaring: For example, 24 = 2(4/2)2 = (24/2)2 = (22)2 = 42 = 16 25 = 21+(4/2)2 = 2(24/2)2 = 2(22)2 = 2(42) = 32 26 = 2(6/ 2)2 = (26/2)2 = (23)2 = 82 = 64 27 = 21+(6/2)2 = 2(26/2)2 = 2(23)2 = 2(82) = 128. Recursive Data Structures

42 Recursive Data Structures
Linear Recursion Algorithm Power(x, n): Input: A number x and integer n = 0 Output: The value xn if n = 0 then return 1 if n is odd then y = Power(x, (n - 1)/ 2) return x * y * y else y = Power(x, n/ 2) return y * y Recursive Data Structures

43 Recursive Data Structures
Linear Recursion Algorithm Power(x, n): Input: A number x and integer n = 0 Output: The value xn if n = 0 then return 1 if n is odd then y = Power(x, (n - 1)/ 2) return x *y * y else y = Power(x, n/ 2) return y * y Each time we make a recursive call we halve the value of n; hence, we make log n recursive calls. That is, this method runs in O(log n) time. It is important that we used a variable twice here rather than calling the method twice. Recursive Data Structures

44 Recursive Data Structures
Linear Recursion Tail recursion occurs when a linearly recursive method makes its recursive call as its last step. The array reversal method is an example. Such methods can be easily converted to non-recursive methods (which saves on some resources). Example: Algorithm IterativeReverseArray(A, i, j ): Input: An array A and nonnegative integer indices i and j Output: The reversal of the elements in A starting at index i and ending at j while i < j do Swap A[i ] and A[ j ] i = i + 1 j = j - 1 return Recursive Data Structures

45 Recursive Data Structures
Binary Recursion Binary recursion occurs whenever there are two recursive calls for each non-base case. Example 1: The DrawTicks method for drawing ticks on an English ruler. Recursive Data Structures

46 Recursive Data Structures
Binary Recursion // draw a tick with no label public static void drawOneTick(int tickLength) { drawOneTick(tickLength, - 1); } // draw one tick public static void drawOneTick(int tickLength, int tickLabel) { for (int i = 0; i < tickLength; i++) System.out.print("-"); if (tickLabel >= 0) System.out.print(" " + tickLabel); System.out.print("\n"); } public static void drawTicks(int tickLength) { // draw ticks of given length if (tickLength > 0) { // stop when length drops to 0 drawTicks(tickLength- 1); // recursively draw left ticks drawOneTick(tickLength); // draw center tick drawTicks(tickLength- 1); // recursively draw right ticks public static void drawRuler(int nInches, int majorLength) { // draw ruler drawOneTick(majorLength, 0); // draw tick 0 and its label for (int i = 1; i <= nInches; i++) { drawTicks(majorLength- 1); // draw ticks for this inch drawOneTick(majorLength, i); // draw tick i and its label Note the two recursive calls Recursive Data Structures

47 Recursive Data Structures
Binary Recursion Example 2: add all the numbers in an integer array A: Algorithm BinarySum(A, i, n): Input: An array A and integers i and n Output: The sum of the n integers in A starting at index i if n = 1 then return A[i ] return BinarySum(A, i, n/ 2) + BinarySum(A, i + n/ 2, n/ 2) Example trace: 3 , 1 2 4 8 7 6 5 Recursive Data Structures

48 Recursive Data Structures
Binary Recursion Example 3: Computing Fibanacci Numbers Fibonacci numbers are defined recursively: F0 = 0 F1 = 1 Fi = Fi-1 + Fi for i > 1. As a recursive algorithm (first attempt): Algorithm BinaryFib(k): Input: Nonnegative integer k Output: The kth Fibonacci number Fk if k = 1 then return k else return BinaryFib(k - 1) + BinaryFib(k - 2) Recursive Data Structures

49 Recursive Data Structures
Binary Recursion Analyzing the Binary Recursion Fibonacci Algorithm Let nk denote number of recursive calls made by BinaryFib(k). Then n0 = 1 n1 = 1 n2 = n1 + n0 + 1 = = 3 n3 = n2 + n1 + 1 = = 5 n4 = n3 + n2 + 1 = = 9 n5 = n4 + n3 + 1 = = 15 n6 = n5 + n4 + 1 = = 25 n7 = n6 + n5 + 1 = = 41 n8 = n7 + n6 + 1 = = 67. Note that the value at least doubles for every other value of nk. That is, nk > 2k/2. It is exponential! Recursive Data Structures

50 Recursive Data Structures
Binary Recursion A Better Fibonacci Algorithm Use linear recursion instead: Algorithm LinearFibonacci(k): Input: A nonnegative integer k Output: Pair of Fibonacci numbers (Fk, Fk-1) if k = 1 then return (k, 0) else (Fk_1, Fk_2) = LinearFibonacci(k - 1) return (Fk_1+Fk_2, Fk_1) Runs in O(k) time. Recursive Data Structures

51 Recursive Data Structures
Multiple Recursion Motivating example: summation puzzles pot + pan = bib dog + cat = pig boy + girl = baby Multiple recursion: makes potentially many recursive calls (not just one or two). Recursive Data Structures

52 Recursive Data Structures
Multiple Recursion Algorithm PuzzleSolve(k,S,U): Input: An integer k, sequence S, and set U (the universe of elements to test) Output: An enumeration of all k-length extensions to S using elements in U without repetitions for all e in U do Remove e from U {e is now being used} Add e to the end of S if k = 1 then Test whether S is a configuration that solves the puzzle if S solves the puzzle then return “Solution found: ” S else PuzzleSolve(k - 1, S,U) Add e back to U {e is now unused} Remove e from the end of S Recursive Data Structures

53 Recursive Data Structures
Multiple Recursion Visualizing PuzzleSolve PuzzleSolve ( 3 , () ,{ a b c } ) Initial call 2 1 ab ac cb ca bc ba abc acb bac bca cab cba Recursive Data Structures


Download ppt "Lecture 02 Stacks, Queues, and Recursion"

Similar presentations


Ads by Google