From 6146d194475b9cdc135185f91514a0ef5ceb10e1 Mon Sep 17 00:00:00 2001 From: Yash Kumar Saini <115717039+yashksaini-coder@users.noreply.github.com> Date: Fri, 1 Nov 2024 11:52:41 +0000 Subject: [PATCH] Two-Stack Enqueue and Dequeue (Lazy Transfer) --- .../Queue Lazy Transfer using Stack/README.md | 93 +++++++++++++++ .../Queue Lazy Transfer using Stack/program.c | 109 ++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 Queue/Queue Lazy Transfer using Stack/README.md create mode 100644 Queue/Queue Lazy Transfer using Stack/program.c diff --git a/Queue/Queue Lazy Transfer using Stack/README.md b/Queue/Queue Lazy Transfer using Stack/README.md new file mode 100644 index 00000000..815ef04a --- /dev/null +++ b/Queue/Queue Lazy Transfer using Stack/README.md @@ -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] +``` \ No newline at end of file diff --git a/Queue/Queue Lazy Transfer using Stack/program.c b/Queue/Queue Lazy Transfer using Stack/program.c new file mode 100644 index 00000000..51b19cd2 --- /dev/null +++ b/Queue/Queue Lazy Transfer using Stack/program.c @@ -0,0 +1,109 @@ +#include +#include +#include + +#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; +} \ No newline at end of file