class CircularDynamicArray: array: Array[Any] # storage array capacity: int # current capacity, doubled if full size: int # current number of elements in array start: int # physical index of "start"
# Return physical index based on "logical" index, O(1)def physical_index(self, i): # return "circular" index return (self.start + i) % self.capacity# Resizing(doubling) is O(n)# but amortized O(1), as shown many times in class# and in exercise 1a with multiplier 1.1def resize(self): # double the capacity, allocate new array new_capacity = self.capacity * 2 new_array = Array[new_capacity] # copy all existing elements for i in range(self.size): new_array[i] = self.array[self.physical_index(i)] # update internal state self.array = new_array self.capacity = new_capacity self.start = 0
def is_empty(self): return self.size == 0# O(1) + possible resizing, amortized O(1)def push_back(self, value): if self.size == self.capacity: self.resize() # regular push to the end self.array[self.physical_index(self.size)] = value self.size += 1# O(1) + possible resizing, amortized O(1)def push_front(self, value): if self.size == self.capacity: self.resize() # push to the beginning, adjust start self.start = (self.start - 1 + self.capacity) % self.capacity self.array[self.start] = value self.size += 1# Pop from the "logical" end, O(1)def pop_back(self): if self.is_empty(): error "Empty" self.size -= 1 return self.array[self.physical_index(self.size)]# Pop from the "logical" start, O(1)def pop_front(self): if self.is_empty(): error "Empty" value = self.array[self.start] # move logical start self.start = (self.start + 1) % self.capacity self.size -= 1 return value