-
Notifications
You must be signed in to change notification settings - Fork 293
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1553 from yashksaini-coder/yash/fix-1498
✅ Two-Stack Enqueue and Dequeue (Lazy Transfer)
- Loading branch information
Showing
2 changed files
with
202 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# Two-Stack Queue Implementation | ||
|
||
This project implements a queue data structure using two stacks with a lazy transfer approach. The implementation provides O(1) amortized time complexity for both enqueue and dequeue operations. | ||
|
||
## Algorithm Overview | ||
|
||
The implementation uses two stacks: | ||
- `inStack`: Used for enqueueing elements | ||
- `outStack`: Used for dequeueing elements | ||
|
||
### Key Operations | ||
|
||
1. **Enqueue**: Push elements directly onto `inStack` - O(1) | ||
2. **Dequeue**: | ||
- If `outStack` is empty, transfer all elements from `inStack` to `outStack` | ||
- Pop and return the top element from `outStack` | ||
- Amortized O(1) | ||
|
||
### Complexity | ||
|
||
- **Time Complexity**: | ||
- **Enqueue**: $O(1)$ | ||
- **Dequeue**: Amortized $O(1)$ | ||
- **Space Complexity**: $O(n)$, where $n$ is the number of elements in the queue. | ||
|
||
### Example | ||
|
||
Consider a queue with the following operations: | ||
|
||
1. **Enqueue 1** | ||
2. **Enqueue 2** | ||
3. **Dequeue** | ||
4. **Enqueue 3** | ||
5. **Dequeue** | ||
|
||
**Operations Breakdown**: | ||
|
||
- **Enqueue 1**: | ||
- `inStack`: [1] | ||
- `outStack`: [] | ||
|
||
- **Enqueue 2**: | ||
- `inStack`: [1, 2] | ||
- `outStack`: [] | ||
|
||
- **Dequeue**: | ||
- Transfer elements from `inStack` to `outStack`: | ||
- `inStack`: [] | ||
- `outStack`: [2, 1] | ||
- Pop from `outStack`: 1 | ||
- Result: | ||
- `inStack`: [] | ||
- `outStack`: [2] | ||
|
||
- **Enqueue 3**: | ||
- `inStack`: [3] | ||
- `outStack`: [2] | ||
|
||
- **Dequeue**: | ||
- Pop from `outStack`: 2 | ||
- Result: | ||
- `inStack`: [3] | ||
- `outStack`: [] | ||
|
||
### Conclusion | ||
|
||
Implementing a queue using two stacks is an efficient way to achieve the FIFO behavior with LIFO structures. This approach ensures that both the **enqueue** and **dequeue** operations have an amortized constant time complexity, making it suitable for applications where performance and resource management are critical. | ||
|
||
## Flowchart | ||
|
||
```mermaid | ||
flowchart TD | ||
A[Start] --> B{Operation Type} | ||
B -->|Enqueue| C[Push to inStack] | ||
C --> D[Done] | ||
B -->|Dequeue| E{outStack Empty?} | ||
E -->|Yes| F[Transfer all elements from inStack to outStack] | ||
E -->|No| G[Pop from outStack] | ||
F --> G | ||
G --> D | ||
subgraph Transfer Process | ||
H[Pop from inStack] --> I[Push to outStack] | ||
I --> J{inStack Empty?} | ||
J -->|No| H | ||
J -->|Yes| K[Transfer Complete] | ||
end | ||
D --> K | ||
K --> L[End] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <stdbool.h> | ||
|
||
#define MAX_SIZE 100 | ||
|
||
typedef struct { | ||
int items[MAX_SIZE]; | ||
int top; | ||
} Stack; | ||
|
||
typedef struct { | ||
Stack* inStack; | ||
Stack* outStack; | ||
} Queue; | ||
|
||
// Stack operations | ||
void initStack(Stack* s) { | ||
s->top = -1; | ||
} | ||
|
||
bool isEmpty(Stack* s) { | ||
return s->top == -1; | ||
} | ||
|
||
bool isFull(Stack* s) { | ||
return s->top == MAX_SIZE - 1; | ||
} | ||
|
||
void push(Stack* s, int value) { | ||
if (!isFull(s)) { | ||
s->items[++s->top] = value; | ||
} | ||
} | ||
|
||
int pop(Stack* s) { | ||
if (!isEmpty(s)) { | ||
return s->items[s->top--]; | ||
} | ||
return -1; // Error value | ||
} | ||
|
||
Queue* createQueue() { | ||
Queue* q = (Queue*)malloc(sizeof(Queue)); | ||
q->inStack = (Stack*)malloc(sizeof(Stack)); | ||
q->outStack = (Stack*)malloc(sizeof(Stack)); | ||
initStack(q->inStack); | ||
initStack(q->outStack); | ||
return q; | ||
} | ||
|
||
void enqueue(Queue* q, int value) { | ||
push(q->inStack, value); | ||
} | ||
|
||
int dequeue(Queue* q) { | ||
if (isEmpty(q->outStack)) { | ||
// Transfer elements from inStack to outStack | ||
while (!isEmpty(q->inStack)) { | ||
push(q->outStack, pop(q->inStack)); | ||
} | ||
} | ||
return pop(q->outStack); | ||
} | ||
|
||
bool isQueueEmpty(Queue* q) { | ||
return isEmpty(q->inStack) && isEmpty(q->outStack); | ||
} | ||
|
||
void destroyQueue(Queue* q) { | ||
free(q->inStack); | ||
free(q->outStack); | ||
free(q); | ||
} | ||
|
||
|
||
int main() { | ||
Queue* q = createQueue(); | ||
|
||
enqueue(q, 1); | ||
enqueue(q, 2); | ||
enqueue(q, 3); | ||
|
||
printf("%d\n", dequeue(q)); // Output: 1 | ||
printf("%d\n", dequeue(q)); // Output: 2 | ||
|
||
enqueue(q, 4); | ||
|
||
printf("%d\n", dequeue(q)); // Output: 3 | ||
printf("%d\n", dequeue(q)); // Output: 4 | ||
|
||
destroyQueue(q); | ||
// Additional test cases | ||
enqueue(q, 5); | ||
enqueue(q, 6); | ||
|
||
printf("%d\n", dequeue(q)); // Output: 5 | ||
printf("%d\n", dequeue(q)); // Output: 6 | ||
|
||
// Check if queue is empty | ||
if (isQueueEmpty(q)) { | ||
printf("Queue is empty\n"); | ||
} else { | ||
printf("Queue is not empty\n"); | ||
} | ||
|
||
destroyQueue(q); | ||
return 0; | ||
} |