diff --git a/paddles/deque.py b/paddles/deque.py new file mode 100644 index 0000000..72c45f4 --- /dev/null +++ b/paddles/deque.py @@ -0,0 +1,121 @@ +# A doubly-linked list node is a list [item, previous, next]. +# These constants make the code more readable. +DATA = 0 +PREV = 1 +NEXT = 2 + + +class LinkedListDeque: + """Deque implementation using a doubly linked list for storage. + + >>> items = LinkedListDeque("abc") + >>> print(items) + LinkedListDeque(['a', 'b', 'c']) + >>> items.size() + 3 + >>> items.take_front() + 'a' + >>> items.take_back() + 'c' + >>> items.front() == items.back() == 'b' + True + >>> items.add_back("C") + >>> items.add_front("A") + >>> print(items) + LinkedListDeque(['A', 'b', 'C']) + """ + + def __init__(self, iterable=None): + """Initialize this deque with the given items, if any.""" + self._head = None + self._tail = None + self._length = 0 + if iterable: + for item in iterable: + self.add_back(item) + + def __str__(self): + """Return a formatted string representation of this deque.""" + strings = [] + current = self._head + while current: + strings.append(f"'{current[DATA]}'") + current = current[NEXT] + return f"LinkedListDeque([{', '.join(strings)}])" + + def size(self): + """Return the number of items in this deque.""" + return self._length + + def add_front(self, item): + """Insert the given item at the front of this deque.""" + node = [item, None, None] + if self.size() == 0: + self._head = node + self._tail = node + else: + node[NEXT] = self._head + self._head = node + self._length += 1 + + def add_back(self, item): + """Insert the given item at the back of this deque.""" + node = [item, None, None] + if self.size() == 0: + self._head = node + self._tail = node + else: + node[PREV] = self._tail + self._tail[NEXT] = node + self._tail = node + self._length += 1 + + def take_front(self): + """Remove and return the item at the front of this deque. + + Raise ValueError if this deque is empty. + """ + if self.size() == 0: + raise ValueError("can't take from an empty deque") + item = self._head[DATA] + self._head = self._head[NEXT] + self._length -= 1 + if self.size() == 0: + self._tail = None + else: + self._head[PREV] = None + return item + + def take_back(self): + """Remove and return the item at the back of this deque. + + Raise ValueError if this deque is empty. + """ + if self.size() == 0: + raise ValueError("can't take from an empty deque") + item = self._tail[DATA] + self._tail = self._tail[PREV] + self._length -= 1 + if self.size() == 0: + self._head = None + else: + self._tail[NEXT] = None + return item + + def front(self): + """Return the item at the front of this deque without removing it. + + Raise ValueError if this deque is empty. + """ + if self.size() == 0: + raise ValueError("Deque is empty") + return self._head[DATA] + + def back(self): + """Return the item at the back of this deque without removing it. + + Raise ValueError if this deque is empty. + """ + if self.size() == 0: + raise ValueError("Deque is empty") + return self._tail[DATA] diff --git a/paddles/queue.py b/paddles/queue.py new file mode 100644 index 0000000..50dd58f --- /dev/null +++ b/paddles/queue.py @@ -0,0 +1,73 @@ +# A linked list node is a list [item, next]. +# These constants make the code more readable. +DATA = 0 +NEXT = 1 + + +class LinkedListQueue: + """A queue implemented using a linked list. + + >>> q = LinkedListQueue("abc") + >>> q.size() + 3 + >>> q.dequeue() + 'a' + >>> q.front() + 'b' + >>> q.enqueue("d") + >>> print(q) + LinkedListQueue(['b', 'c', 'd']) + """ + + def __init__(self, iterable=None): + """Initialize this queue with the given items, if any.""" + self._head = None + self._tail = None + self._length = 0 + if iterable: + for item in iterable: + self.enqueue(item) + + def __str__(self): + """Return a formatted string representation of this queue.""" + strings = [] + current = self._head + while current: + strings.append(f"'{current[DATA]}'") + current = current[NEXT] + return f"LinkedListQueue([{', '.join(strings)}])" + + def size(self): + """Return the number of items in this queue.""" + return self._length + + def enqueue(self, item): + """Insert the given item at the back of this queue.""" + node = [item, None] + if self.size() == 0: + self._head = node + self._tail = node + else: + self._tail[NEXT] = node + self._tail = node + self._length += 1 + + def front(self): + """Return the item at the front of this queue without removing it.""" + if self.size() == 0: + raise ValueError("can't access front of an empty queue") + return self._head[DATA] + + def dequeue(self): + """Remove and return the item at the front of this queue, + + or raise ValueError if this queue is empty. + """ + if self.size() == 0: + raise ValueError("can't dequeue from an empty queue") + item = self._head[DATA] + self._head = self._head[NEXT] + self._length -= 1 + if self.size() == 0: + self._tail = None + return item diff --git a/paddles/stack.py b/paddles/stack.py new file mode 100644 index 0000000..936a911 --- /dev/null +++ b/paddles/stack.py @@ -0,0 +1,126 @@ +class DynamicArrayStack: + """A stack implementation using Python lists. + + >>> stack = DynamicArrayStack("abc") + >>> stack.size() + 3 + >>> stack.pop() + 'c' + >>> stack.peek() + 'b' + >>> stack.push("C") + >>> print(stack) + DynamicArrayStack(['a', 'b', 'C']) + """ + + def __init__(self, iterable=None): + """Initialize this stack with the given items, if any.""" + if iterable: + self._members = list(iterable) + else: + self._members = [] + + def __str__(self): + """Return a string representation of this stack. + + The string is 'DynamicArrayStack([bottom member, ..., top member])'. + """ + return f"DynamicArrayStack({self._members})" + + def size(self): + """Return how many members this stack has.""" + return len(self._members) + + def push(self, item): + """Add the given item to the top of this stack.""" + self._members.append(item) + + def pop(self): + """Remove and return the item on the top of this stack. + + Raise ValueError if this stack is empty. + """ + if self.size() == 0: + raise ValueError("can't pop a member from an empty stack") + return self._members.pop() + + def peek(self): + """Return the item on the top of this stack. + + Raise ValueError if this stack is empty. + """ + if self.size() == 0: + raise ValueError("can't peek top member of an empty stack") + return self._members[-1] + + +# Each linked list node is a tuple (data, next). +# These constants make the code more readable. +DATA = 0 +NEXT = 1 + + +class LinkedListStack: + """A stack implementation using singly-linked lists. + + >>> stack = LinkedListStack("abc") + >>> stack.size() + 3 + >>> stack.pop() + 'c' + >>> stack.peek() + 'b' + >>> stack.push("C") + >>> print(stack) + LinkedListStack(['a', 'b', 'C']) + """ + + def __init__(self, iterable=None): + """Initialize this stack with the given items, if any.""" + self._head = None + self._length = 0 + if iterable: + for item in iterable: + self.push(item) + + def __str__(self): + """Return a string representation of this stack. + + The string is 'LinkedListStack([bottom member, ..., top member])'. + """ + strings = [] + current = self._head + while current: + strings.append(f"'{current[DATA]}'") + current = current[NEXT] + return "LinkedListStack([" + ", ".join(reversed(strings)) + "])" + + def size(self): + """Return how many members this stack has.""" + return self._length + + def push(self, item): + """Add the given item to the top of this stack.""" + self._head = (item, self._head) + self._length += 1 + + def pop(self): + """Remove and return the item on the top of this stack. + + Raise ValueError if this stack is empty. + """ + if self.size() == 0: + raise ValueError("can't pop a member from an empty stack") + item = self._head[DATA] + self._head = self._head[NEXT] + self._length -= 1 + return item + + def peek(self): + """Return the item on the top of this stack. + + Raise ValueError if this stack is empty. + """ + if self.size() == 0: + raise ValueError("can't peek top member of an empty stack") + return self._head[DATA]