Maze exit finder (cont.) Choosing maze data structures � How to represent a maze square? � Solution must lead to smaller problems boolean find_exit(int x, int y) /* 2 nd try */ – Okay, a class, but what data are stored? { if ( we have been here before ) � Ways to know if exit or not, if has been visited yet or not return false; /* don’t try same spot again */ � Maybe ways to know about neighboring squares if ( x,y is an exit ) – How about some helper methods? return true; /* success! */ � e.g., isExit(), isMarked(), hasNeighbor(direction), … /* rest as before */ � How to represent the whole maze? � So need a way to remember where we’ve been – Suggest: array of references to maze squares – e.g., mark square upon entering find_exit – Any other ways? – Q: is it ever necessary to remove the mark? Towers of Hanoi (demo) Decimal (value) to binary (string) /** � Problem: move n disks from peg a to peg c, using * Returns a String representation of the binary equivalent peg b to hold disks temporarily; keep small on top * of a specified integer. The worstTime(n) is O(log n). * @param n – an int in decimal notation. � Recursive solution: method with params n , a , b , c * @return the binary equivalent of n, as a String – Base case: just one disk – trivial: * @throws IllegalArgumentException, if n is less than 0 */ // (From Collins text’s instructor resources) � If n is 1, move 1 disk from a to c public static String getBinary (int n) { – General case: assume a method that can move a tower if (n < 0) throw new IllegalArgumentException( ); of height n-1. This method!!! � Move top n-1 disks from a to b , using c for holding purposes if (n <= 1) return Integer.toString (n); � Move the bottom disk from a to c � Move all n-1 disks on b to c , using a for holding purposes return getBinary (n / 2) + Integer.toString (n % 2); } � Iterative solution much more difficult in this case Demo Eliminating recursion Queues Rear Front � Can always simulate recursion by explicit stack – Use iteration instead of recursion last enqueued 1st enqueued � Instead of recursive call: push key values onto stack last dequeued 1st dequeued – e.g., maze finder – push coordinates (x, y) � Instead of return: pop values from stack – e.g., back to square (x, y) in maze finder � FIFO data structure – First In, First Out � Sometimes an easy non-recursive translation � Typical operations similar to stacks without a stack – especially if “tail recursion” – enqueue (an item at rear of queue) – e.g, factorial, fibonacci, ruler tick marks, … – dequeue (item at front of queue) – Much harder for maze and Hanoi examples – peek (at front item) – isEmpty , isFull , size , clear 1
Some queue applications Applying a queue – palindrome � Many operating system applications � Palindrome - same forward and backward – Time-sharing systems rely on process queues – e.g., Abba, and “Able was I ere I saw Elba.” � Often separate queues for high priority jobs that take little � Lots of ways to solve, including recursive time, and low priority jobs that require more time � Can use a queue and a stack together – Printer queues and spoolers � Printer has its own queue with bounded capacity – Fill a queue and a stack with copies of letters (only) � Spoolers queue up print jobs on disk, waiting for print queue – Then empty both together, verifying equality – Buffers – coordinate processes with varying speeds � Reminder – we’re using an abstraction � Simulation experiments – We still don’t know how queues are implemented!!! – Models of queues at traffic signals, in banks, etc., To use them, it does not matter! used to “see what happens” under various conditions – Abstraction is Good! Queue interface Implementing queues � Easy to do with a list (e.g., ArrayList): � e.g., java.util.Queue: public interface Queue<E> extends Collection<E> { – Mostly same as stack implementation boolean offer(E o); // enqueue – Enqueue – add to end – list.add(item); E poll(); // dequeue (null if empty) – Then to dequeue and peek: refer to first item E remove(); // dequeue (exception if empty) � e.g., to dequeue – list.remove(0); E peek(); // peek (null if empty) � Array implementation is trickier: E element(); // peek (exception if empty) – Must keep track of front and rear indices } – Increment front/rear using modulus arithmetic � All Known Implementing Classes: � Indices cycle past last index to first again – idea is to reuse – AbstractQueue, ArrayBlockingQueue, ConcurrentLinkedQueue, the beginning of the array after dequeues DelayQueue, LinkedBlockingQueue, LinkedList, PriorityBlockingQueue, PriorityQueue, SynchronousQueue See demos in ~mikec/cs20/demo03/queue/ at CSIL Queue operation complexity What are iterators? � Implementing a queue with an array � Objects for iterating over elements of structure – enqueue(object) – add to end and increment tail index � e.g., java.util.Iterator: � O(1) if array is not full; otherwise O(n) to resize/copy interface Iterator<E> { – dequeue() – remove front and increment front index boolean hasNext(); // true if more objects � O(1) – does not depend on size of queue E next(); // return object and increment � Implementing with single-linked list // throws NoSuchElementException if !hasNext() void remove(); // optional – and potentially dangerous – enqueue(object) – add a last item // may throw UnsupportedOperationException � O(n) – for single-linked list with just a first pointer } � But O(1) if also have a pointer to last element – an easy fix � Handy to implement as inner class of structure – dequeue() – remove first item – Has reference to all data structure fields/methods � O(1) – point first at first.next – not affected by n size – Could be anonymous/local to getIterator method – Why not enqueue first and dequeue last? 2
Why iterators? Implementing linked lists � Provide ability to traverse list (or other structure) � e.g., a method to insert a new second node – imagine list now is (DUS → ORD → SAN) , without direct access to nodes want (DUS → BRU → ORD → SAN) � Easy to use – e.g., print list with while loop: or now (DUS) , want (DUS → BRU) Iterator it = list.getIterator(); while (it.hasNext()) or now () , want (BRU) print(it.next()); – Any other special cases? � Even shorter with a for loop: � A strategy: for(Iterator it=list.getIterator(); it.hasNext();) print(it.next()); // the increment step happens here create new node to hold BRU – call it n; – And simpler with enhanced for loop: if empty list – set first to n; return; else set n.next to first.next; for ( DataType d : list) print(d); set first.next to n; return; Code to insert new 2 nd node Searching for a node � Assume instance variable for first node: � Idea: return reference to the node that contains ListNode first; // refers to first node or null if list is empty particular info , or return null if the info is not in � So use that fact to write “is empty” method: boolean isEmpty() { return first == null; } the list (Note: probably a private method – returns node reference) � Then easy to code insert 2 nd node method: � Strategy: void insertNewSecondNode(Object data){ declare local node reference - call it n; ListNode n = new ListNode(); // null data and next n.data = data; point n at first node in list; if (isEmpty()) first = n; // leave next null while (n points to non-null node) { else { if (n’s referent has the info) n.next = first.next; return n; first.next = n; } else advance n to n.next; } } return null if get this far; List traversal without iterators � Search strategy typifies list-hopping activity: start by referencing first node; process that node; change reference to that node’s next link; keep going until success (e.g., found info), or until end (i.e., reference is null); – Same idea works for lots of list operations � e.g., print list – immediately applicable � To append (add last), first must link-hop to last node � To remove a node, must link-hop to node that precedes it � But also usually consider potential special cases – e.g., first node, last node, empty list, just one node, … 3
Recommend
More recommend