Chapter 8 Stacks and Queues 1 This is a stack of books. 2 This is a queue. 3 Chap.8 Contents 8.1 Stacks 8.1.1 The PureStack Interface 8.1.2 Implementations of the PureStack Interface 8.1.3 Stack Application 1: How Compilers Implement Recursion 8.1.4 Stack Application 2: Converting from Infix to Postfix 8.1.5 Prefix Notation 8.2 Queues 8.2.1 The PureQueue Interface 8.2.2 Implementations of the PureQueue Interface 8.2.3 Computer Simulation 8.2.4 Queue Application: A Simulated Car Wash 4 8.1 Stacks A stack is a special list: the only element 可 remove, access, or modify 的 是最近一次 insert 的 element . 5 That is, the element most recently inserted is the next element to be removed. Last-In,First-Out (LIFO) 6 Object-Oriented (O-O) Terms • Stack, Queue 是特別的 list. 以O-O 術語, 它們 EXTEND (延伸) list (ArrayList 或 LinkedList) General concepts (通用觀念) are Super class such as ArrayList Specialized concepts (專用觀念 ) are Sub class such as Stack 7 Top – 是最近一次 insert 的 element Push – insert element 到 stack 中 Pop – 由 stack 中 remove top element 8 Start with an empty stack. Push “張三” 9 Top 張三 10 Push “李四” 11 Top 李四 張三 12 Pop. 13 Top 張三 14 8.1.1 The PureStack Interface public interface PureStack<E> { /** size * 紀錄在 PureStack 有多少 elements * * @return PureStack 中 elements 的數量 */ int size(); 15 /** isEmpty * 記錄此 PureStack object 是否內部沒有任何 element. * * @return true – 如果此 PureStack object 沒有任何 * elements; 否則, return false. */ boolean isEmpty(); /** push * inserts a 指定的 element * on the top of this PureStack object. * * The averageTime(n) is constant and * worstTime(n) is O(n). * * @param element – the element to be pushed. * */ void push (E element); 16 /** pop * removes the top element from this PureStack object. * * @return – the element removed. * @throws NoSuchElementException * – 如果此 PureStack is empty. */ E pop(); /** peek * returns the top element on this PureStack object. * * @return – the element returned. * @throws NoSuchElementException * – 如果此 PureStack object is empty. */ E peek(); } // interface PureStack 17 8.1.2 實作PureStack Interface There is an implementation in java.util 18 public class Stack extends Vector { … Vector is 實質上相同於 ArrayList. 19 The push, pop and peek methods are easily defined. For example: public E push (E item) { addElement(item); return item; } 20 But NO Vector methods are overridden(覆 蓋) 所以可呼叫 vector 的任何 method 這會干擾到 stack 的定義! For example, it is NOT allowed to remove a stack element at index 7: myStack.remove (7); 21 解決之道有三: 1. Inherit (繼承) from ArrayList 或 LinkedList Ugh! 唉阿! Too many overrides. 2. Use an array protected E[ ] data; protected int top; Then, top 將會是 array 的尾端 (不是前端 index 0)不方便. 3.Aggregate (組成) an ArrayList or LinkedList Yes, and all method definitions are one-liners. 採此法 22 public class LinkedListPureStack <E> { protected LinkedList<E> list; // aggregate a list public LinkedListPureStack // copy constructor (LinkedListPureStack<E> otherStack) { list = new LinkedList<E> (otherStack.list); } public void push (E element) { list.add (element); } // push … } // LinkedListPureStack 23 寫出下列的輸出結果: LinkedListPureStack<Integer> myStack = new LinkedListPureStack<Integer>( ); for (int i = 0; i < 10; i++) myStack.push (i * i); while (!myStack.isEmpty( )) System.out.println (myStack.pop( )); ANS: 81 (9*9), 64 (8*8), 49, 36, 25, 16, 9, 4, 1, 0 24 8.1.3 Stack 的應用1: Compiler如何實作Recursive 每當有 recursive method 被呼叫, 前一次 method 資訊會被儲存,不被覆蓋. 這資訊叫 activation record. 25 Activation record 包含: 1. 呼叫 method 的 return address 2. 每個被呼叫 method 的 parameter 所對應之 argument 3. 每個被呼叫 method 的 local variable 26 Run-time stack 處理 activation records. Push: When method is called Pop: When execution of method is completed 27 Decimal to Binary: Ex: get binary “101” from decimal “5” public static String getBinary (int n) { if (n < 0) throw new IllegalArgumentException(); if (n <= 1) return Integer.toString (n); return getBinary(n / 2) + Integer.toString(n % 2); } // method getBinary 28 The following iterative method maintains its own stack: 29 public static String getBinary (int n) { ArrayStack<Integer> myStack = new ArrayStack<Integer>(); String binary = new String(); If(n<0)throw new IllegalArgumentException( ); myStack.push (n % 2); while (n > 1) { n /= 2; myStack.push (n % 2); } while(!myStack.isEmpty()) binary+=myStack.pop(); return binary; } // end of getBinary 30 請注意,我們只儲存 n % 2 在 stack 中, 但我們無需儲存 return address. 因為此 getBinary 沒用到 recursive. 31 Exercise: Trace the execution of the above method after an initial call of: getBinary (20); show the contents of myStack. 32 8.1.4 Stack 應用 2: 轉換Infix(中置式) 到 Postfix(後置式) 在 infix 表示法中, operator(運算子) 放置於 operands(運算元) 之間. For example: a+b c – d + (e * f – g * h) / i 33 Old compilers: Infix Machine language 這會因為parentheses(小括號)而產生混亂. Newer compilers: Infix Postfix Machine language 34 在 postfix 表示法中, an operator 直接地放置 於他的operands之後. No parenthesis needed. Infix a+b Postfix ab+ a+b*c abc*+ a*b+c ab*c+ (a + b) * c ab+c* 35 postfix 不必使用 Parentheses 很棒! 36 Let’s convert an infix string below to a postfix string. x–y*z ANS: xyz*37 Postfix 保留 operands的先後順序, so an operand can be appended to postfix as soon as that operand is encountered in infix. 38 Infix Postfix x–y*z x 39 Infix Postfix x–y*z x The operands for ‘-’ 尚未在 postfix, 所以 ‘ - ’ 一定要先暫存在某地方. 40 Infix Postfix x–y*z xy 41 Infix Postfix x–y*z xy The operands for ‘*’ 未在postfix, 所以‘*’ 一定要暫存在某地方, 且儲存在‘-’之前. 42 Infix Postfix x–y*z xyz 43 Infix Postfix x–y*z xyz* – ANS. 44 As another test case, we start with x*y-z. After moving ‘x’ to postfix, ‘*’ is temporarily saved, and then ‘y’ 被加到postfix. What happens when ‘-’ is accessed? Infix x*y– z Postfix xy 45 ‘*’ 現在一定要移到 postfix 中, 因為‘*’的所有 operands 都已在 postfix 了. 接著‘-’ 要暫存起來. 在 ‘z’ 被移到 postfix 後, ‘-’ 被移到 postfix, and we are done 我們完成了! Infix x*y– z Postfix xy*z– (ANS.) 46 暫時儲存處是: stack! Here is the strategy (pseudo-code) for maintaining the stack: 47 for each operator in infix: loop until operator pushed: if operator stack is empty, Push else if infix operator has greater precedence than top operator on stack, Push else Pop and append to postfix endif endif 48 口訣: if Infix Greater, then Push 49 Convert from infix to postfix: Infix Postfix a+b*c/d-e 50 Infix Postfix a+b*c/d–e abc*d/+e – * + Operator stack 51 What about parentheses? Left parenthesis: Push, but with lowest 優先權. Right parenthesis: 持續 pop 加到 postfix 直到‘(‘ popped; 丟棄‘(‘ 及 ‘)‘ 52 Convert to postfix: x * (y + z) 53 Infix x * (y + z) Postfix xyz+* * Operator stack 54 Infix x * (y + z – (a / b + c) * d) / e Postfix Operator stack 55 為了決定轉換 infix 成 postfix 的所有動 作, 我們必須知道 1) the current token in infix 2) the top token on operator stack. 56 下列的 transition matrix 說明了轉換 infix 表示式到 postfix 表示式 57 Top token on Stack ( Infix token Operand ) ( +, - *, / Append to Postfix *, / empty Append to Postfix Append to Postfix Append to Postfix Pop; Pitch ‘(‘ Push Pop to Postfix Push Pop to Postfix Push Error Push Pop to Postfix Pop to Postfix Push Push Pop to Postfix Pop to Postfix Push Push Error empty +, - Pop to Postfix Push Done 58 Tokens 59 A token 是 program 最小有意義單元 (smallest meaningful unit). Each token has two parts: 1. A generic part, for the category(分類) of the token; 2. A specific part, to access the characters in the token. 60 For example: 1 2 ADD_OP + Operand a 61 8.1.5 Prefix(前置式) Notation prefix 表示法中, operator 放置在 operands 之前. 62 Infix a+b Prefix +ab a * (b + c) *a+bc a*b+c +*abc prefix 如同 postfix, 沒有 parentheses. 63 Whenever opt is popped from operator stack, opd1 and then opd2 are popped from operand stack. The string opt + opd2 + opd1 is pushed onto operand stack. Note: opd2 was pushed before opd1. 64 我們必須使用兩個 stacks: Operator stack: 相同於 postfix stack 的使用 Operand stack: 用於儲存 operands 65 Convert from infix to prefix: Infix a + (b * c – d) / e 66 Infix Prefix a + (b * c – d) / e +a/– *bcde +a/– *bcde /– *bcde e –*bcd d *bc c b a Operand stack * ( + Operator stack 67 Exercise: Convert to Prefix a – b + c * (d / e – (f + g)) 68 8.2 Queues A queue is a special list: 只在尾端 insert 只在前端 delete 69 Enqueue –在尾端 insert element Dequeue –在前端 delete element Front – return 前端 element (的 reference) Queue 的 first element inserted will be the first element deleted: First-In, First-Out (FIFO) 70 回想一下; STACK: (Last-In-First-Out) LIFO 71 Enqueue “張三” 72 張三 Front Back 73 Enqueue “李四” 74 張三 Front 李四 Back 75 Enqueue “王五” 76 張三 Front 李四 王五 Back 77 Dequeue 78 李四 Front 王五 Back 79 8.2.1 The PureQueue Interface public interface PureQueue<E> { //size returns elements 數量在此 PureQueue object. int size( ); // isEmpty returns true // if this PureQueue object has no elements. boolean isEmpty( ); 80 /**enqueue * inserts 一個指定的 element 在此 PureQueue object. * 的尾端 The averageTime(n) is * constant and worstTime(n) is O(n). * * @param element – the element to be appended. */ void enqueue (E element); /** dequeue * removes 在此 PureQueue * object 最前端的 element. * * @return – the element removed. * @throws NoSuchElementException – if this * PureQueue object is empty. */ E dequeue(); 81 /** front * returns the front element in this PureQueue * object. * * @return – the element returned. * * @throws NoSuchElementException – if * PureQueue object is empty. * */ E front(); } // PureQueue 82 對於 dequeue method, what is worstTime (n)? ANS: O(1) 83 8.2.2 Implementations of the PureQueue Interface 為了 code re-use 的目的, the implementation will work with an 現有的 class. ArrayList? LinkedList? 84 Inheritance (繼承法): The implementation of PureQueue is-a LinkedList OR Aggregation (組成法): The implementation of PureQueue has-a LinkedList 85 Inheritance 帶來沉重負擔: 必須 override 32 個 不適用的 super class methods. Ex: public E get (int index) { throw new UnsupportedOperationException( );} 86 Heavy Inheritance Tax 在 class reuse 實務上, 被reuse的class要有完整method description developers 清楚了解後, 再寫dummy code (如上述get) 來override 不能reuse的methods. 這是相當沉重的負擔,有如稅負,故叫 Inheritance Tax . 87 So, we’ll use aggregation: public class LinkedListPureQueue<E> implements PureQueue<E> { protected LinkedList<E> list; 88 public LinkedListPureQueue() {list = new LinkedList<E>(); } public void enqueue (E element) { {list.addLast (element); // same as list.add (element);} public E dequeue() {return list.removeFirst(); } 89 Determine the output from the following: LinkedListPureQueue<Integer> myQueue = new LinkedListPureQueue<Integer>(); for (int i = 0; i < 10; i++) myQueue.enqueue (i * i); while (!myQueue.isEmpty( )) System.out.println (myQueue.dequeue( )); ANS: 0(0*0),1(1*1),4, 9, 16, 25, 36, 49, 64, 81 90 8.2.3 Computer Simulation(模擬) SYSTEM 是由互動的部分 (interacting parts) 所組成 91 model 是 system的簡化. 建構model 是為了研究 system 92 Physical model: 不同於 system 只在於其 scale(規模) 或 intensity(強度). Examples: pre-season (美職棒季前賽) 93 Mathematical model: A set of equations, variables, and assumptions 94 A 500’ D 200’ ? 500’ B 400’ C E Assumptions: Angle ADC = angle BCD BEC forms a right triangle DCE forms a straight line Line segment AB parallel to DC Distance from A to B? 95 徒手解決 math model 不可行, 所以發展 computer program 來計算. Computer simulation: The development of computer programs to solve math models. 96 Develop System Computer Model Verify Run Interpretation Output Decipher 97 如果 model 詮釋的 不能對應到 system 的行 為, 表示 model 不合 就換另一個 model! 98 Feedback(回饋): 本身的 output 會影響自己 Here, the model is affected by its output. 99 8.2.4 Queue Application: A Simulated Car Wash Analysis: 1) One wash station 2) 10 minutes for each car to get washed 3) At any time, at most 5 cars waiting to be washed; any others: turned away and not counted 100 carWash • Minimal waiting time is 0 min. • Maximal waiting time:10 min*5car= 50 min arrival queue departure to queue from queue Wash Station car car car car car 101 Average waiting time = sum of waiting times / number of cars In a given minute, a departure is processed before an arrival. 102 如果一台車到達時, 剛好沒有車正在洗 (no car is waiting), 那這台車就會立即進入 wash station. 如果有車正在洗,他必須等待. Sentinel (結束訊號) is 999. 103 System test 1: 8 11 11 13 14 16 16 20 999 104 Time 8 11 11 13 14 16 16 18 20 28 38 48 58 68 Event Waiting Time Arrival 0 (arri 8) Arrival (arri 11) Arrival (arri 11) Arrival (arri 13) Arrival (arri 14) Arrival (arri 16) Arrival (Overflow) Departure 18 - 11 = 7 7 (arri 11,deq 18) Arrival (arri 20) Departure 17 (arri 11) Departure 25 (arri 13) Departure 34 (arri 14) Departure 42 (arri 16) Departure 48 (arri 20) Average waiting time = 173.0 minutes / 7 cars = 24.7 minutes per car 本表為本程式輸出 105 Exercise: Given the following arrival times, determine the average waiting time: 4, 8, 12, 16, 23, 999 (the sentinel) 106 Design of CarWash class 107 /** CarWash * 初始化此 CarWash object. */ public CarWash() 108 /** process * * @param nextArrivalTime 下一輛車的到達時間 * the time when the next arrival will occur. * * @throws IllegalArgumentException – * If nextArrivalTime is less than the current time. */ public void process (int nextArrivalTime) 109 /** finishUp * wash 所有尚未 wash 的車子 */ public void finishUp() /* getResults * returns 所有 arrival 與 departures 紀錄 * 及 average waiting time. */ public LinkedList<String> getResults() 110 Fields? First, we’ll decide what variables will be needed, and then choose the fields from them. 111 PureQueue<Car> carQueue; 112 carQueue 中每個 element 是 Car What information about a car do we need? 113 為何單獨設計 Car class? 為日後維修方便 We have a Car class for the sake of later modifications to the problem. For example, the cost of a wash might depend on the number of axles. 114 Car class • • • • • /* Car * 初始化 Car object * 為指定的下次到達時間 */ public Car (int nextArrivalTime) • • • • /* getArrivalTime /* 記錄 the just dequeued car 的 arrival time. * @return the arrival time of this car */ public int getArrivalTime() 115 To calculate the average waiting time: int numberOfCars, sumOfWaitingTimes; 116 To get the sum of the waiting times: int currentTime, waitingTime; waitingTime = currentTime – car.getArrivalTime(); 計算車子進 wash station 前的 waiting time. 117 The simulation will be event-based: Is the next event an arrival or a departure? 118 // time of next arriving car to the queue int nextArrivalTime; // time of next departing car from the queue // it is 10000 if no car being washed // so next event will be an arrival int nextDepartureTime; 119 Finally, // to store the: // arrivals, // departures, and // average waiting time // LinkedList<String> results; 120 A rule of thumb (經驗之談) is that: a field should be needed in most of the class’s public methods. 121 Fields (Data structure): PureQueue<Car> carQueue; LinkedList<String> results; int currentTime, waitingTime, sumOfWaitingTimes, numberOfCars, nextDepartureTime;//10000 if no car is being washed 122 public CarWash() { carQueue<Car> = new LinkedListPureQueue<Car>(); results = new LinkedList<String>(); results.add (“Time Event Waiting Time”); currentTime = 0; waitingTime = 0; numberOfCars = 0; sumOfWaitingTimes = 0; nextDepartureTime = 10000; } // constructor 123 public void process (int nextArrivalTime) { if (nextArrivalTime < currentTime) throw new IllegalArgumentException(); while (nextArrivalTime >= nextDepartureTime) processDeparture(); processArrival (nextArrivalTime); } 124 protected void processArrival (int nextArrivalTime){ currentTime = nextArrivalTime; results.add (Integer.toString (currentTime) + “\tArrival”); if (carQueue.size() == 5) results.add (“ (Overflow)\n”); else{ /* not overflow */ numberOfCars++; if/*no car being wash*/(nextDepartureTime == 10000) nextDepartureTime = currentTime + /*wash time*/10; else /* wait in the queue */ carQueue.enqueue (new Car (nextArrivalTime)); results.add ("\n"); }/*else*/ } /*end of processArrival*/ 125 protected void processDeparture() { currentTime = nextDepartureTime; results.add (Integer.toString (currentTime) + “\tDeparture\t\t” + Integer.toString (waitingTime) + "\n"); if (!carQueue.isEmpty()) { /* carQueue is not empty */ Car car = carQueue.dequeue(); waitingTime = currentTime – car.getArrivalTime(); sumOfWaitingTimes += waitingTime; nextDepartureTime = currentTime + /*wash time*/ 10; } else { /* carQueue is empty */ waitingTime = 0; nextDepartureTime = 10000; } //else } // end of processDeparture 126 public class Car{ protected int arrivalTime; public Car(){}//default constructor, //for the sake of sub-classses of Car public Car (int nextArrivalTime){ arrivalTime=nextArrivalTime; }// constructor with int parameter public int getArrivalTime(){ return arrivalTime; }/*getArrivalTime*/ }/*Car*/ 127 arrival times 不一定要從 file 讀進來, Instead 相反的, we will read in: int meanArrivalTime; // the average time between arrivals Then, using double randomDouble = random.nextDouble( ); to calculate int timeTillNext = (int)Math.round (-meanArrivalTime * Math.log (1 - randomDouble) ); 128 Exercise: Assume that meanArrivalTime is 3. If Math.log (1 – randomDouble) = –0.6 and currentTime = 25, When will the next arrival occur? 129 timeTillNext = (int)Math.round (-meanArrivalTime * Math.log (1 - randomDouble)); = (int)Math.round (-3 * -0.6) = (int)Math.round (1.8) =2 So, the next arrival will be at time 25+2 = 27. 130