The puzzle is to implement basic stack operations with using only basic queue operations. That is, a queue object is given, we need construct a wrapper for the stack functions, push, pop, which will only use the queue object as its storage, and naturally will have to use the queue operations.
This can be done using only one queue object. Although either the push or the pop complexity will no more be O(1).
The idea is pretty simple. We start with an empty queue. For the push operation we simply insert the value to be pushed into the queue. The pop operation needs some manipulation. When we need to pop from the stack (simulated with a queue), first we get the number of elements in the queue, say n, and remove (n-1) elements from the queue and keep on inserting in the queue one by one. That is, we remove the front element from the queue, and immediately insert into the queue in the rear, then we remove the front element from the queue and then immediately insert into the rear, thus we continue upto (n-1)elements. Then we will perform a remove operation, which will actually remove the nthelement of the original state of the queue, and return. Note that the nth element in the queue is the one which was inserted last, and we are returning it first, therefore it works like a pop operation (Last in First Out). The idea is shown below.
1
2
3
4
|
void push (queue_t q)
{
queue.insert (element);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
element_type pop (void)
{
n = queue.element_count ();
for (i from 0 to n - 2) // Only (n-1) elements 0 to (n-2)
{
queue_element = queue.remove ();
queue.insert (queue_element);
}
element = queue.remove ();
return element;
}
|
Note that in the pop process, because we are inserting the first (n-1) elements in the queue in the same order we are removing, after the for loop termination in pop the state of the queue would have the last element in the queue at the font, and the remaining elements will maintain the same order which they had before the execution of the forloop. At this point we are simply removing the queue’s first element, which happens to beactually the last element in the original state of the queue, is removed and sent. After this the queue has the same elements in the same order except the last element. Which is exactly the behaviour we wanted.
Here is a simple diagram. Assume that the queue representation in the diagram has infinite length, and implemented as a linear queue (not circular) for simplicity.
/** PUSH operation **/
front,rear
|
v
+----+----+----+----+----+----+
| 1 | | | | | | push (1) : insert (1)
+----+----+----+----+----+----+
front rear
| |
v V
+----+----+----+----+----+----+
| 1 | 2 | | | | | push (2) : insert (2)
+----+----+----+----+----+----+
front rear
| |
v V
+----+----+----+----+----+----+
| 1 | 2 | 3 | 4 | | | push (3), push (4) : insert (3), insert (4)
+----+----+----+----+----+----+
|
/** POP operation **/
initial:
front rear
| |
v V
+----+----+----+----+----+----+
| 1 | 2 | 3 | 4 | | |
+----+----+----+----+----+----+
popping
4 elements present. We will remove first 3 elements and immediately
insert them as follows.
front rear
| |
v V
+----+----+----+----+----+----+----+----+
| | 2 | 3 | 4 | 1 | | | | queue.insert (queue.remove ());
+----+----+----+----+----+----+----+----+ //first element
front rear
| |
v V
+----+----+----+----+----+----+----+----+
| | | 3 | 4 | 1 | 2 | | | queue.insert (queue.remove ());
+----+----+----+----+----+----+----+----+ //second element
front rear
| |
v V
+----+----+----+----+----+----+----+----+
| | | | 4 | 1 | 2 | 3 | | queue.insert (queue.remove ());
+----+----+----+----+----+----+----+----+ //third element
front rear
| |
v V
+----+----+----+----+----+----+----+----+
| | | | | 1 | 2 | 3 | | return (queue.remove ());
+----+----+----+----+----+----+----+----+ //return required element 4
At this point we have popped the last inserted element, and the
queue holds the elements in correct order except the element 4 removed.
|
Now we go into the implementation, which is pretty simple.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
#include <stdio.h>
#include <stdlib.h>
/* Queue structure */
#define QUEUE_EMPTY_MAGIC 0xdeadbeef
typedef
struct
_queue_t
{
int
*arr;
int
rear, front, count, max;
} queue_t;
/* Queue operation function prototypes */
queue_t *queue_allocate (
int
n);
void
queue_insert (queue_t * q,
int
v);
int
queue_remove (queue_t * q);
int
queue_count (queue_t * q);
int
queue_is_empty (queue_t * q);
/* NOTE: Here is the stuff we are interested in */
/* Simulated stack operations START */
/* NOTE: passing the queue object, on which we will only operate the
* queue operations.
*/
void
stack_push (queue_t * q,
int
v)
{
queue_insert (q, v);
}
int
stack_pop (queue_t * q)
{
int
i, n = queue_count (q);
int
removed_element;
for
(i = 0; i < (n - 1); i++)
{
removed_element = queue_remove (q);
queue_insert (q, removed_element);
/* same as below */
//queue_insert (q, queue_remove (q))
}
removed_element = queue_remove (q);
return
removed_element;
}
int
stack_is_empty (queue_t * q)
{
return
queue_is_empty (q);
}
int
stack_count (queue_t * q)
{
return
queue_count (q);
}
/* Simulated stack operations END */
/* Queue operations START */
int
queue_count (queue_t * q)
{
return
q->count;
}
queue_t *
queue_allocate (
int
n)
{
queue_t *queue;
queue =
malloc
(
sizeof
(queue_t));
if
(queue == NULL)
return
NULL;
queue->max = n;
queue->arr =
malloc
(
sizeof
(
int
) * n);
queue->rear = n - 1;
queue->front = n - 1;
return
queue;
}
void
queue_insert (queue_t * q,
int
v)
{
if
(q->count == q->max)
return
;
q->rear = (q->rear + 1) % q->max;
q->arr[q->rear] = v;
q->count++;
}
int
queue_remove (queue_t * q)
{
int
retval;
/* magic number if queue is empty */
if
(q->count == 0)
return
QUEUE_EMPTY_MAGIC;
q->front = (q->front + 1) % q->max;
retval = q->arr[q->front];
q->count--;
return
retval;
}
int
queue_is_empty (queue_t * q)
{
return
(q->count == 0);
}
/* Queue operations END */
/* For demo */
void
queue_display (queue_t * q)
{
int
i = (q->front + 1) % q->max, elements = queue_count (q);
while
(elements--)
{
printf
(
"[%d], "
, q->arr[i]);
i = (i >= q->max) ? 0 : (i + 1);
}
}
#define MAX 128
int
main (
void
)
{
queue_t *q;
int
x, select;
/* Static allocation */
q = queue_allocate (MAX);
do
{
printf
(
"\n[1] Push\n[2] Pop\n[0] Exit"
);
printf
(
"\nChoice: "
);
scanf
(
" %d"
, &select);
switch
(select)
{
case
1:
printf
(
"\nEnter value to Push:"
);
scanf
(
" %d"
, &x);
/* Pushing */
stack_push (q, x);
printf
(
"\n\n__________________________\nCurrent Queue:\n"
);
queue_display (q);
printf
(
"\n\nPushed Value: %d"
, x);
printf
(
"\n__________________________\n"
);
break
;
case
2:
/* Popping */
x = stack_pop (q);
printf
(
"\n\n\n\n__________________________\nCurrent Queue:\n"
);
queue_display (q);
if
(x == QUEUE_EMPTY_MAGIC)
printf
(
"\n\nNo values removed"
);
else
printf
(
"\n\nPopped Value: %d"
, x);
printf
(
"\n__________________________\n"
);
break
;
case
0:
printf
(
"\nQutting.\n"
);
return
0;
default
:
printf
(
"\nQutting.\n"
);
return
0;
}
}
while
(1);
return
0;
}
|
This code is basically divided in three parts. One is a hand made implementation of a circular queue of integers. Next the stack operations using the queue, and a demo driver which will demonstrate the code interactively. In the code, the stack simulating functions are highlighted. The stack empty and stack element count functions are implemented here with the queue functions. Although it looks a bit messy but i couldn’t stop myself writing the C code and post, although a CPP implementation would have been cleaner. Note that the implementation considers that the queue (stack) size is static defined by the MAXmacro, and also the queue will only contain integers. The code is straight forward therefore i am not going to describe it. Although if there is any problem feel free to drop a comment.
Here is a stack wrapper class in CPP using the STL queue class. Thanks to Subhendu Berafor writing it for me.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
#include <iostream>
#include <queue>
using
namespace
std;
template
<
class
T>
class
mystack
{
private
:
queue <T> Q;
public
:
void
ms_push (T data);
T ms_pop ();
};
template
<
class
T>
void
mystack <T>::ms_push (T data)
{
Q.push (data);
}
template
<
class
T> T mystack <T>::ms_pop ()
{
T n, temp;
n = Q.size ();
for
(
int
i = 0; i < (n - 1); i++)
{
temp = Q.front ();
Q.pop ();
Q.push (temp);
/* Same as below */
//Q.push (Q.pop ());
}
temp = Q.front ();
Q.pop ();
return
temp;
}
|
The other way is just the opposite. When pushing the element, instead of directly inserting it in the queue we can insert a new element in the queue and then if there are n elements in the queue, then we remove and insert (n-1). In this case while popping we simply return the first element. Here is what i am trying to say:
1
2
3
4
|
int stack_pop (queue_data *q)
{
return queue_remove (q);
}
|
1
2
3
4
5
6
7
8
9
10
|
void stack_push (queue_data *q, int val)
{
int old_count = queue_get_element_count (q), i;
queue_insert (q, val);
for (i=0; i<old_count; i++)
{
queue_insert (q, queue_remove (q));
}
}
|
Same stuff is in here in my answer:http://stackoverflow.com/questions/688276/implement-stack-using-two-queues
Not posting the code for this one.
Clearly, if we do the dequeue, enqueue operation in the pop function, then it will have a execution growth upperbound of O(n) where n is the length of the queue, and the pushoperation will have O(1) time bound. The reverse is valid for the “Other Method”.
What’s the use? I don’t know, it’s just a puzzle that i came by.
What about the reverse, that is implementing a queue with a stack? I will post it soon as i get some time (The ASCII art and making sure things work is *pain*).
Check out the new post which does the opposite: Implement queue using stack