Queues are dynamic collections which have some concept of order. This can be either based on order of entry into the queue - giving us First-In-First-Out (FIFO) or Last-In-First-Out (LIFO) queues. Both of these can be built with linked lists: the simplest "add-to-head" implementation of a linked list gives LIFO behaviour. A minor modification - adding a tail pointer and adjusting the addition method implementation - will produce a FIFO queue. See image belowe:
Performance
A straightforward analysis shows that for both these cases, the time needed to add or delete an item is constant and independent of the number of items in the queue. Thus we class both addition and deletion as an O(1) operation. For any given real machine+operating system+language combination, addition may take c1c2 seconds, but we aren't interested in the value of the constant, it will vary from machine to machine, language to language, etc. The key point is that the time is not dependent on n - producing O(1) algorithms. seconds and deletion Once we have written an O(1) method, there is generally little more that we can do from an algorithmic point of view. Occasionally, a better approach may produce a lower constant time. Often, enhancing our compiler, run-time system, machine, etc will produce some significant improvement. However O(1) methods are already very fast, and it's unlikely that effort expended in improving such a method will produce much real gain!
Coding: Example
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#define MAX 100
char *p[MAX], *pop(void);
int spos = 0;
int rpos = 0;
void add(void), push(char *q), print(void), remove(void);
void add(void)
{
char s[256], *p;
do {
printf("spos %d: ", spos+1);
gets(s);
if(*s==0) {
break;
}
p = (char *) malloc(strlen(s)+1);
if(!p) {
printf("Out of memory.\n");
return;
}
strcpy(p, s);
if(*s) {
push(p);
}
} while(*s);
}
void print(void)
{
int t;
for(t=rpos; t < spos; ++t)
printf("%d. %s\n", t+1, p[t]);
}
void remove(void)
{
char *p;
if((p=pop())==NULL) {
return;
}
printf("%s\n", p);
}
void push(char *q)
{
if(spos==MAX) {
printf("List Full\n");
return;
}
p[spos] = q;
spos++;
}
char *pop(void)
{
if(rpos==spos) {
printf("No more.\n");
return NULL;
}
rpos++;
return p[rpos-1];
}
int main(void)
{
char s[80];
register int t;
for(t=0; t < MAX; ++t) {
p[t] = NULL;
}
while(1) {
printf("Add(A), Print(P), Remove(R), Quit(Q): ");
gets(s);
*s = toupper(*s);
switch(*s) {
case 'A':
add();
break;
case 'P':
print();
break;
case 'R':
remove();
break;
case 'Q':
exit(0);
}
}
return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#define MAX 100
char *p[MAX], *pop(void);
int spos = 0;
int rpos = 0;
void add(void), push(char *q), print(void), remove(void);
void add(void)
{
char s[256], *p;
do {
printf("spos %d: ", spos+1);
gets(s);
if(*s==0) {
break;
}
p = (char *) malloc(strlen(s)+1);
if(!p) {
printf("Out of memory.\n");
return;
}
strcpy(p, s);
if(*s) {
push(p);
}
} while(*s);
}
void print(void)
{
int t;
for(t=rpos; t < spos; ++t)
printf("%d. %s\n", t+1, p[t]);
}
void remove(void)
{
char *p;
if((p=pop())==NULL) {
return;
}
printf("%s\n", p);
}
void push(char *q)
{
if(spos==MAX) {
printf("List Full\n");
return;
}
p[spos] = q;
spos++;
}
char *pop(void)
{
if(rpos==spos) {
printf("No more.\n");
return NULL;
}
rpos++;
return p[rpos-1];
}
int main(void)
{
char s[80];
register int t;
for(t=0; t < MAX; ++t) {
p[t] = NULL;
}
while(1) {
printf("Add(A), Print(P), Remove(R), Quit(Q): ");
gets(s);
*s = toupper(*s);
switch(*s) {
case 'A':
add();
break;
case 'P':
print();
break;
case 'R':
remove();
break;
case 'Q':
exit(0);
}
}
return 0;
}
Answer:
6.1 Priority Queues
Often the items added to a queue have a priority associated with them: this priority determines the order in which they exit the queue - highest priority items are removed first.
This situation arises often in process control systems. Imagine the operator's console in a large automated factory. It receives many routine messages from all parts of the system: they are assigned a low priority because they just report the normal functioning of the system - they update various parts of the operator's console display simply so that there is some confirmation that there are no problems. It will make little difference if they are delayed or lost.
However, occasionally something breaks or fails and alarm messages are sent. These have high priority because some action is required to fix the problem (even if it is mass evacuation because nothing can stop the imminent explosion!).
Typically such a system will be composed of many small units, one of which will be a buffer for messages received by the operator's console. The communications system places messages in the buffer so that communications links can be freed for further messages while the console software is processing the message. The console software extracts messages from the buffer and updates appropriate parts of the display system. Obviously we want to sort messages on their priority so that we can ensure that the alarms are processed immediately and not delayed behind a few thousand routine messages while the plant is about to explode.
As we have seen, we could use a tree structure - which generally provides O(logn) performance for both insertion and deletion. Unfortunately, if the tree becomes unbalanced, performance will degrade to O(n) in pathological cases. This will probably not be acceptable when dealing with dangerous industrial processes, nuclear reactors, flight control systems and other life-critical systems.
AsideThe great majority of computer systems would fall into the broad class of information systems - which simply store and process information for the benefit of people who make decisions based on that information. Obviously, in such systems, it usually doesn't matter whether it takes 1 or 100 seconds to retrieve a piece of data - this simply determines whether you take your coffee break now or later. However, as we'll see, using the best known algorithms is usually easy and straight-forward: if they're not already coded in libaries, they're in text-books. You don't even have to work out how to code them! In such cases, it's just your reputation that's going to suffer if someone (who has studied his or her algorithms text!) comes along later and says"Why on earth did X (you!) use this O(n2) method -Of course, hardware manufacturers are very happy if you use inefficient algorithms - it drives the demand for new, faster hardware - and keeps their profits high! |
There is a structure which will provide guaranteed O(logn) performance for both insertion and deletion: it's called a heaps.