diff --git a/Exercise_1.py b/Exercise_1.py index 532833f5d..d012937e1 100644 --- a/Exercise_1.py +++ b/Exercise_1.py @@ -1,24 +1,60 @@ +""" +Time Complexity: +- push: O(1) +- pop: O(1) +- peek: O(1) +- size: O(1) + +Space Complexity: +- O(n) where n = capacity +- Here capacity is fixed at 10000, so space complexity is O(1) in practical terms. + +Simple fixed-capacity stack implementation. +""" + class myStack: - #Please read sample.java file before starting. - #Kindly include Time and Space complexity at top of each file - def __init__(self): - - def isEmpty(self): - - def push(self, item): - - def pop(self): - - - def peek(self): - - def size(self): - - def show(self): - - -s = myStack() -s.push('1') -s.push('2') -print(s.pop()) -print(s.show()) + # Please read sample.java file before starting. + # Kindly include Time and Space complexity at top of each file + def __init__(self, capacity=10000): + self.capacity = capacity + self.stack = [None] * capacity + self.top = -1 + + def isFull(self): + return self.top == self.capacity - 1 + + def isEmpty(self): + return self.top == -1 + + def push(self, item): + if self.isFull(): + raise OverflowError("Stack is full. Stack Overflow") + self.top += 1 + self.stack[self.top] = item + + def pop(self): + if self.isEmpty(): + raise IndexError("Stack is empty. Stack Underflow") + item = self.stack[self.top] + self.stack[self.top] = None + self.top -= 1 + return item + + def peek(self): + if self.isEmpty(): + raise IndexError("Stack is empty.") + return self.stack[self.top] + + def size(self): + return self.top + 1 + + def show(self): + return [self.stack[i] for i in range(self.top + 1)] + + +if __name__ == "__main__": + s = myStack() + s.push('1') + s.push('2') + print(s.pop()) + print(s.show()) diff --git a/Exercise_2.py b/Exercise_2.py index b11492215..41ede011a 100644 --- a/Exercise_2.py +++ b/Exercise_2.py @@ -1,4 +1,18 @@ +""" +1. Push + Time complexity: O(1) - constant number of pointer updates + regardless of stack size. + + Space complexity: O(1) - one new node is allocated per call; + does not depend on the current size of the stack. +2. Pop + Time complexity: O(1) - constant number of pointer updates + regardless of stack size. + + Space complexity: O(1) - one new node is allocated per call; + does not depend on the current size of the stack. +""" class Node: def __init__(self, data): self.data = data @@ -6,11 +20,35 @@ def __init__(self, data): class Stack: def __init__(self): + self.head = None + self.size = 0 - def push(self, data): + def push(self, data): + """ + Approach: Create a new node, point its `next` at the + current head (the old top of the stack), then make the + new node the head. This inserts at the front of the + linked list, which is O(1) since no traversal is needed. + """ + new_node = Node(data) + new_node.next = self.head + self.head = new_node + self.size += 1 + def pop(self): - + """ + Approach: Check if the stack is empty. If not, remove the head node + (the top of the stack) and return its data. Update the head to point + to the next node in the list. + """ + if self.head is None: + return None + data = self.head.data + self.head = self.head.next + self.size -= 1 + return data + a_stack = Stack() while True: #Give input as string if getting an EOF error. Give input like "push 10" or "pop" diff --git a/Exercise_3.py b/Exercise_3.py index a5d466b59..5049b3488 100644 --- a/Exercise_3.py +++ b/Exercise_3.py @@ -1,9 +1,29 @@ +""" +Time complexity and space complexity for the following operations on a singly-linked list: + +1. Append + Time complexity: O(n) - we may need to traverse the entire list to find the end. + Space complexity: O(1) - we only allocate a new node, which does not depend on the current size of the list. + +2. Find + Time complexity: O(n) - we may need to traverse the entire list to find the key. + Space complexity: O(1) - we do not allocate any additional space that depends on the size of the list. + +3. Remove + Time complexity: O(n) - we may need to traverse the entire list to find the key to remove. + Space complexity: O(1) - we do not allocate any additional space that depends on the size of the list. + +""" + + class ListNode: """ A node in a singly-linked list. """ def __init__(self, data=None, next=None): - + self.data = data + self.next = next + class SinglyLinkedList: def __init__(self): """ @@ -16,17 +36,141 @@ def append(self, data): """ Insert a new element at the end of the list. Takes O(n) time. - """ + Approach: Create a new node with the given data. + If the list is empty, set the head to this new node. + Otherwise, traverse to the end of the list and + set the next pointer of the last node to the new node. + + """ + + new_node = ListNode(data) # was: Node(data) + if self.head is None: + self.head = new_node + else: + current = self.head + while current.next: + current = current.next + current.next = new_node + def find(self, key): """ Search for the first element with `data` matching `key`. Return the element or `None` if not found. Takes O(n) time. + + Approach: Start from the head and traverse the list, + checking each node's data against the key. + If a match is found, return that node. + If the end of the list is reached without finding a match, + return None. """ - + current = self.head + while current: + if current.data == key: + return current + current = current.next + return None + def remove(self, key): """ Remove the first occurrence of `key` in the list. Takes O(n) time. + + Approach: Traverse the list while keeping track of the + previous node. If the current node's data matches the key, + update the previous node's next pointer to skip the current node. + If the node to remove is the head, update the head to the next node. """ + current = self.head + previous = None + while current: + if current.data == key: + if previous is None: + self.head = current.next + else: + previous.next = current.next + return + previous = current + current = current.next + +def run_tests(): + # --- Test append --- + ll = SinglyLinkedList() + ll.append(10) + ll.append(20) + ll.append(30) + + values = [] + current = ll.head + while current: + values.append(current.data) + current = current.next + assert values == [10, 20, 30], f"append failed: {values}" + print("append: OK ->", values) + + # --- Test find (existing) --- + node = ll.find(20) + assert node is not None and node.data == 20, "find failed to locate existing key" + print("find existing: OK -> found", node.data) + + # --- Test find (missing) --- + node = ll.find(999) + assert node is None, "find should return None for missing key" + print("find missing: OK -> None") + + # --- Test remove head --- + ll.remove(10) + values = [] + current = ll.head + while current: + values.append(current.data) + current = current.next + assert values == [20, 30], f"remove head failed: {values}" + print("remove head: OK ->", values) + + # --- Test remove middle/tail --- + ll.append(40) + ll.remove(30) # now middle-ish + values = [] + current = ll.head + while current: + values.append(current.data) + current = current.next + assert values == [20, 40], f"remove middle failed: {values}" + print("remove middle: OK ->", values) + + # --- Test remove tail --- + ll.remove(40) + values = [] + current = ll.head + while current: + values.append(current.data) + current = current.next + assert values == [20], f"remove tail failed: {values}" + print("remove tail: OK ->", values) + + # --- Test remove nonexistent key (should not raise, no-op) --- + ll.remove(999) + values = [] + current = ll.head + while current: + values.append(current.data) + current = current.next + assert values == [20], f"remove nonexistent changed list: {values}" + print("remove nonexistent: OK -> no change", values) + + # --- Test remove last remaining element --- + ll.remove(20) + assert ll.head is None, "list should be empty after removing last element" + print("remove last element: OK -> list is empty") + + # --- Test operations on empty list --- + assert ll.find(1) is None, "find on empty list should return None" + ll.remove(1) # should not raise + print("empty list operations: OK -> no crash") + + print("\nAll tests passed!") + + +run_tests()