When an element is inserted in a queue, REAR = REAR + 1
. When an element is deleted from the queue, FRONT = FRONT + 1
when queues are implemented using arrays.
Now, initially, both FRONT = REAR = -1
indicating the queue is empty. When the first element is added, FRONT = REAR = 0
(assuming array from 0 to n-1).
Now, if we assume a condition where FRONT = 0 and REAR = n-1
implying the queue is full. When a few elements are removed the FRONT pointer changes. Let us say FRONT = 5 and REAR = 10
. Hence, array locations 0 to 4 are free.
When I wish to add an element now, I add at the location 0 and FRONT
points to it. But the locations 1, 2, 3 and 4 are free.
But, when the next time I try to insert an element, the code for the queueing arithmetic will throw an error saying the queue is full. Since FRONT = 0 and REAR = n-1
. How do I insert at the remaining free locations and also understand this queuing arithmetic better?
I would also like to understand how FRONT = REAR + 1
acts as a condition for checking if the queue is full?
You want to think circularly here in terms of relative, circular ranges instead of absolute, linear ones. So you don't want to get too hung up on the absolute indices/addresses of FRONT
and REAR
. They're relative to each other, and you can use modulo arithmetic to start getting back to the beginning of the array like Pac-Man when he goes off the side of the screen. It can be useful when you're drawing these things out to literally draw your array as a circle on your whiteboard.
When I wish to add an element now, I add at the location 0 and FRONT points to it. But the locations 1, 2, 3 and 4 are free.
I think in your statement above you got it backwards a bit. Insertions in a queue advance REAR
, not FRONT
. In such a case, REAR
would be at 0, and FRONT
would still be at 5. If you push again, REAR
would be at 1 and you'd overwrite the first index, and FRONT
would still be 5.
If N=3
and FRONT=2
and REAR=2
, we have one element in the queue after pushing and popping a lot. When you push (enqueue), we set: REAR=(REAR+1)%N
making FRONT=2
, REAR=0
giving us two elements. If we push again, FRONT=2
, REAR=1
giving us 3 elements, and the queue is full.
Visually:
R
[..x]
F
R
[x.x]
F
R
[xxx]
F
... and now we're full. A queue is full if the next circular index from the REAR
is the FRONT
. In the case where FRONT=2
, REAR=1
, we can see that (REAR+1)%N
is equal to FRONT
, so it's full.
If we popped (dequeued) at this point at this point, we would set FRONT=(FRONT+1)%N
and it would look like this:
R
[xx.]
F
I would also like to understand how FRONT = REAR + 1 acts as a condition for checking if the queue is full?
This doesn't suffice when you use this kind of circular indexing. We need a slight augmentation: the queue is full when FRONT
is equal to (REAR+1)%N
. We need that modulo arithmetic to handle those "wrap around to the other side" cases.