Problem 3.1: Describe how you could use a single array to implement three stacks.
Splitting the array into three individual parts is quite intuitive.
How can we take full advantage of the unused space? I was inspired by the implementation of circular queue. Actually, in my perspective, my solution is better than the one on answer page. The idea is not complicated: When there is no free space next to top of the stack, into which we want to push a value, I shift other stacks to make a free space available for the stack.
It takes me more than two hours to implement the class. Thanks to work break down method, I encapsulated some work into utility functions and made the implementation quite smooth.
Here are the test cases:
Splitting the array into three individual parts is quite intuitive.
How can we take full advantage of the unused space? I was inspired by the implementation of circular queue. Actually, in my perspective, my solution is better than the one on answer page. The idea is not complicated: When there is no free space next to top of the stack, into which we want to push a value, I shift other stacks to make a free space available for the stack.
It takes me more than two hours to implement the class. Thanks to work break down method, I encapsulated some work into utility functions and made the implementation quite smooth.
class n_stacks:
def __init__(self, stacks_num, array_size):
if array_size < stacks_num:
print "array_size is too small"
return
self.stacks_num = stacks_num
self.array = [None for i in range(0, array_size)]
size_per_stack = int(array_size/stacks_num)
self.tops = [size_per_stack*i for i in range(0, self.stacks_num)]
self.bottoms = self.tops[:]
def push(self, stack_no, value):
next_stack_no = next_in_circle(stack_no, self.stacks_num)
# If there is no room between given stack and the next stack
if self.tops[stack_no] == self.bottoms[next_stack_no]:
# If failed to make room
if not self.make_room_for(stack_no):
return False
# If we can come here, there is some room available
self.array[self.tops[stack_no]] = value
self.tops[stack_no] = next_in_circle(self.tops[stack_no], len(self.array))
return True
def pop(self, stack_no):
# If there is no element in this stack
if self.tops[stack_no] == self.bottoms[stack_no]:
return None
self.tops[stack_no] = pre_in_circle(self.tops[stack_no], len(self.array))
return self.array[self.tops[stack_no]]
def length(self, stack_no):
if self.bottoms[stack_no] <= self.tops[stack_no]:
return (self.tops[stack_no] - self.bottoms[stack_no])
else:
return (len(self.array) - (self.bottoms[stack_no] - self.tops[stack_no]))
def make_room_for(self, stack_no):
# Iterate all stacks after the given one
# and try to find a free element between two of them
current_no = next_in_circle(stack_no, self.stacks_num)
while current_no != stack_no:
next_no = next_in_circle(current_no, self.stacks_num)
# If there is a free element between current stack and next one
if self.tops[current_no] != self.bottoms[next_no]:
# A stack occupies at least one element (even when it is empty)
# Whether there is a free element based on the additional condition above?
current_top_next = next_in_circle(self.tops[current_no], len(self.array))
if (current_top_next == self.bottoms[next_no]) and (self.tops[current_no] == self.bottoms[current_no]):
# If there is no free element, continue
pass
else:
# If there is a free element, break and utilize the free element
break
current_no = next_no
# If we did not find a free element
if current_no == stack_no:
return False
# If we find one, shift stacks to make the
# free element available for given stack
while current_no != stack_no:
self.shift_stack_forward(current_no)
current_no = pre_in_circle(current_no, self.stacks_num)
return True
def shift_stack_forward(self, stack_no):
# Shift all elements in the given stack
n = self.tops[stack_no]
while n != self.bottoms[stack_no]:
n_pre = pre_in_circle(n, len(self.array))
self.array[n] = self.array[n_pre]
n = n_pre
# Shift the top and bottom
self.tops[stack_no] = next_in_circle(self.tops[stack_no], len(self.array))
self.bottoms[stack_no] = next_in_circle(self.bottoms[stack_no], len(self.array))
# Utility function to get next index in a circle
def next_in_circle(current, total):
return (current+1)%total
# Utility function to get previous index in a circle
def pre_in_circle(current, total):
result = current - 1
if result < 0:
return (total + result)
else:
return result
Here are the test cases:
from n_stacks import *
if __name__ == "__main__":
triple_stacks = n_stacks(3, 9)
print "stack 0: length", triple_stacks.length(0)
print "stack 1: length", triple_stacks.length(1)
print "stack 2: length", triple_stacks.length(2)
print "stack 0: push 0", triple_stacks.push(0, 0)
print "stack 0: push 1", triple_stacks.push(0, 1)
print "stack 0: push 2", triple_stacks.push(0, 2)
print "stack 1: push 10", triple_stacks.push(1, 10)
print "stack 1: push 11", triple_stacks.push(1, 11)
print "stack 1: push 12", triple_stacks.push(1, 12)
print "stack 2: push 20", triple_stacks.push(2, 20)
print "stack 2: push 21", triple_stacks.push(2, 21)
print "stack 2: push 22", triple_stacks.push(2, 22)
print "stack 2: push 23", triple_stacks.push(2, 23)
print "stack 0: pop", triple_stacks.pop(0)
print "stack 2: push 23", triple_stacks.push(2, 23)
print "stack 2: push 24", triple_stacks.push(2, 24)
print "stack 1: pop", triple_stacks.pop(1)
print "stack 2: push 24", triple_stacks.push(2, 24)
print "stack 2: pop", triple_stacks.pop(2)
print "stack 2: pop", triple_stacks.pop(2)
print "stack 2: pop", triple_stacks.pop(2)
print "stack 2: pop", triple_stacks.pop(2)
print "stack 2: pop", triple_stacks.pop(2)
print "stack 2: pop", triple_stacks.pop(2)
print "stack 0: push 2", triple_stacks.push(0, 2)
print "stack 0: push 3", triple_stacks.push(0, 3)
print "stack 0: push 4", triple_stacks.push(0, 4)
print "stack 0: push 5", triple_stacks.push(0, 5)
print "stack 0: push 6", triple_stacks.push(0, 6)
print "stack 2: push 20", triple_stacks.push(2, 20)
print "stack 2: push 21", triple_stacks.push(2, 21)
print "stack 0: length", triple_stacks.length(0)
print "stack 1: length", triple_stacks.length(1)
print "stack 2: length", triple_stacks.length(2)
print "stack 0: pop", triple_stacks.pop(0)
print "stack 0: pop", triple_stacks.pop(0)
print "stack 0: pop", triple_stacks.pop(0)
print "stack 0: pop", triple_stacks.pop(0)
print "stack 0: pop", triple_stacks.pop(0)
print "stack 0: pop", triple_stacks.pop(0)
print "stack 0: pop", triple_stacks.pop(0)
print "stack 1: push 12", triple_stacks.push(1, 12)
print "stack 1: push 13", triple_stacks.push(1, 13)
print "stack 1: push 14", triple_stacks.push(1, 14)
print "stack 1: push 15", triple_stacks.push(1, 15)
print "stack 1: push 16", triple_stacks.push(1, 16)
print "stack 1: push 17", triple_stacks.push(1, 17)
print "stack 0: length", triple_stacks.length(0)
print "stack 1: length", triple_stacks.length(1)
print "stack 2: length", triple_stacks.length(2)
print "stack 1: pop", triple_stacks.pop(1)
print "stack 1: pop", triple_stacks.pop(1)
print "stack 1: pop", triple_stacks.pop(1)
print "stack 1: pop", triple_stacks.pop(1)
print "stack 1: pop", triple_stacks.pop(1)
print "stack 1: pop", triple_stacks.pop(1)
print "stack 1: pop", triple_stacks.pop(1)
print "stack 1: pop", triple_stacks.pop(1)
print "stack 0: length", triple_stacks.length(0)
print "stack 1: length", triple_stacks.length(1)
print "stack 2: length", triple_stacks.length(2)
print "stack 2: pop", triple_stacks.pop(2)
print "stack 2: pop", triple_stacks.pop(2)
print "stack 0: length", triple_stacks.length(0)
print "stack 1: length", triple_stacks.length(1)
print "stack 2: length", triple_stacks.length(2)