Brief Discussion on Stacks and Queues
Queues and stacks are two of the most fundamental data structures. Queues follow the First-in-first-out (FIFO) policy, and stacks follow the Last-in-first-out (LIFO) policy. Although they may look very different, they can be interconnected in some ways. In this blog, we will discuss how we can implement one structure with the other one by reviewing two corresponding implementations for the leetcode problem 232 and 225.
Implementing Queues
232. Implement Queue using Stacks
One common and natural way to implement queues is by using two stacks. One is for incoming elements, and the other is for releasing elements. Thanks to programmer Carl, the following gif explains the mechanism crystal clear.
Define Stack
Given its necessity, we define a stack first.
1 | typedef struct { |
The standard operations for the stack are push, pop, and peek. The above implementation would be considered a standard stack structure in C
.
Build Queues
With stack defined, we can now implement our Queues. As we mentioned before, we need one stack to receive elements in the queue and another to release elements from the queue.
1 | typedef struct { |
Stack Communication within Queue
The core of this implementation is how the two stacks cooperate and communicate. The following implementation is one version that works. You can get an idea of how stacks deal with the inner traffic.
1 | MyQueue* myQueueCreate() { |
Implementing Stacks
Practical ways to implement stacks are using Queues or Linked Lists. If you use queues, you will need to implement a queue with standard operations, including push
, pop
and top
. The inner traffic of the queue implementation can be described as following
As we can see, queue2
is only a temporary container for queue1
, making it a little awkward to implement a stack with queues. Don’t get me wrong. This approach works fine for sure. But a more natural and easier way to implement it is using the Linked Lists.
Define Linked Lists
The first step is to implement a linked list. The next
node and its self
value are two essential elements of the linked list. Therefore,
1 | typedef struct stackListNode { |
Linked List Traffic
Three standard operations of a Stack are push
, pop
and top
. Based on the starter code from leetcode, we can complete the code.
1 | typedef struct { |
Leak or Not?
As you may have probably noticed from my comment above, there is a memory leakage problem with that implementation. If we move obj->top
pointer to its next node, you will have no problem passing all tests, even with better spatial utilizations, but you will leave the previous obj->top
unfreed. For a language like Python, the kernel will recycle the unfreed objects. However, you will need to free those used objects manually for lower-level languages like C.
A myStackPop
implementation with proper memory handling should look similar to this,
1 | int myStackPop(MyStack* obj) { |
In this way, the old node will be recycled appropriately.
References
Brief Discussion on Stacks and Queues
https://realzza.github.io/leetcode/Brief-Discussion-on-Stacks-and-Queues/