Dynamic Stack Using Resizable Array in DSA C - Time & Space Complexity
We want to understand how the time to add or remove items from a dynamic stack changes as the stack grows.
How does resizing the array affect the speed of stack operations?
Analyze the time complexity of the following code snippet.
#define INITIAL_CAPACITY 4
typedef struct {
int *array;
int capacity;
int top;
} Stack;
void push(Stack *s, int value) {
if (s->top == s->capacity) {
s->capacity *= 2;
s->array = realloc(s->array, s->capacity * sizeof(int));
}
s->array[s->top++] = value;
}
int pop(Stack *s) {
if (s->top == 0) return -1; // empty
return s->array[--s->top];
}
This code shows a stack that doubles its array size when full, allowing dynamic growth.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Adding or removing one item from the stack.
- How many times: Each push or pop happens once per call, but resizing copies all elements occasionally.
Most pushes take a simple step, but when the array is full, it doubles size and copies all items.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 simple pushes + 2 resizes copying 4 and 8 items |
| 100 | About 100 pushes + 5 resizes copying 4, 8, 16, 32, 64 items |
| 1000 | About 1000 pushes + 8 resizes copying increasing items each time |
Pattern observation: Most pushes are fast, but resizing happens rarely and costs more, spreading the cost over many pushes.
Time Complexity: O(1) amortized
This means each push usually takes a quick step, but sometimes a bigger step happens; averaged out, each push is still fast.
[X] Wrong: "Every push takes a long time because of resizing."
[OK] Correct: Resizing happens only occasionally, so most pushes are quick; the big cost is spread out over many pushes.
Understanding amortized time helps you explain why dynamic arrays and stacks are efficient in real programs, showing you know how data structures work under the hood.
"What if we shrank the array size when many items are popped? How would that affect the time complexity?"
Practice
Solution
Step 1: Understand fixed vs dynamic size
A fixed-size array stack has a set capacity that cannot change during runtime.Step 2: Identify dynamic array behavior
A dynamic stack with a resizable array can increase or decrease its size as elements are pushed or popped.Final Answer:
It can grow or shrink in size as needed during runtime. -> Option CQuick Check:
Dynamic resizing means flexible size [OK]
- Thinking dynamic stack has fixed size
- Confusing memory usage with resizing
- Assuming no resizing is needed
Solution
Step 1: Identify correct use of realloc
realloc is used to resize the memory block; new size should be double current capacity times element size.Step 2: Check each option
stack->array = realloc(stack->array, stack->capacity * 2 * sizeof(int)); correctly doubles capacity and multiplies by sizeof(int). Others either allocate wrong size or use malloc instead of realloc.Final Answer:
stack->array = realloc(stack->array, stack->capacity * 2 * sizeof(int)); -> Option AQuick Check:
Double capacity with realloc [OK]
- Using malloc instead of realloc for resizing
- Dividing capacity instead of doubling
- Not multiplying by sizeof element
void push(Stack *stack, int value) {
if (stack->top == stack->capacity - 1) {
stack->capacity *= 2;
stack->array = realloc(stack->array, stack->capacity * sizeof(int));
}
stack->array[++stack->top] = value;
}
Stack stack = {malloc(2 * sizeof(int)), 2, -1};
push(&stack, 10);
push(&stack, 20);
push(&stack, 30);
What is the value of stack.capacity after these operations?Solution
Step 1: Initial capacity and pushes
Initial capacity is 2. After pushing 2 elements, stack is full (top = 1).Step 2: Push triggers resize
On pushing third element, capacity doubles from 2 to 4.Final Answer:
4 -> Option AQuick Check:
Capacity doubles when full [OK]
- Assuming capacity increases by 1
- Forgetting to double capacity
- Confusing top index with capacity
int pop(Stack *stack) {
if (stack->top == -1) {
printf("Stack Underflow\n");
return -1;
}
int value = stack->array[stack->top--];
if (stack->top < stack->capacity / 4) {
stack->capacity /= 2;
stack->array = realloc(stack->array, stack->capacity * sizeof(int));
}
return value;
}Solution
Step 1: Analyze capacity reduction condition
Capacity halves when top is less than one-fourth capacity.Step 2: Identify edge case
If capacity becomes 0 or less, realloc with zero size causes segmentation fault.Final Answer:
Reducing capacity below 1 causes invalid realloc size. -> Option DQuick Check:
Capacity must not shrink below 1 [OK]
- Ignoring minimum capacity limit
- Confusing overflow with underflow
- Misusing post-decrement operator
int pop(Stack *stack) {
if (stack->top == -1) {
printf("Stack Underflow\n");
return -1;
}
int value = stack->array[stack->top--];
if (stack->top < stack->capacity / 4) {
stack->capacity /= 2;
stack->array = realloc(stack->array, stack->capacity * sizeof(int));
}
return value;
}Solution
Step 1: Understand shrinking condition
Stack shrinks when elements < 1/4 capacity but must not go below 2 capacity.Step 2: Add capacity lower bound check
Before halving capacity, check if capacity is greater than 2 to avoid shrinking below 2.Final Answer:
Add a check: if (stack->capacity > 2) before halving capacity. -> Option BQuick Check:
Prevent capacity below 2 by conditional check [OK]
- Shrinking capacity below minimum allowed
- Changing condition incorrectly
- Removing resizing causes memory waste
Ready to prove it?
Pass the test and this pattern moves unknown
