390 likes | 625 Views
Chapter 6 Stacks and Queues. 6.1 Stacks. A stack is a linear data structure which can be accessed only at one of its ends for storing and retrieving data. A LIFO (Last In First Out) structure. Chapter 6 Stacks and Queues. 6.1 Stacks. Operations on a stack.
E N D
Chapter 6 Stacks and Queues 6.1 Stacks A stack is a linear data structure which can be accessed only at one of its ends for storing and retrieving data. A LIFO (Last In First Out) structure
Chapter 6 Stacks and Queues 6.1 Stacks Operations on a stack initialize(stack) --- clear the stack empty(stack) --- check to see if the stack is empty full(stack) --- check to see if the stack is full push(el,stack) --- put the element el on the top of the stack pop(stack) --- take the topmost element from the stack How to implement a stack?
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (a): Use an array Top of stack #define Max 50 typedef int Eltype; typedef Eltype StackType[Max]; int top;
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (a): Use an array Initialize(StackType stack) push(Eltype el, StackType stack) { { top = 0; if (full(stack)) } printf(“stack overflow”); empty(StackType stack) else stack[top++] = el; { } return top == 0; Eltype pop(StackType stack) } { full(StackType stack) if (empty(stack)) { printf(“stack underflow”); return top == Max; else return stack[--top]; } }
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (a): Use an array Drawback: initialize() uses stack as a parameter, but this parameter is not read or modified, since initialize() assigns zero to the global variable top. This means that only one stack can be used in the entire program. One way to solve this problem is to incorporate top in the list of parameters for each function. This, however, would somewhat violate the original statement of the stack-related functions which do not include such a parameter explicitly.
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (b): Use an array, but using the first cell of the array as top #define Max 50 typedef int Eltype; typedef Eltype StackType[Max+1]; top
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (b): Use an array, but using the first cell of the array as top Initialize(StackType stack) push(Eltype el, StackType stack) { { stack[0]=1; if (full(stack)) } printf(“stack overflow”); empty(StackType stack) else stack[stack[0]++] = el; { } return stack[0] == 1; Eltype pop(StackType stack) } { full(StackType stack) if (empty(stack)) { printf(“stack underflow”); return stack[0] == Max; else return stack[--stack[0]]; } }
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (b): Use an array, but using the first cell of the array as top Drawback: It severely limits the possible choices for Eltype which should be now an int or char, since stack[0] is incremented and decremented. Therefore, no structure can be stored and even float numbers can cause problems.
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (c): stack is declared as a structure with two fields: one for storage, one for keeping track of the topmost position. #define Max 50 typedef int Eltype; typedef struct StackRec { Eltype storage[Max]; int top; }; typedef struct StackRec StackType;
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (c): stack is declared as a structure Initialize(StackType *stack) push(Eltype el, StackType *stack) { { (*stack).top=0; if (full(*stack)) } printf(“stack overflow”); empty(StackType stack) else (*stack).storage[ (*stack).top++]=el; { } return stack.top == 1; Eltype pop(StackType *stack) } { full(StackType stack) if (empty(*stack)) { printf(“stack underflow”); return stack.top == Max; else return (*stack).storage[--(*stack).top];; } }
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (c): stack is declared as a structure Drawback: the array limit the number of elements that can be stored on the stack If Max is declared too small, stack overflow may prematurely occur, and if Max is too large, a certain amount of memory will be unnecessarily wasted.
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (d): stack is declared as a structure, use linked list for storage Struct StackRec { ElType storage; struct StackRec *next }; typedef struct StackRec *StackType;
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (d): stack is declared as a structure, use linked list for storage Initialize(StackType *stack) push(Eltype el, StackType *stack) { { StackType tmp=(StackType) malloc( (*stack)=NULL; sizeof(struct StackRec)); } if (tmp==NULL) printf(“memory overflow”); empty(StackType stack) else { tmp->storage=el; tmp->next=*stack; { *stack=tmp; } return stack == NULL; } } Insert in the front
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (d): stack is declared as a structure, use linked list for storage ElType pop (stackType *stack) { if (*stack == NULL) printf(“stack underflow”); else { StackType tmp=*stack; ElType el=(*stack)->storage; *stack=(*stack)->next; free(tmp); return el; } }
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (d): stack is declared as a structure, use linked list for storage Drawback: In this implementation, it has to be remembered that in some cases a stack variable is passed to the functions, in other cases, a pointer to this variable is passed. For consistency, it is better if only one passing mode is used. If stack variables are declared as pointers to pointers, there would be no need to pass their addresses.
Chapter 6 Stacks and Queues 6.1 Stacks Implementation (e): stack is declared as a pointer of pointer, use linked list for storage, Another way of passing stack instead of &stack is by declaring stack as a one-cell array, since all arrays are passed by reference and not by value.
Chapter 6 Stacks and Queues 6.1 Stacks Example: Adding very large numbers Treat these numbers as strings of numerals, store the numbers corresponding to these numerals on two stacks, and then perform addition by popping numbers from the stacks 1 2 9 4 2 3 3 + = 7 6 6 8 5 1 8732 + 5629 = 14361
Chapter 6 Stacks and Queues 6.1 Stacks Example: Adding very large numbers Read the numerals of the first number and store the numbers corresponding to them on one stack; Read the numerals of the second number and store the numbers corresponding to them on another stack; result=0; while at least one stack is not empty pop a number from each non-empty stack and add them; push the sum (minus 10 if necessary) on the result stack; store carry in result; push carry on the result stack if it is not zero; pop numbers from the result stack and display them;
Chapter 6 Stacks and Queues 6.2 Queues A queue is simply a waiting line, which grows by adding elements to its end and shrinks by taking elements from its front. (FIFO: First-In First-Out) front
Chapter 6 Stacks and Queues 6.2 Queues Operations for Queues: initialize(queue) -- clear the queue empty(queue) -- check to see if the queue is empty full(queue) -- check to see if the queue is full enq(el,queue) -- put the element el at the end of the queue deq(queue) -- take the first element from the queue
Chapter 6 Stacks and Queues 6.2 Queues Array Implementation 0 1 2 … Max-1 first and last index (first=last=-1) After enqueue the elements 2,4,7: 2 4 7 first last
Chapter 6 Stacks and Queues 6.2 Queues Array Implementation We must consider the situation of wrapping-around. Two possible situations when the queue is full: Happen when elements are dequeued and the spaces are reused.
Chapter 6 Stacks and Queues 6.2 Queues Array Implementation Operations:
Chapter 6 Stacks and Queues 6.2 Queues Array Implementation Treat the array as circular:
Chapter 6 Stacks and Queues 6.2 Queues Array Implementation Struct Qstorage { int first, last; int storage[Max]; }; typedef struct Qstorage QType[1]; QType Q; initialize(QType Q) { Q[0].first=Q[0].last=-1; }
Chapter 6 Stacks and Queues 6.2 Queues Array Implementation full(Qtype Q) { return Q[0].first == 0 && Q[0].last == Max-1 || Q[0].first == Q[0].last + 1; } empty(Qtype Q) { return Q[0].first == -1; }
Chapter 6 Stacks and Queues 6.2 Queues Array Implementation enq(int el, Qtype Q) { if (!full(Q)) if (Q[0].last == Max-1 || Q[0].last == -1) { Q[0].storage[0]=el; Q[0].last=0; if (Q[0].first == -1) Q[0].first=0; } else Q[0].storage[++Q[0].last]=el; else printf(“Full Q\n”); }
Chapter 6 Stacks and Queues 6.2 Queues Array Implementation deq (Qtype Q) { int tmp; if (!empty(Q)) { tmp=Q[0].storage[Q[0].first]; if (Q[0].first == Q[0].last) Q[0].last = Q[0].first = -1; else if (Q[0].first == Max-1) Q[0].first = 0; else Q[0].first++; return tmp; } else printf(“Empty Q\n”); }
Chapter 6 Stacks and Queues 6.2 Queues Exercise: Implement the queue operations using linked lists. Hint: (just a suggestion, no standard answer) struct QStorage { Eltype el; QStorage *next; } QStorage *first, *last; Initially, first=last=NULL;
Chapter 6 Stacks and Queues 6.2 Queues Applications of queues in Queuing Theory In queuing processes, there are a number of customers coming (arrival rate) to servers (number of servers) to receive service. The throughput of the server (service rate) may be limited. Therefore, customers have to wait (waiting time) in queue(s) (number of queues) before they are served and they spend some amount of time (service time or processing time) while they are being served. Two important parameters to minimize: average waiting time and average queue length
Chapter 6 Stacks and Queues 6.2 Queues Applications of queues in Queuing Theory Example: A Bank’s service queuing example
Chapter 6 Stacks and Queues 6.2 Queues
Chapter 6 Stacks and Queues 6.2 Queues Currently, six clerks are employed, no lines are ever observed, and the bank management would like to know whether six clerks is too many. Would five suffice? Four? Maybe even three? Can lines be expected at any time? Write a simulation program to find out.
Chapter 6 Stacks and Queues 6.2 Queues Implementation code for simulating the number of servers needed
Chapter 6 Stacks and Queues 6.2 Queues
Chapter 6 Stacks and Queues 6.3 Priority Queues In many situations simple queues are inefficient. First in, first out scheduling has to be overruled using some priority criteria. In situations like these, a priority queue is needed. In priority queues, elements are dequeued according to their priority and to their current queue position. The problem with a priority queue is in finding an efficient implementation which allows relatively fast enqueuing and dequeuing.
Chapter 6 Stacks and Queues 6.3 Priority Queues Priority queues can be represented by a linked list with two variations: 1. Enqueuing each element to proper place according to priority: dequeuing time O(1), enqueuing time O(n) 2. Enqueuing as they arrived, dequeuing searchs the highest priority element: dequeuing time: O(n), enqueuing time O(1) Another (costly but efficient) implementation: Use as many as queues as there are priorities, All elements with the same priority go to the same queue.
Chapter 6 Stacks and Queues Exercise: Write a program to perform the four basic arithmetic operations, +, -, *, /, on very large integers (the result of division should also be an integer, disregarding the remainder.)