Chapter 5: Queues
Table of Contents
- Chapter 5: Queues
- 1. Queues
- 2. Applications
- 3. Implementation
- 4. Queues: Problems & Solutions
- Pro 1.1: Reverse a Queue
- Pro 1.2: Reverse first k elements of a queue
- Pro 2.1: Implement a Queue using stacks
- Pro 2.2: Implement a Stack using queues
- Pro 3: Maximum Sum in Sliding Window
- Pro 4: Give a Formula in terms of rear, front and n
- Pro 5: Implement Doubly Ended Queues
- Pro 6: Check whether each Successive Pair is Consecutive or not
- Pro 7: Rearrange the Elements by Interleaving
1. Queues
A queue is an ordered list in which insertion are done at rear and deletions are done at front. The first element to be inserted is the first one to be deleted. Hence, it is called First in First out (FIFO) or Last on Last out (LILO) list.
2. Applications
- Operating systems schedule jobs (with equal priority) in the order of arrival
- Multi-programming
- Asynchronous data transfer (file IO, pipes, sockets)
- Any first-come first-served scenario
- Auxiliary data structure for algorithms
- Component of other data structures
3. Implementation
Simple Circular Array Implementation
struct ArrayQueue{
int front, rear;
int capacity;
int *array;
};
struct ArrayQueue *Queue(int size){
struct ArrayQueue *Q=malloc(sizeof(struct ArrayQueue));
if(!Q) return NULL;
Q->capacity=size;
Q->front=Q->rear=-1;
/*in this version, we initialize the pointers both at -1*/
/*and index -1 is a NULL element space sacrifice for convention*/
Q->array=malloc(Q->capacity * (sizeof(int));
if(!Q->arrray) return NULL;
return Q;
}
int IsEmptyQueue(struct ArraQueue *Q){
return (Q->front==-1);
}
int IsFullQueue(struct ArrayQueue *Q){
return ((Q->rear +1) % Q->capacity == Q->front); //IMP: (rear+1)%MaxSize==front ?
}
int QueueSize(){
return (Q->capacity - Q->front + Q->rear + 1) % Q->capacity; //IMP: (MaxSize-front+rear+1)%MaxSize
}
void EnQueue(struct ArrayQueue *Q, int data){
if(IsFullQueue(Q))
printf("Queue Overflow");
else{
Q->rear=(Q->rear +1) % Q->capacity;
Q->array[Q->rear]=data;
if(Q->front==-1) //first element enQueue, forward the front pointer
Q->front=Q->rear;
}
}
int DeQueue(struct ArrayQueQue *Q){
int data;
if(IsEmptyQueue(Q)){
printf("Queue is empty");
return 0;
}
else{
data=Q->array[front];
if(Q->front==Q->rear)
Q->front=Q->rear=-1; //only one element in queue
else
Q->front=(Q->front +1) % Q->capacity;
}
return data;
}
void DeleteQueue(struct ArrayQueue *Q){
if(Q){
if(Q->array)
free(Q->array);
free(Q);
}
}
/*another version with different pointers' assignment*/
struct ArrayQueue *Queue(int size){
struct ArrayQueue *Q=malloc(sizeof(struct ArrayQueue));
if(!Q) return NULL;
Q->capacity=size;
Q->front=Q->rear=0;
/*in this version, we initialize the pointers both at 0*/
/*front: point to the first element*/
/*rear: point to the next position of last element*/
Q->array=malloc(Q->capacity * (sizeof(int));
if(!Q->arrray) return NULL;
return Q;
}
int IsEmptyQueue(struct ArraQueue *Q){
return (Q->front==Q->front);
}
int IsFullQueue(struct ArrayQueue *Q){
return ((Q->rear +1) % Q->capacity == Q->front);
}
int QueueSize(){
return (Q->capacity - Q->front + Q->rear) % Q->capacity; //no'+1' operation
}
Dynamic Circular Array Implementation
struct DynArrayQueue{
int front, rear;
int capacity;
int *array;
};
struct DynArrayQueue *CreateDynQueue(){
struct DynArrayQueue *Q=malloc(sizeof(struct DynArrayQueue));
if(!Q) return NULL;
Q->capacity=1;
Q->front=Q->rear=-1;
Q->array=malloc(Q->capacity * sizeof(int));
if(!Q->array) return NULL;
return Q;
}
int IsEmptyQueue(struct DynArrayQueue *Q){
return (Q->front==-1);
}
int IsFullQueue(struct DynArrayQueue *Q){
return ((Q->rear +1) % Q->capacity == Q->front);
int QueueSize(){
return (Q->capacity - Q->front + Q->rear + 1) % Q->capacity;
}
void EnQueue(struct DynArrayQueue *Q, int data){
if(IsFullQueue(Q))
ResizeQueue(Q);
Q->rear = (Q->rear + 1) % Q->capacity;
Q->array[Q->rear] = data;
if(Q->front==-1) //enqueue first element
Q->front = Q->rear;
}
void ResizeQueue(struct DynArrayQueue *Q){
int size=Q->capacity;
Q->capacity=Q->capacity * 2;
Q->array=realloc(Q->array, Q->capacity);
if(!Q->array){
printf("Memory Error");
return;
}
if(Q->front > Q->rear){
for(int i=0; i<Q->front; i++)
Q->array[i+size]=Q->array[i];
Q->rear = Q->rear + size;
}
int DeQueue(struct DynArrayQueue *Q){
int data;
if(IsEmptyQueue(Q)){
printf("Queue is empty");
return 0;
}
else{
data=Q->array[Q->front];
if(Q->front==Q->rear)
Q->front=Q->rear=-1;
else
Q->front=(Q->front + 1) % Q->capacity;
}
return data;
}
void DeleteQueue(struct DynArrayQueue *Q){
if(Q){
if(Q->array)
free(Q->array);
free(Q);
}
}
Linked List Implementation
struct ListNode{
int data;
struct ListNode *next;
};
struct Queue{
struct ListNode *front;
struct ListNode *rear;
};
struct Queue *CreateQueue(){
struct Queue *Q;
struct ListNode *temp;
Q=malloc(sizeof(struct Queue));
if(!Q) return NULL;
temp=malloc(sizeof(struct ListNode));
Q->front=Q->rear=NULL;
return Q;
}
int IsEmptyQueue(struct Queue *Q){
return (Q->front==NULL);
}
void EnQueue(struct Queue *Q, int data){
struct ListNode *newNode;
newNode=malloc(sizeof(struct ListNode));
if(!newNode) return NULL;
newNode->data=data;
newNode->next=NULL;
if(Q->rear)
Q->rear->next=newNode;
Q->rear=newNode;
if(Q->front==NULL)
Q->front=Q->rear;
}
int DeQueue(struct Queue *Q){
int data;
struct ListNode *temp;
if(IsEmptyQueue(Q)){
printf("Queue is empty");
return 0;
}
else{
temp=Q->front;
data=Q->front->data;
Q->front==Q->front->next;
free(temp);
}
return data;
}
void DeleteQueue(struct Queue *Q){
struct ListNode *temp;
while(Q){
temp=Q;
Q=Q->next;
free(temp);
}
free(Q);
}
4. Queues: Problems & Solutions
Pro 1.1: Reverse a Queue
Solution : auxiliary stack
void ReverseQueue(struct Queue *Q){
struct Stack *S=CreateStack();
while(!IsEmptyQueue(Q))
Push(S, DeQueue(Q));
while(!IsEmptyStack(Q))
EnQueue(Q, Pop(S));
}
Pro 1.2: Reverse first k elements of a queue
Given an integer k and a queue of integers, how to reverse the order of the first k elements of the queue, leaving the other elements in the same relative order.
Queue: [1, 2, 3, 4, 5, 6, 7, 8, 9] k=4, to [4, 3, 2, 1, 5, 6, 7, 8, 9]
Solution :
void reverseQueueK(struct Queue *q, int k){
if(q==NULL || k>QueueSize(q)) return ;
else if(k>0){
struct Stack *s=CreateStack();
for(int i=0; i<k i++)
Push(s, DeQueue(q));
while(!IsEmptyStack(s)
EnQueue(q, Pop(s));
for(int i=0; i<QueueSize(q)-k; i++)
EnQueue(q, DeQueue(q)); //wrap around rest of elements
}
}
Pro 2.1: Implement a Queue using stacks
Solution :
struct Queue{
struct Stack *s1;
struct Stack *s2;
};
void EnQueue(struct Queue *Q, int data){
Push(Q->s1,data); //push into stack1
}
int DeQueue(struct Queue *Q){
if(!IsEmptyStack(Q->s2))
return Pop(Q->s2); //pop at stack2
else{
while(!IsEmptyStack(Q->s1))
Push(Q->s2,Pop(Q->s1)); //push s1 element into s2
return Pop(Q->s2);
}
}
Pro 2.2: Implement a Stack using queues
Solution :
struct Stack{
struct Queue *q1; //store elements
struct Queue *q2; //hold temporarily during pop and top methods
};
void Push(struct Stack *s, int data){
if(IsEmptyQueue(s->q1))
EnQueue(s->q2, data); //if q1 empty, enqueue element into q2
else
EnQueue(s->q1, data); //otherwise, enqueue element into q1
}
int Pop(struct Stack *s){
if(IsEmptyQueue(s->q2)){ //if q2 empty
int size=QueueSize(s->q1);
for(int i=0; i<size-1; i++) //transfer element from q1 to q2
EnQueue(s->q2,DeQueue(s->q1));
return DeQueue(s->q1); //the last element left in the q1 is the top element on the stack
}
else{
int size=QueueSize(s->q2);
for(int i=0; i<size-1; i++)
EnQueue(s->q1, DeQueue(s->q2));
return DeQueue(s->q2);
}
}
Pro 3: Maximum Sum in Sliding Window
Given array A[] with sliding window of size w which is moving from the very left of the array to the very right. Assume that we can only see the w numbers in the window. Each time the sliding window moves rightwards by one position.
Solution : Doubly Ended Queue
- Refer to Priority Queues
Pro 4: Give a Formula in terms of rear, front and n
A queue is set up in a circular array A[0…n-1] with front and rear defined as usual. Assume that n-1 locations in the array are available for storing the elements with the other element being used to detect full/empty condition. Give a formula for the number of elements in terms of rear, front, and n.
Solution :
- Rear of the queue is somewhere clockwise from the front.
- To enqueue an element, we move rear one position (+1) clockwise and write the element in that position.
- To dequeue an element, we simply move front one position (+1) clockwise.
- Queue migrates in a clockwise direction as we enqueue and dequeue.
- Emptiness and fullness to be checked carefully.
- Analyze the possible situations, we will get:
Number of Element = rear - front + 1 , if rear == front
= rear - front + n , otherwise
Pro 5: Implement Doubly Ended Queues
Solution :
void pushBackDEQ(struct ListNode **head, int data){
struct ListNode *newNode=(struct ListNode*)malloc(sizeof(struct ListNode));
newNode->data=data;
if(*head==NULL){
*head=newNode;
(*head)->next=*head;
(*head)->prev=*head;
}
else{
newNode->prev=(*head)->prev;
newNode->next=*head;
(*head)->prev->next=newNode;
(*head)->prev=newNode;
}
}
void pushFrontDEQ(struct ListNode **head, int data){
pushBackDEQ(head, data);
*head=(*head)->prev;
}
int popBackDEQ(struct ListNode **head){
int data;
if((*head)->prev==*head){
data=(*head)->data;
free(*head);
*head=NULL;
}
else{
struct ListNode *newTail=(*head)->prev->prev;
data=(*head)->prev->data;
newTail->next=*head;
free((*head)->prev);
(*head)->prev=newTail;
}
return data;
}
int popFrontDEQ(struct ListNode **head){
int data;
*head=(*head)->next;
data=popBackDEQ(head);
return data;
}
Pro 6: Check whether each Successive Pair is Consecutive or not
Given a stack of integers, how to check whether each successive pair of numbers in the stack is consecutive or not. If the stack has an odd number of elements, the element at the top is left out of a pair.
Solution :
int checkStackPair(struct Stack *s){
struct Queue *q=CreateQueue();
int pairwiseOrder=1
while(!IsEmptyStack(s))
EnQueue(q, Pop(s);
while(!IsEmptyQueue(q))
Push(s, DeQueue(q));
while(!IsEmptyStack(s)){
int temp1=Pop(s);
EnQueue(q, temp1)
if(!IsEmptyStack(s)){
int temp2=Pop(s);
EnQueue(q, temp2);
is(abs(n-m)!=1)
pairwiseOrder=0;
}
}
while(!IsEmptyQueue(q))
Push(s, Dequeue(q));
return pairwiseOrder;
}
Pro 7: Rearrange the Elements by Interleaving
Queue: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20] to [11, 16, 12, 17, 13, 18, 14, 19, 15, 20]
Solution :
void interleavingQueue(struct Queue *q){ //only use 1 stack, need multi-times transforms to ensure the order
if(QueueSize(q) % 2 != 0) return;
struct Stack *s=CreateStack();
int half=QueueSize(q)/2; // Q <[a1 a2 b1 b2]<
for(int i=0; i<half; i++)
Push(s, DeQueue(q)); // S ><[a2 a1] Q <[b1 b2]<
while(!IsEmptyStack(s))
EnQueue(q, Pop(s)); // Q <[b1 b2 a2 a1]<
for(int i=0; i<half; i++)
EnQueue(q, DeQueue(q)); // Q <[a2 a1 b1 b2]<
for(int i=0; i<half; i++)
Push(s, DeQueue(q)); // S ><[a1 a2] Q <[b1 b2]<
while(!IsEmptyStack(s)){
EnQueue(q, Pop(s)); // while1: S ><[a2] Q <[b1 b2 a1]< -----S ><[a2] Q <[b2 a1 b1]<
EnQueue(q, DeQueue(q)); // while2: S [] Q <[b2 a1 b1 a2]< ----S [] Q <[a1 b1 a2 b2]<
}
}