360 likes | 377 Views
COP 3540 Data Structures with OOP. Chapter 4.2 Priority Queues. A Different Kind of Structure. A Priority Queue – a different kind of queue. Similar to a regular queue: insert in rear, remove from front.
E N D
COP 3540 Data Structures with OOP Chapter 4.2 Priority Queues
A Different Kind of Structure • A Priority Queue – a different kind of queue. • Similar to a regular queue: • insert in rear, • remove from front. • Items in priority queue are ordered by somekeyItem with the lowest key / highest key is always at the front. • Items then ‘inserted’ in ‘proper’ position • Idea behind the Priority Queue is simple: • Is a queue • But the items are ordered by a key. • Impliesyour ‘position’ in the queue may be changed by the arrival of a new item.
Applications of Priority Queues • Many, many applications. • Scheduling queues for a processor, print queues, transmit queues, backlogs, etc.…. • Assume’ item with ‘smallest key’ has the highest priority. • This means this item will be in the front of queue • “Obtained” via a remove(). • Note: a priority queue is no longer FIFO! • You will still remove from front of queue, but insertions are governed by a priority.
Priority Queues: Access • remove() • So, the first item has priority and can be retrieved (removed) quickly and returned to calling environment. • Hence, ‘remove()’ is easy (and will take O(1) time) • insert() • But, we want to insert quickly. Must go into proper position. • For our purposes here: Implementing data structure: array; • slowtoinsert(), but for • small number of items in the pqueue, and • where insertionspeed is not critical, • this is the simplest and best approach. • Code for Priority Queue:
class PriorityQApp { public static void main(String[] args) { PriorityQ thePQ = new PriorityQ(5); // what do youknow about the priority queue at this point? thePQ.insert(30); thePQ.insert(50); thePQ.insert(10); thePQ.insert(40); thePQ.insert(20); while( !thePQ.isEmpty() ) { long item = thePQ.remove(); System.out.print(item + " "); // 10, 20, 30, 40, 50 Note: ORDERED! } // end while System.out.println(""); } // end main() //------------------------------------------------------------- } // end class PriorityQApp
//priorityQ.java class PriorityQ { // array in sorted order, from max at 0 to min at size-1 private int maxSize; private long[] queArray; private int nItems; public PriorityQ(int s) { // constructor // Samo samo! maxSize = s; queArray = new long[maxSize]; nItems = 0; } public void insert(long item) { // insert item // ============================= int j; // insert: 30, 50, 10, 40, 20 if (nItems==0) // if no items, // look carefully at the ‘disposition’ of the index…. queArray[nItems++] = item; // insert at 0 // Note: algorithm increments first then inserts. else // if queue has items, // So index always points to the last item inserted! { // So, if pqueue is empty, this is simple, but: for (j=nItems-1; j>=0; j--) // start at end, // if pqueue is NOT empty, must find correct spot!!!! { if (item > queArray[j] ) // if new item larger, queArray[j+1] = queArray[j]; // shift upward; Keep shifting until ‘if’ is false. else // if smaller, done shifting. break; // We’ve moved everything forward to open a slot for ‘item.’ } // end for queArray[j+1] = item; // insert new item whether we’ve had to shift or not. nItems++; // increment number of items in the pqueue. } // end else (nItems > 0) // Note value of j when we leave the for loop! queArray[j+ 1 is correct. =============================================== } // end insert() public long remove() // remove minimum item; remove from front of queue. Easy { return queArray[--nItems]; } // can we infer we always point to next open slot? public long peekMin() // peek at minimum item { return queArray[nItems-1]; } public boolean isEmpty() // true if queue is empty Very straightforward. { return (nItems==0); } public boolean isFull() // true if queue is full { return (nItems == maxSize); } } // end class PriorityQ`
Let’s insert 30, 50, 10, 40, 20 • Insert 30 • First: queue is empty; 30 is easily added. nitems = 1. • Insert 50; nitems not equal 0 (= 1); go to else branch. • Execute the for loop – one time only. (j set to 0; and ‘true’) • item > q(0)? True • 30 from q(0) gets moved into the queue by one. Q(1) = 30 • Leaves q(0) ‘available;’ Loop. • For loop: j set to -1; predicate is false. Fall out of for loop • j = -1; incremented to 0;l 50 inserted into queue(0). • q(0) = 50; q(1)=30; increment nitems to 2. • Insert 10; nitems = 2. • Go to if within for loop. j = nitems-1 j = 1; for is true. • Is 10 > q(1)? 10 > 30? No. Break! Takes me out of the for-loop. • q[j+1] = item. Is q[2] = 10; nitems = 3 (queue = 50, 30, 10) • Insert 40 nitems = 3 • Go to for loop; j set to 2. • If item > q[2]? Is 40 > 10? Yes; • Move q[2] to q[3]; (q[2] is now ‘available’). Loop. • Iterate: for loop; j set to 1 • If item > q[1]? 40 > 30? Yes; • Move q[1] to q[2]; (q[1] is now available). Loop. • Iterate for loop. j set to 0. • If item > q[0]? 40 > 50? No! Break out of for loop and fall through. • q[j+1] = item is q[1] = 40. • nitems is increments to 4. queue is (now) 50, 40, 30, 10
Discussion of Priority Queue Code • Did you like the code? Any problems?? • PriorityQApp • Pretty simple: • Code builds the priority queue with explicit entries and, • as long as the pqueue is not empty, it displays the pqueue entries as it removes them. • Priority (the queue) • If pqueue is empty, • insert() is simple, as shown. • Else, • start at front of queue and shifts existing items to the right one at a time. • It ‘inserts’ the new item and increments the total number of items in the pqueue. • Note: the isFull() should be checked before ever shifting or attempting an insert! You know you will (at best) only add one entry. So be certain isFull allows you to add one item!! • Note: front is always located at nitems-1; rear is always located at 0.
Efficiency of Priority Queues • remove() runs in O(1) time – super • Recall, front is at location nitems-1 • insert() runs in O(n) time (makes sense!) • Recall, rear (for the insert) will start to search at nitems-1, because here’s where the largest entry will be located...
Parsing Arithmetic Expressions • Application: parsing arithmetic expressions: x = -b + ((b**2 – 4 * a * c)) / 2*a Recognize this expression? It is the positive root of a quadratic expression In expressions such as these, how does the computer parse (analyze) the expression and then, evaluate it correctly? Remember, we have a hierarchyofoperators to consider too and MUST be enforced in order for the correct code to be generated and evaluated.
Parsing Arithmetic Expressions • Best computer-based algorithmic approach: Two steps: • 1. Transform arithmetic expression into postfix • 2. Evaluate the expression (using stacks) • You should have / will have had this in Comp Structures. It is an essential concept. • Postfix notation removes parentheses and allows direct evaluation (using stacks).
Postfix Notation • Goal: Develop a string where the operators (*, -, +, …) appear ‘last’ (hence the term: postfix). e.g. ab+. Postfix notation a.k.a. Reverse Polish Notation • We normally use infix notation, where the operators lie in between the operands we are interested in, such as a+b. • Most of the operators we use are binary. • There is also a ‘prefix’ notation, which has more limited applications. We will not deal with this one here.
Infix and postfix notations • In evaluating an expression in postfix notation, the convention is that an operator operates on the twopreviousoperands. That is the rule. • For more complex expressions, like those involving parentheses or trig functions, etc., the rule still applies, but requires more advanced recognition algorithms. • (Just as +, -, … are ‘operators,’ so too are sin(), cos(), tan-1(), etc. And they have precedence as functions.) • Here are some statements in postfix notation taken from the book.
Table: infix to postfix notations You know parentheses override normal hierarchical evaluation. Further, these can be nested, so: Infix Postfix a+b-c ab+c- a*b/c ab*c/ a+b*c abc*+ a*b+c ab*c+ a*(b+c) abc+* a*b+c*d ab*cd*+ ((a+b)*c)-d ab+c*d- a+b*(c-d/(e+f)) abcdef+/*+ Once the first step of the algorithm is done (converting infix to postfix) then the postfix form can be evaluated. But first: let’s look at the conversion process:
Enter: Operator Precedence • If the nextoperator has the same or lesser(or equal) precedence, then you can perform the operation and substitute the result into the expression and continue. • If the nextoperator has greaterprecedence, we have a bit more complexity Consider a common familiar case: 3+4*5 We have 3 + 4 but we are NOT at end of expression and the Nextoperator (which we don’t see) has higher precedence. Continue We know * and / have higher precedence than + and – and thus must be carried out before any + or – operations. Is the answer: 35 or 23? After the 5 is read, the multiplication can be carried out because there is nothing afterward or no operator afterwards with a higher priority. thus, we have 345*+ and answer is 23.
Operator Precedence with Parentheses • Consider: 3*(4+5) • Remember: parentheses are operators and affect precedence! • Nice rule: a ‘closing’ parenthesis always causes the most recent expressing to be evaluated back to the ‘opening’ parenthesis even if nested. • Parentheses have higherprecedence than almost all operators (and have equal to the highest precedence in C) • So, the rule of not being able to evaluate if a higher precedent operator follows holds. • Discuss: 3*(4+5) 3*(4+5) – only when the closing parenthesis is reached can the expression (4+5) be evaluated. Thus we have 345+* Giving: 3*9 or 27.
Saving Operators on a Stack – Creating a Postfix Notation • Use a Stack to track operators • As we scan the string, push() operators onto a Stack until we encounter an operator of lower precedence. • Pop the stack and perform an operation. • Push results back on to stack and continue… • Need to transform an infix expression into postfix first. • Remember: we cannot later evaluate the expression until we get it into postfix format. • Let’s scan an expression one token at a time and basedonthetoken, we will push the stack, evaluate, or pop the stack. • Consider the pseudocode on Table 4.10.
Table 4.10 – Pseudocode – Sort of: ‘If input token is:…’ Input Item (infix) Action Operand Write it to output stream note: operands written to output stream Open parenthesis Push onto stack note: this is an ‘operator’ stack Close parenthesis While stack not empty, repeat: pop stack if item not ‘(,’ write to output stream (it will be an operator…) Quit loop if item is ( (note: paren is not written to stream) Operator (opThis) If stack empty push opThis (add operator to stack) Otherwise (stack is not empty) // we’ve work to do… While stack not empty, repeat: pop item from stack if item is (, push it back or if item is operator (call it ‘opTop’) and if opTop < opThis push opTop or(like + and *) if opTop >= opThis, output opTop (like * and +) Quit loop if opTop < opThis ( like + and * ) Push opThis ( onto top of stack) end Otherwise No more items While (stack is not empty) pop item; output it. LET’S APPLY THE ALGORITHM
Expression: a*(b+c) The ‘brute force’ approach! Input Item (infix) Action Postfix Stack Operand Write it to output stream a nothing Open parenthesis Push onto stack Close parenthesis When stack not empty, repeat: pop stack if item not (, write to output Quit loop if item is ( Operator (opThis) If stack empty push opThis (add optr to stack) Otherwise (stack is not empty) while stack not empty, repeat: pop stack if item is (, push it back or if item is operator (opTop) and if opTop < opThis push opTop or if opTop >= opThis, output opTop Quit loop if opTop < opThis or item is ( Push opThis No more items While stack is not empty pop item; output it.
Expression: a*(b+c) Input Item (infix) Action Postfix Stack Operand Write it to output stream a * Open parenthesis Push onto stack Close parenthesis When stack not empty, repeat: pop stack if item not (, write to output Quit loop if item is ( Operator (opThis) If stack empty (it is…) push opThis (add optr to stack) Otherwise (stack is not empty) while stack not empty, repeat: pop stack if item is (, push it back or if item is operator (opTop) and if opTop < opThis push opTop or if opTop >= opThis, output opTop Quit loop if opTop < opThis or item is ( Push opThis No more items While stack is not empty pop item; output it.
Expression: a*(b+c) Input Item (infix) Action Postfix Stack Operand Write it to output stream a ( (top) * Open parenthesis Push onto stack Close parenthesis When stack not empty, repeat: pop stack if item not (, write to output Quit loop if item is ( Operator (opThis) If stack empty push opThis (add optr to stack) Otherwise (stack is not empty) while stack not empty, repeat: pop stack if item is (, push it back or if item is operator (opTop) and if opTop < opThis push opTop or if opTop >= opThis, output opTop Quit loop if opTop < opThis or item is ( Push opThis No more items While stack is not empty pop item; output it.
Expression: a*(b+c) Input Item (infix) Action Postfix Stack Operand Write it to output stream ab ( * Open parenthesis Push onto stack Close parenthesis When stack not empty, repeat: pop stack if item not (, write to output Quit loop if item is ( Operator (opThis) If stack empty push opThis (add optr to stack) Otherwise (stack is not empty) while stack not empty, repeat: pop stack if item is (, push it back or if item is operator (opTop) and if opTop < opThis push opTop or if opTop >= opThis, output opTop Quit loop if opTop < opThis or item is ( Push opThis No more items While stack is not empty pop item; output it.
Expression: a*(b+c) Input Item (infix) Action Postfix Stack Operand Write it to output stream ab ( * Open parenthesis Push onto stack Close parenthesis When stack not empty, repeat: pop stack if item not (, write to output Quit loop if item is ( Operator (opThis)If stack empty ------------------------------- it is not, …. push opThis (add optr to stack) Otherwise(stack is not empty) ------- true, so: while stack not empty, repeat: pop stack yields ( and stack is now * if item (opTop) is (, push it back (it was) or (stack is ( if item (opTop) is operator (opTop) (it was not) and if opTop < opThis push opTop or if opTop >= opThis, output opTop Quit loop if opTop < opThis or item is ( (it wasso quit loop) Push opThis onto stack stack is now: + End Otherwise ( No more items While stack is not empty * pop item; output it.
Expression: a*(b+c) Input Item (infix) Action Postfix Stack Operand Write it to output stream abc + ( Open parenthesis Push onto stack * Close parenthesis When stack not empty, repeat: pop stack if item not (, write to output Quit loop if item is ( Operator (opThis) If stack empty push opThis (add optr to stack) Otherwise (stack is not empty) while stack not empty, repeat: pop stack if item is (, push it back or if item is operator (opTop) and if opTop < opThis push opTop or if opTop >= opThis, output opTop Quit loop if opTop < opThis or item is ( Push opThis onto stack No more items While stack is not empty pop item; output it.
Expression: a*(b+c) Input Item (infix) Action Postfix Stack Operand Write it to output stream abc + ( Open parenthesis Push onto stack * Close parenthesisWhen stack not empty, repeat: pop item gives up a + stack has ( if item not (, (it is not)write to output * Output = abc+ Stack = ( * loop pop stack - gives us a ( … Quit loop if item is ( (it is) Output still abc+ Stack = * Operator (opThis) If stack empty push opThis (add optr to stack) Otherwise (stack is not empty) while stack not empty, repeat: pop stack if item is (, push it back or if item is operator (opTop) and if opTop < opThis push opTop or if opTop >= opThis, output opTop Quit loop if opTop < opThis or item is ( Push opThis onto stack No more items While stack is not empty pop item; output it.
Expression: a*(b+c) Input Item (infix) Action Postfix Stack Operand Write it to output stream abc+ * Open parenthesis Push onto stack Close parenthesis When stack not empty, repeat: pop stack if item not (, write to output Quit loop if item is ( Operator (opThis) If stack empty push opThis (add optr to stack) Otherwise (stack is not empty) while stack not empty, repeat: pop stack if item is (, push it back or if item is operator (opTop) and if opTop < opThis push opTop or if opTop >= opThis, output opTop Quit loop if opTop < opThis or item is ( Push opThis onto stack No more items While stack is not empty pop item; output it. Yields: abc+* empty stack
Java Code: Convert Infix to Postfix • Remember: this conversion from our standard infix notation to postfix is ONLY to facilitate later evaluation of the expression. • This conversion to postfix is done for us by the compiler, so that during execution, the computer can actually evaluate the expression using the current values of the variables. • Here’s the actual code that would implement this previous algorithm…
Infix to Postfix notation – class definition and Constructor – more to come class InToPost // infix to postfix conversion { private StackX theStack; private String input; private String output = ""; //-------------------------------------------------------------- public InToPost(String in) // constructor { input = in; //the text string is inputted int stackSize = input.length(); // Its length (a String operations) // is the stackSize theStack = new StackX(stackSize); // Passed to Constructor to } // create the Stack //--------------------------------------------------------------
public String doTrans() { // do translation to postfix for(int j=0; j<input.length(); j++) // for each char { char ch = input.charAt(j); // get it gets a character theStack.displayStack("For "+ch+" "); // *diagnostic* displays the character. switch(ch) { Switch to process the input character. case '+': // it's + or - case '-': gotOper(ch, 1); // go pop operators break; // (precedence 1) case '*': // it's * or / case '/': gotOper(ch, 2); // go pop operators Note that when we go to pop the stack break; // (precedence 2) we send opThis and a ‘precedence.’ case '(': // it's a left paren Note: * and / have precedence 2….. theStack.push(ch); // push it break; case ')': // it's a right paren gotParen(ch); // go pop operators break; default: // must be an operand output = output + ch; // write it to output break; } // end switch Note: we determine if we have an ‘operator’ } // end for or an operand. This drives the logic! while( !theStack.isEmpty() ) { // pop remaining opers theStack.displayStack("While "); // *diagnostic* output = output + theStack.pop(); // write to output } theStack.displayStack("End "); // *diagnostic* return output; // return postfix } // end doTrans()
public void gotOper(char opThis, int prec1) { // got operator from input; note: it is opThis and its precedence is sent too. while( !theStack.isEmpty() ) { char opTop = theStack.pop(); Pop the operator STack if( opTop == '(' ) // if it's a '(‘ IF operator is a (, replace it and we are done here… { theStack.push(opTop); // restore '(' break; } // -------------------------------------------------------------------- else // it's an operator Need to establish precedent comparisons. { int prec2; // setting up to compare precedence ofopTop and opThis!!! if(opTop=='+' || opTop=='-') // Calculate precedence of opTop prec2 = 1; // Set to 1 if it has low precedence (+ or -.) else prec2 = 2; // otherwise, it is high if(prec2 < prec1) // if prec of new op less than precedence of passed token, push { // and we are done here. theStack.push(opTop); // save newly-popped op back onto Stack break; } // ---------------------------------------------------------------- else // prec of new not less than precedence of passed operator (same or greater…) output = output + opTop; // add popped operator to output postfix string that is evolving. } // end else (it's an operator) } // end while -------------------------------------------------------- and you are done again. theStack.push(opThis); // push new operator onto Stack } // end gotOp() public void gotParen(char ch) { // got right paren from input while( !theStack.isEmpty() ) { char chx = theStack.pop(); // pop the Stack if( chx == '(' ) // if popped '(‘ // if hit balancing (, we are done here. Otherwise, add char to postfix string. break; // we're done else // if popped operator output = output + chx; // output it } // end while } // end popOps() should be end gotParen B O O K E R R O R //-------------------------------------------------------------- } // end class InToPost
class InfixApp { public static void main(String[] args) throws IOException { String input, output; while(true) { System.out.print("Enter infix: "); System.out.flush(); input = getString(); // read a string from kbd if( input.equals("") ) // quit if [Enter] break; // make a translator InToPost theTrans = new InToPost(input); output = theTrans.doTrans(); // do the translation System.out.println("Postfix is " + output + '\n'); } // end while } // end main() //-------------------------------------------------------------- public static String getString() throws IOException { InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); String s = br.readLine(); return s; } //-------------------------------------------------------------- } // end class InfixApp
Evaluating Postfix Expressions – Java Code • Now that we have an algorithm to translate our accustomed infix notation to postfix, we now need to be able to submit a postfix expression to the run time environment so that the expression can be evaluated!
// postfix.java import java.io.*; // for I/O class StackX { private int maxSize; private int[] stackArray; private int top; public StackX(int size) { // constructor maxSize = size; stackArray = new int[maxSize]; top = -1; } public void push(int j) // put item on top of stack NOTHING NEW HERE… { stackArray[++top] = j; } public int pop() // take item from top of stack { return stackArray[top--]; } public int peek() // peek at top of stack { return stackArray[top]; } public boolean isEmpty() // true if stack is empty { return (top == -1); } public boolean isFull() // true if stack is full { return (top == maxSize-1); } public int size() // return size { return top+1; } public int peekN(int n) // peek at index n { return stackArray[n]; } public void displayStack(String s) { System.out.print(s); System.out.print("Stack (bottom-->top): "); for(int j=0; j<size(); j++) { System.out.print( peekN(j) ); System.out.print(' '); } System.out.println(""); } } // end class StackX
class ParsePost { private StackX theStack; private String input; public ParsePost(String s) // CONSTRUCTOR { input = s; } public int doParse() { theStack = new StackX(20); // make new stack char ch; int j; int num1, num2, interAns; for(j=0; j<input.length(); j++) // for each char, { ch = input.charAt(j); // read from input theStack.displayStack(""+ch+" "); // *diagnostic* if(ch >= '0' && ch <= '9') // if it's a number theStack.push( (int)(ch-'0') ); // push it else // it's an operator { num2 = theStack.pop(); // pop operands num1 = theStack.pop(); switch(ch) // do arithmetic { case '+': interAns = num1 + num2; break; case '-': interAns = num1 - num2; break; case '*': interAns = num1 * num2; break; case '/': interAns = num1 / num2; break; default: interAns = 0; } // end switch theStack.push(interAns); // push result } // end else } // end for interAns = theStack.pop(); // get answer return interAns; } // end doParse() } // end class ParsePost
class PostfixApp { public static void main(String[] args) throws IOException { String input; int output; while(true) { System.out.print("Enter postfix: "); System.out.flush(); input = getString(); // read a string from kbd if( input.equals("") ) // quit if [Enter] break; // make a parser ParsePost aParser = new ParsePost(input); output = aParser.doParse(); // do the evaluation System.out.println("Evaluates to " + output); } // end while } // end main() public static String getString() throws IOException { InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); String s = br.readLine(); return s; } } // end class PostfixApp