Queue Implementation Using Linked List in DSA C - Time & Space Complexity
We want to understand how the time taken by queue operations changes as the number of items grows.
How does adding or removing items affect the work done?
Analyze the time complexity of the following queue operations using a linked list.
// Node structure
struct Node {
int data;
struct Node* next;
};
// Enqueue operation
void enqueue(struct Node** front, struct Node** rear, int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = NULL;
if (*rear == NULL) {
*front = *rear = newNode;
return;
}
(*rear)->next = newNode;
*rear = newNode;
}
// Dequeue operation
int dequeue(struct Node** front, struct Node** rear) {
if (*front == NULL) return -1; // Queue empty
struct Node* temp = *front;
int val = temp->data;
*front = (*front)->next;
if (*front == NULL) *rear = NULL;
free(temp);
return val;
}
This code adds items to the end and removes from the front of a linked list to implement a queue.
Look for loops or repeated steps inside enqueue and dequeue.
- Primary operation: Adding or removing a single node pointer.
- How many times: Each enqueue or dequeue does a fixed number of steps, no loops.
Each operation works with only the front or rear pointer, so the work stays the same no matter how many items are in the queue.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 5-7 steps per enqueue or dequeue |
| 100 | 5-7 steps per enqueue or dequeue |
| 1000 | 5-7 steps per enqueue or dequeue |
Pattern observation: The number of steps does not increase with more items; it stays constant.
Time Complexity: O(1)
This means each enqueue or dequeue takes the same amount of time regardless of queue size.
[X] Wrong: "Enqueue or dequeue takes longer as the queue grows because we have to move through the list."
[OK] Correct: We keep track of front and rear pointers, so we never walk through the whole list during these operations.
Knowing that queue operations with linked lists run in constant time shows you understand efficient data structure design, a key skill in coding interviews.
"What if we did not keep a rear pointer and only had a front pointer? How would the time complexity of enqueue change?"
Practice
Solution
Step 1: Understand queue size constraints in arrays
Arrays have fixed size, so queues implemented with arrays have a maximum capacity.Step 2: Analyze linked list flexibility
Linked lists can grow or shrink dynamically, so queues using linked lists can handle any number of elements limited only by memory.Final Answer:
Dynamic size allows efficient memory use without fixed limits -> Option DQuick Check:
Linked list queues grow dynamically = A [OK]
- Thinking arrays always use less memory
- Confusing access speed with size flexibility
- Assuming linked lists have simpler code
Solution
Step 1: Check pointer usage in struct
The next pointer must be a pointer to struct Node, so 'struct Node* next;' is correct.Step 2: Validate typedef syntax
Using typedef with struct Node allows using 'Node' as type directly, which is correct in typedef struct Node { int data; struct Node* next; } Node;.Final Answer:
typedef struct Node { int data; struct Node* next; } Node; -> Option CQuick Check:
Pointer to struct Node with typedef = A [OK]
- Using 'int next;' instead of pointer
- Omitting 'struct' keyword before Node*
- Declaring next as struct Node instead of pointer
void enqueue(Node** front, Node** rear, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = NULL;
if (*rear == NULL) {
*front = *rear = newNode;
} else {
(*rear)->next = newNode;
*rear = newNode;
}
}
What will be the state of the queue after these operations?
enqueue(&front, &rear, 10); enqueue(&front, &rear, 20); enqueue(&front, &rear, 30);Solution
Step 1: Trace first enqueue call
Queue is empty, so front and rear point to new node with data 10.Step 2: Trace second and third enqueue calls
New nodes with 20 and 30 are added at rear, linked in order: 10 -> 20 -> 30 -> NULL.Final Answer:
10 -> 20 -> 30 -> NULL -> Option AQuick Check:
Enqueue adds at rear, order preserved = C [OK]
- Confusing enqueue with push (stack behavior)
- Reversing order of nodes
- Not updating rear pointer correctly
int dequeue(Node** front, Node** rear) {
if (*front == NULL) {
printf("Queue is empty\n");
return -1;
}
Node* temp = *front;
int value = temp->data;
*front = (*front)->next;
free(temp);
return value;
}Solution
Step 1: Analyze dequeue steps
Function removes front node and updates front pointer correctly.Step 2: Check rear pointer update
If after dequeue front becomes NULL (queue empty), rear should also be set to NULL but this is missing.Final Answer:
Does not update rear when queue becomes empty -> Option BQuick Check:
Rear must be NULL if queue empty = B [OK]
- Forgetting to update rear on empty queue
- Not freeing dequeued node memory
- Returning wrong data value
reverseQueue that reverses the elements of a queue implemented using a linked list. Which approach is the most efficient and correct?Solution
Step 1: Understand reversing queue with linked list
Reversing requires changing order of elements, which is not straightforward by swapping nodes in singly linked list.Step 2: Evaluate stack approach
Using a stack to push all elements then enqueue back reverses order efficiently and correctly.Step 3: Check other options
Swapping data repeatedly is complex and error-prone; copying to new list wastes memory; re-enqueueing same order does nothing.Final Answer:
Use a stack to push all elements, then enqueue them back to queue -> Option AQuick Check:
Stack reverses order efficiently = D [OK]
- Trying to swap nodes in singly linked list
- Re-enqueueing without changing order
- Copying to new list wastes memory
Ready to prove it?
Pass the test and this pattern moves unknown
