Before moving with this topic, you must check this first ,
Step-by-Step simulation of enqueue and dequeue for the linked-list queue Used symbolic addresses (NodeA@0x100) so you can follow memory and pointers precisely.
Notation
-
frontandreararestruct Node *instruct Queue. -
A node:
[ data | next ] -
NULLrepresents a null pointer. -
newNode = malloc(...)creates a node (shown with a symbolic address).
Initial state — empty queue
front -> NULL
rear -> NULL
ENQUEUE (value = 10) — inserting into an empty queue
Pseudo-code steps (typical):
-
newNode = (struct Node*)malloc(sizeof(struct Node)); -
newNode->data = 10; -
newNode->next = NULL; -
if
rear == NULL(queue empty) then:-
front = rear = newNode;
else -
rear->next = newNode; rear = newNode;
-
Execution with symbolic addresses:
-
Allocate:
newNode = NodeA@0x100-
NodeA:
[ 10 | NULL ]
-
-
Since
rear == NULL(empty), do:front = NodeA@0x100 rear = NodeA@0x100
Resulting layout
front/rear
↓
[ 10 | NULL ] (NodeA@0x100)
Notes: front == rear because only one node exists.
ENQUEUE (value = 20) — inserting into non-empty queue
Before operation
front -> NodeA@0x100 [10 | NULL]
rear -> NodeA@0x100 [10 | NULL]
Steps
-
newNode = NodeB@0x110→[20 | NULL] -
rear->next = newNode;-
NodeA.next = NodeB@0x110
-
-
rear = newNode;
After
front -> NodeA@0x100 [10 | next -> 0x110]
↓
NodeB@0x110 [20 | NULL]
↑
rear
Pointer updates
-
NodeA.next changed from
NULL→0x110 -
rearupdated from0x100→0x110
ENQUEUE (value = 30) — repeat
-
newNode = NodeC@0x120→[30 | NULL] -
rear->next = newNode;(NodeB.next -> NodeC) -
rear = newNode;
Queue
front -> NodeA@0x100 [10] -> NodeB@0x110 [20] -> NodeC@0x120 [30 | NULL]
↑
rear
DEQUEUE — remove from front (normal case: >1 node)
Before
front -> NodeA@0x100 [10] -> NodeB@0x110 [20] -> NodeC@0x120 [30]
rear -> NodeC@0x120
Typical pseudo-code:
-
if
front == NULL→ queue empty, return / underflow. -
temp = front; -
value = temp->data; -
front = front->next; -
if
front == NULLthenrear = NULL;// queue became empty -
free(temp);(optional to show deallocation)
First dequeue (removes 10):
-
temp = NodeA@0x100 -
value = 10 -
front = NodeA->next→front = NodeB@0x110 -
front != NULL, so do not changerear -
free(NodeA)
After
front -> NodeB@0x110 [20] -> NodeC@0x120 [30]
rear -> NodeC@0x120
(Dequeued value = 10)
DEQUEUE repeatedly until single node removed
Second dequeue (removes 20):
-
temp = NodeB@0x110 -
front = NodeB->next→NodeC@0x120 -
rearunchanged (NodeC@0x120) -
free NodeB
Queue after
front -> NodeC@0x120 [30 | NULL]
rear -> NodeC@0x120
(Dequeued value = 20)
Third dequeue (removes 30) — edge case: single element
-
temp = NodeC@0x120 -
front = NodeC->next→NULL -
After assigning
front = NULL, checkif (front == NULL) rear = NULL; -
So set
rear = NULL -
free NodeC
Queue after
front -> NULL
rear -> NULL
(Dequeued value = 30)
Notes: This is the important edge-case: after removing the last element, both front and rear must be set to NULL. Many bugs occur when code forgets to update rear, leaving it as a dangling pointer.
ENQUEUE after some DEQUEUEs (queue reused)
Suppose after previous operations the queue is empty (front = rear = NULL). Now enqueue(40):
-
Allocate NodeD@0x140
[40 | NULL] -
Because
rear == NULL, setfront = rear = NodeD@0x140.
Works identically to first enqueue.
DEQUEUE on EMPTY queue (underflow)
Before
front = NULL
rear = NULL
-
If code calls
dequeue(), checkif (front == NULL)→ return underflow / print error. -
Do not dereference
frontorrear(would crash).
Step-by-step pointer table (example timeline)
| Step | Operation | front pointer | rear pointer | Comment |
|---|---|---|---|---|
| 0 | initial | NULL | NULL | empty |
| 1 | enqueue(10) | NodeA@0x100 | NodeA@0x100 | single node |
| 2 | enqueue(20) | NodeA@0x100 | NodeB@0x110 | NodeA.next -> NodeB |
| 3 | enqueue(30) | NodeA@0x100 | NodeC@0x120 | NodeB.next -> NodeC |
| 4 | dequeue() | NodeB@0x110 | NodeC@0x120 | removed NodeA |
| 5 | dequeue() | NodeC@0x120 | NodeC@0x120 | removed NodeB |
| 6 | dequeue() | NULL | NULL | removed NodeC; queue empty |
| 7 | dequeue() | NULL | NULL | underflow — nothing to remove |
Implementation details to avoid mistakes (practical tips)
-
Always set
newNode->next = NULLwhen enqueueing — rear must always end withNULL. -
Check
rear == NULLto decide whether queue is empty — don't checkfrontonly (consistent checks are fine). -
After dequeue when
frontbecomes NULL, setrear = NULL. Forgetting this leavesreardangling. -
Free removed nodes (if using
malloc) to avoid memory leak. -
Do not use
rear->nextwithout verifyingrear != NULL. (safe in enqueue because you check for empty first). -
Thread-safety: for concurrent use, protect
front/rearwith locks; simultaneous enqueue+dequeue otherwise unsafe.
Time & Space complexity (quick recap)
-
enqueue— O(1) time (no traversal), O(1) auxiliary space per operation (for new node).
When an algorithm has O(1) (constant time) complexity, it means:
The time required to perform the operation does NOT depend on the number of elements in the queue.
Whether the queue has
-
1 element
-
10 elements
-
1,000 elements
-
1,000,000 elements
…enqueue takes the same amount of time.
-
dequeue— O(1) time, O(1) space. -
Total space — O(n) nodes in heap for
nqueued elements.
.png)
Comments
Post a Comment