├── README.md ├── binary_search.py ├── binary_search_tree.py ├── binary_tree.py ├── bubble_sort.py ├── complexity.py ├── doubly_linked_list.py ├── factorial.py ├── fibonacci.py ├── heap_sort.py ├── insertion_sort.py ├── linked_list.py ├── merge.py ├── merge_sort.py ├── number_print.py ├── parenthesis_balance.py ├── queue_test.py ├── quick_sort.py ├── selection_sort.py ├── test_doubly_linked_list.py └── tree.py /README.md: -------------------------------------------------------------------------------- 1 | # intro_ds_algo_py 2 | This repo contains programs from my upcoming book (to be published at the end of the year) - Introduction to Data Structures and Algorithms in Python. The language of the book is Bangla (aka Bengali), but someday I may also start writing in English (for that I need to learn to write in English). 3 | This is a very introductory book which will prepare the readers to learn more about data structures and algorithms. 4 | 5 | ## Table of Contents (for the Curious) 6 | 7 | * What is Data Structures and Algorithms? 8 | * How to test your code? 9 | * Searching Algorithms 10 | * Linear Search 11 | * Binary Seaarch 12 | * Sorting Algorithms 13 | * Selection Sort 14 | * Bubble Sort 15 | * Insertion Sort 16 | * Stack and Queue 17 | * Stack 18 | * Queue 19 | * Linked List 20 | * Linked List Concepts 21 | * Implementing Linked List in Python 22 | * Doubly Linked List 23 | * Introduction to Recursion 24 | * Tree 25 | * Binary Tree 26 | * Tree Traversal Algorithms 27 | * Binary Search Tree 28 | * Heap, Heap Sort and Priority Queue 29 | * Heap Data Structure 30 | * Heap Sort 31 | * Priority Queue 32 | * Hash Table 33 | * More Sorting Algorithms 34 | * Merge Sort 35 | * Quick Sort 36 | * Counting Sort 37 | -------------------------------------------------------------------------------- /binary_search.py: -------------------------------------------------------------------------------- 1 | def binary_search(L, x): 2 | left, right = 0, len(L)-1 3 | while left <= right: 4 | mid = (left + right)//2 5 | if L[mid] == x: 6 | return mid 7 | if L[mid] < x: 8 | left = mid + 1 9 | else: 10 | right = mid - 1 11 | return -1 12 | 13 | 14 | def binary_search_recursive(L, left, right, x): 15 | if left > right: 16 | return -1 17 | 18 | mid = (left + right) // 2 19 | if L[mid] == x: 20 | return mid 21 | if L[mid] < x: 22 | return binary_search_recursive(L, mid+1, right, x) 23 | else: 24 | return binary_search_recursive(L, left, mid-1, x) 25 | 26 | 27 | if __name__ == "__main__": 28 | L = [1, 2, 3, 5, 6, 7, 8, 9] 29 | left = 0 30 | right = len(L) - 1 31 | for x in range(1, 11): 32 | position = binary_search_recursive(L, left, right, x) 33 | if position == -1: 34 | if x in L: 35 | print(x, "is in L, but function returned -1") 36 | else: 37 | print(x, "not in list") 38 | else: 39 | if L[position] == x: 40 | print(x, "found in correct position.") 41 | else: 42 | print("binary search returned", position, "for", x, "which is not correct") 43 | print("program terminated") -------------------------------------------------------------------------------- /binary_search_tree.py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, data): 3 | self.data = data 4 | self.parent = None 5 | self.left = None 6 | self.right = None 7 | 8 | def __repr__(self): 9 | return repr(self.data) 10 | 11 | def add_left(self, node): 12 | self.left = node 13 | if node is not None: 14 | node.parent = self 15 | 16 | def add_right(self, node): 17 | self.right = node 18 | if node is not None: 19 | node.parent = self 20 | 21 | 22 | def bst_insert(root, node): 23 | last_node = None 24 | current_node = root 25 | while current_node is not None: 26 | last_node = current_node 27 | if node.data < current_node.data: 28 | current_node = current_node.left 29 | else: 30 | current_node = current_node.right 31 | 32 | if last_node is None: 33 | # tree was empty, node is the only node, hence root 34 | root = node 35 | elif node.data < last_node.data: 36 | last_node.add_left(node) 37 | else: 38 | last_node.add_right(node) 39 | 40 | return root 41 | 42 | 43 | def bst_search(node, key): 44 | while node is not None: 45 | if node.data == key: 46 | return node 47 | if key < node.data: 48 | node = node.left 49 | else: 50 | node = node.right 51 | 52 | return node 53 | 54 | 55 | """ 56 | 10 57 | / \ 58 | 5 17 59 | / \ / \ 60 | 3 7 12 19 61 | / \ 62 | 1 4 63 | 64 | """ 65 | def bst_create(): 66 | root = TreeNode(10) 67 | 68 | for item in [5, 17, 3, 7, 12, 19, 1, 4]: 69 | node = TreeNode(item) 70 | root = bst_insert(root, node) 71 | 72 | return root 73 | 74 | 75 | def in_order(node): 76 | if node.left: 77 | in_order(node.left) 78 | print(node) 79 | if node.right: 80 | in_order(node.right) 81 | 82 | 83 | def bst_minimum(root): 84 | while root.left is not None: 85 | root = root.left 86 | return root 87 | 88 | 89 | def bst_transplant(root, current_node, new_node): 90 | if current_node.parent is None: 91 | root = new_node 92 | elif current_node == current_node.parent.left: 93 | current_node.parent.add_left(new_node) 94 | else: 95 | current_node.parent.add_right(new_node) 96 | return root 97 | 98 | 99 | def bst_delete(root, node): 100 | if node.left is None: 101 | root = bst_transplant(root, node, node.right) 102 | elif node.right is None: 103 | root = bst_transplant(root, node, node.left) 104 | else: 105 | min_node = bst_minimum(node.right) 106 | if min_node.parent != node: 107 | root = bst_transplant(root, min_node, min_node.right) 108 | min_node.add_right(node.right) 109 | root = bst_transplant(root, node, min_node) 110 | min_node.add_left(node.left) 111 | 112 | return root 113 | 114 | 115 | if __name__ == "__main__": 116 | root = bst_create() 117 | print("BST:") 118 | in_order(root) 119 | for key in [1, 5, 10]: 120 | node = bst_search(root, key) 121 | print("will delete", node) 122 | root = bst_delete(root, node) 123 | print("BST:") 124 | in_order(root) -------------------------------------------------------------------------------- /binary_tree.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data): 3 | self.data = data 4 | self.left = None 5 | self.right = None 6 | 7 | def __repr__(self): 8 | return repr(self.data) 9 | 10 | def add_left(self, node): 11 | self.left = node 12 | 13 | def add_right(self, node): 14 | self.right = node 15 | 16 | """ 17 | 2 18 | / \ 19 | 7 9 20 | / \ \ 21 | 1 6 8 22 | / \ / \ 23 | 5 10 3 4 24 | """ 25 | def create_tree(): 26 | two = Node(2) 27 | seven = Node(7) 28 | nine = Node(9) 29 | two.add_left(seven) 30 | two.add_right(nine) 31 | one = Node(1) 32 | six = Node(6) 33 | seven.add_left(one) 34 | seven.add_right(six) 35 | five = Node(5) 36 | ten = Node(10) 37 | six.add_left(five) 38 | six.add_right(ten) 39 | eight = Node(8) 40 | nine.add_right(eight) 41 | three = Node(3) 42 | four = Node(4) 43 | eight.add_left(three) 44 | eight.add_right(four) 45 | 46 | # now return the root node 47 | return two 48 | 49 | 50 | def pre_order(node): 51 | print(node) 52 | if node.left: 53 | pre_order(node.left) 54 | if node.right: 55 | pre_order(node.right) 56 | 57 | 58 | def post_order(node): 59 | if node.left: 60 | post_order(node.left) 61 | if node.right: 62 | post_order(node.right) 63 | print(node) 64 | 65 | 66 | def in_order(node): 67 | if node.left: 68 | in_order(node.left) 69 | print(node) 70 | if node.right: 71 | in_order(node.right) 72 | 73 | 74 | if __name__ == "__main__": 75 | root = create_tree() 76 | in_order(root) -------------------------------------------------------------------------------- /bubble_sort.py: -------------------------------------------------------------------------------- 1 | def bubble_sort(L): 2 | n = len(L) 3 | for i in range(0, n): 4 | swapped = False 5 | for j in range(0, n-i-1): 6 | if L[j] > L[j+1]: 7 | L[j], L[j+1] = L[j+1], L[j] 8 | swapped = True 9 | if not swapped: 10 | break 11 | 12 | if __name__ == "__main__": 13 | L = [6, 1, 4, 9, 2] 14 | print("Before sort:", L) 15 | bubble_sort(L) 16 | print("After sort:", L) 17 | 18 | -------------------------------------------------------------------------------- /complexity.py: -------------------------------------------------------------------------------- 1 | n = input() 2 | n = int(n) 3 | 4 | count = 0 5 | for i in range(n): 6 | for j in range(n): 7 | for k in range(n): 8 | count += 1 9 | print("n =", n, "count =", count) 10 | -------------------------------------------------------------------------------- /doubly_linked_list.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=None, prev=None, next=None): 3 | self.data = data 4 | self.prev = prev 5 | self.next = next 6 | 7 | def __repr__(self): 8 | return repr(self.data) 9 | 10 | 11 | class DoublyLinkedList: 12 | def __init__(self): 13 | self.head = Node() 14 | 15 | def __repr__(self): 16 | nodes = [] 17 | current_node = self.head.next 18 | while current_node is not None: 19 | nodes.append(repr(current_node)) 20 | current_node = current_node.next 21 | return ",".join(nodes) 22 | 23 | def append(self, data): 24 | node = Node(data) 25 | 26 | if self.head.next is None: 27 | self.head.next = node 28 | return 29 | 30 | current_node = self.head.next 31 | while current_node.next is not None: 32 | current_node = current_node.next 33 | 34 | current_node.next = node 35 | node.prev = current_node 36 | 37 | def prepend(self, data): 38 | first_node = self.head.next 39 | new_node = Node(data, None, first_node) 40 | self.head.next = new_node 41 | if first_node is not None: 42 | first_node.prev = new_node 43 | 44 | def search(self, item): 45 | current_node = self.head.next 46 | while current_node is not None: 47 | if current_node.data == item: 48 | return current_node 49 | current_node = current_node.next 50 | return None 51 | 52 | def remove_node(self, node): 53 | if node.prev is not None: 54 | node.prev.next = node.next 55 | else: 56 | self.head.next = node.next 57 | 58 | if node.next is not None: 59 | node.next.prev = node.prev 60 | 61 | def remove(self, item): 62 | node = self.search(item) 63 | if node is None: 64 | return 65 | self.remove_node(node) 66 | -------------------------------------------------------------------------------- /factorial.py: -------------------------------------------------------------------------------- 1 | def factorial(n): 2 | if n < 0: 3 | return None 4 | if n in [0, 1]: 5 | return 1 6 | return n * factorial(n-1) 7 | 8 | 9 | def test_factorial(): 10 | assert factorial(1) == 1 11 | assert factorial(0) == 1 12 | assert factorial(-1) == None 13 | assert factorial(5) == 120 14 | -------------------------------------------------------------------------------- /fibonacci.py: -------------------------------------------------------------------------------- 1 | def fibonacci(n): 2 | print("Trying to find fibonacci for", n) 3 | if n == 1 or n == 2: 4 | return 1 5 | return fibonacci(n-2) + fibonacci(n-1) 6 | 7 | 8 | if __name__ == "__main__": 9 | print("5th fibonacci number is", fibonacci(5)) -------------------------------------------------------------------------------- /heap_sort.py: -------------------------------------------------------------------------------- 1 | def left(i): 2 | return 2 * i 3 | 4 | 5 | def right(i): 6 | return 2 * i + 1 7 | 8 | 9 | def parent(i): 10 | return i // 2 11 | 12 | 13 | def is_max_heap(heap): 14 | heap_size = len(heap) - 1 15 | for i in range(heap_size, 1, -1): 16 | p = parent(i) 17 | if heap[p] < heap[i]: 18 | return False 19 | return True 20 | 21 | 22 | def max_heapify(heap, heap_size, i): 23 | l = left(i) 24 | r = right(i) 25 | 26 | if l <= heap_size and heap[l] > heap[i]: 27 | largest = l 28 | else: 29 | largest = i 30 | if r <= heap_size and heap[r] > heap[largest]: 31 | largest = r 32 | 33 | if largest != i: 34 | heap[i], heap[largest] = heap[largest], heap[i] 35 | max_heapify(heap, heap_size, largest) 36 | 37 | 38 | def build_max_heap(heap): 39 | heap_size = len(heap) - 1 40 | for i in range(heap_size//2, 0, -1): 41 | max_heapify(heap, heap_size, i) 42 | 43 | 44 | def heap_sort(heap): 45 | build_max_heap(heap) 46 | heap_size = len(heap) - 1 47 | for i in range(len(heap)-1, 1, -1): 48 | heap[1], heap[i] = heap[i], heap[1] 49 | heap_size -= 1 50 | max_heapify(heap, heap_size, 1) 51 | 52 | 53 | def get_maximum(heap, heap_size): 54 | return heap[1] 55 | 56 | 57 | def extract_max(heap, heap_size): 58 | max_item = heap[1] 59 | heap[1] = heap[heap_size] 60 | heap_size -= 1 61 | max_heapify(heap, heap_size, 1) 62 | return max_item 63 | 64 | 65 | def insert_node(heap, heap_size, node): 66 | heap_size += 1 67 | heap[heap_size] = node 68 | i = heap_size 69 | while i > 1 and heap[parent(i)] < heap[i]: 70 | heap[parent(i)], heap[i] = heap[i], heap[parent(i)] 71 | i = parent(i) 72 | return heap_size 73 | 74 | 75 | if __name__ == "__main__": 76 | heap = [None, 12, 7, 1, 3, 10, 17, 19, 2, 5] 77 | print("Before sorting:", heap) 78 | heap_sort(heap) 79 | print("After sorting:", heap) 80 | -------------------------------------------------------------------------------- /insertion_sort.py: -------------------------------------------------------------------------------- 1 | def insertion_sort(L): 2 | n = len(L) 3 | for i in range(1, n): 4 | item = L[i] 5 | 6 | # Now find the suitable place for i 7 | j = i - 1 8 | while j >= 0 and L[j] > item: 9 | # Shift L[j] to it's right/next position 10 | L[j+1] = L[j] 11 | j = j - 1 12 | 13 | # [j+1] is the suitable position for item 14 | L[j+1] = item 15 | 16 | 17 | if __name__ == "__main__": 18 | L = [6, 1, 4, 9, 2] 19 | print("Before sort:", L) 20 | insertion_sort(L) 21 | print("After sort:", L) 22 | -------------------------------------------------------------------------------- /linked_list.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=None, next=None): 3 | self.data = data 4 | self.next = next 5 | 6 | def __repr__(self): 7 | return repr(self.data) 8 | 9 | 10 | class LinkedList: 11 | def __init__(self): 12 | self.head = None 13 | 14 | def __repr__(self): 15 | nodes = [] 16 | current_node = self.head.next 17 | while current_node: 18 | nodes.append(repr(current_node)) 19 | current_node = current_node.next 20 | return ",".join(nodes) 21 | 22 | def append(self, data): 23 | node = Node(data) 24 | 25 | if self.head is None: 26 | self.head = Node(None, node) 27 | return 28 | 29 | current_node = self.head.next 30 | while current_node.next: 31 | current_node = current_node.next 32 | 33 | current_node.next = node 34 | 35 | def prepend(self, data): 36 | node = Node(data, self.head.next) 37 | self.head.next = node 38 | 39 | def insert(self, data, new_data): 40 | current_node = self.head.next 41 | while current_node: 42 | if current_node.data == data: 43 | new_node = Node(new_data, current_node.next) 44 | current_node.next = new_node 45 | break 46 | current_node = current_node.next 47 | 48 | def search(self, item): 49 | current_node = self.head.next 50 | while current_node: 51 | if current_node.data == item: 52 | return current_node 53 | current_node = current_node.next 54 | return None 55 | 56 | 57 | def remove(self, item): 58 | previous_node = self.head 59 | current_node = previous_node.next 60 | while current_node: 61 | if current_node.data == item: 62 | break 63 | previous_node = current_node 64 | current_node = current_node.next 65 | 66 | if current_node is None: 67 | return None 68 | 69 | if self.head == previous_node: 70 | self.head.next = current_node.next 71 | else: 72 | previous_node.next = current_node.next 73 | -------------------------------------------------------------------------------- /merge.py: -------------------------------------------------------------------------------- 1 | def merge(a, b): 2 | merged_list = [] 3 | 4 | len_a, len_b = len(a), len(b) 5 | index_a, index_b = 0, 0 6 | while index_a < len_a and index_b < len_b: 7 | if a[index_a] < b[index_b]: 8 | merged_list.append(a[index_a]) 9 | index_a += 1 10 | else: 11 | merged_list.append(b[index_b]) 12 | index_b += 1 13 | 14 | if index_a < len_a: 15 | merged_list.extend(a[index_a:]) 16 | elif index_b < len_b: 17 | merged_list.extend(b[index_b:]) 18 | 19 | return merged_list 20 | 21 | if __name__ == "__main__": 22 | scenarios = [ 23 | {"a": [1], "b": [2], "expected": [1, 2]}, 24 | {"a": [2], "b": [1], "expected": [1, 2]}, 25 | {"a": [], "b": [1, 2], "expected": [1, 2]}, 26 | {"a": [1, 2], "b": [], "expected": [1, 2]}, 27 | {"a": [1, 3, 5, 6], "b": [2, 4, 7, 8], "expected": [1, 2, 3, 4, 5, 6, 7, 8]}, 28 | {"a": [1], "b": [2, 3, 4], "expected": [1, 2, 3, 4]}, 29 | ] 30 | for item in scenarios: 31 | merged_list = merge(item["a"], item["b"]) 32 | try: 33 | assert item["expected"] == merged_list 34 | except AssertionError: 35 | print("Output didn't match expected output") 36 | print("expected:", item["expected"]) 37 | print("got:", merged_list) 38 | -------------------------------------------------------------------------------- /merge_sort.py: -------------------------------------------------------------------------------- 1 | def merge(a, b): 2 | merged_list = [] 3 | 4 | len_a, len_b = len(a), len(b) 5 | index_a, index_b = 0, 0 6 | while index_a < len_a and index_b < len_b: 7 | if a[index_a] < b[index_b]: 8 | merged_list.append(a[index_a]) 9 | index_a += 1 10 | else: 11 | merged_list.append(b[index_b]) 12 | index_b += 1 13 | 14 | if index_a < len_a: 15 | merged_list.extend(a[index_a:]) 16 | elif index_b < len_b: 17 | merged_list.extend(b[index_b:]) 18 | 19 | return merged_list 20 | 21 | 22 | def merge_sort(L): 23 | if len(L) <= 1: 24 | return L 25 | mid = len(L) // 2 26 | left = merge_sort(L[:mid]) 27 | right = merge_sort(L[mid:]) 28 | return merge(left, right) 29 | 30 | 31 | if __name__ == "__main__": 32 | L = [[4, 7, 2, 3], [10], [10, 9, 8, 7, 6], [2, 3, 1], [1, 2], [2, 1]] 33 | for li in L: 34 | sorted_list = merge_sort(li) 35 | print("Original List:", li) 36 | print("Sorted List:", sorted_list) 37 | print() -------------------------------------------------------------------------------- /number_print.py: -------------------------------------------------------------------------------- 1 | def print_number(n): 2 | if n == 0: 3 | return 4 | print_number(n-1) 5 | print(n) 6 | 7 | 8 | if __name__ == "__main__": 9 | print_number(5) -------------------------------------------------------------------------------- /parenthesis_balance.py: -------------------------------------------------------------------------------- 1 | def is_balanced(input_str): 2 | s = list() 3 | 4 | for ch in input_str: 5 | if ch == '(': 6 | s.append(ch) 7 | if ch == ')': 8 | if not s: 9 | return False 10 | previous_ch = s.pop() 11 | if previous_ch != '(': 12 | return False 13 | 14 | return not s 15 | 16 | 17 | if __name__ == "__main__": 18 | input_str = input() 19 | 20 | if is_balanced(input_str): 21 | print(input_str, "is balanced.") 22 | else: 23 | print(input_str, "is not balanced.") 24 | 25 | -------------------------------------------------------------------------------- /queue_test.py: -------------------------------------------------------------------------------- 1 | class Queue: 2 | def __init__(self, size): 3 | self.items = [0] * size 4 | self.max_size = size 5 | self.head, self.tail, self.size = 0, 0, 0 6 | 7 | def enqueue(self, item): 8 | if self.is_full(): 9 | print("Queue is full!") 10 | return 11 | print("Inserting", item) 12 | self.items[self.tail] = item 13 | self.tail = (self.tail + 1) % self.max_size 14 | self.size += 1 15 | 16 | def dequeue(self): 17 | item = self.items[self.head] 18 | self.head = (self.head + 1) % self.max_size 19 | self.size -= 1 20 | return item 21 | 22 | def is_empty(self): 23 | if self.size == 0: 24 | return True 25 | return False 26 | 27 | def is_full(self): 28 | if self.size == self.max_size: 29 | return True 30 | return False 31 | 32 | 33 | if __name__ == "__main__": 34 | q = Queue(3) 35 | q.enqueue("Tahmid") 36 | q.enqueue("Rafi") 37 | q.enqueue("Tamim") 38 | q.enqueue("Subeen") 39 | while not q.is_empty(): 40 | person = q.dequeue() 41 | print(person) 42 | q.enqueue("Subeen") 43 | print(q.items) 44 | print("head:", q.head) 45 | print("tail:", q.tail) 46 | 47 | -------------------------------------------------------------------------------- /quick_sort.py: -------------------------------------------------------------------------------- 1 | def partition(L, low, high): 2 | pivot = L[high] 3 | 4 | i = low - 1 5 | for j in range(low, high): 6 | if L[j] < pivot: 7 | i += 1 8 | L[i], L[j] = L[j], L[i] 9 | 10 | L[i+1], L[high] = L[high], L[i+1] 11 | return i+1 12 | 13 | 14 | def quick_sort(L, low, high): 15 | if low >= high: 16 | return 17 | p = partition(L, low, high) 18 | quick_sort(L, low, p-1) 19 | quick_sort(L, p+1, high) 20 | 21 | 22 | if __name__ == "__main__": 23 | L = [1, 5, 6, 3, 8, 4, 7, 2] 24 | quick_sort(L, 0, len(L)-1) 25 | print(L) -------------------------------------------------------------------------------- /selection_sort.py: -------------------------------------------------------------------------------- 1 | def selection_sort(L): 2 | n = len(L) 3 | for i in range(0, n-1): 4 | index_min = i 5 | for j in range(i+1, n): 6 | if L[j] < L[index_min]: 7 | index_min = j 8 | if index_min != i: 9 | L[i], L[index_min] = L[index_min], L[i] 10 | 11 | if __name__ == "__main__": 12 | L = [6, 1, 4, 9, 2] 13 | print("Before sort:", L) 14 | selection_sort(L) 15 | print("After sort:", L) 16 | 17 | -------------------------------------------------------------------------------- /test_doubly_linked_list.py: -------------------------------------------------------------------------------- 1 | from doubly_linked_list import * 2 | 3 | 4 | def test_append(): 5 | dll = DoublyLinkedList() 6 | 7 | assert dll.head.next == None, "linked list empty, so head should point to None" 8 | 9 | item = 5 10 | dll.append(item) 11 | assert dll.head.next.data == item, "head should point to the first node" 12 | 13 | second_item = 8 14 | dll.append(second_item) 15 | assert dll.head.next.data == item, "head should point to the first node" 16 | 17 | first_node = dll.head.next 18 | second_node = first_node.next 19 | assert first_node.next.data == second_item, "first node should point to second node" 20 | assert second_node.prev.data == item, "previous node of second_node should be the first node" 21 | assert str(dll) == "5,8", "string representation of dll should match 5,8" 22 | 23 | 24 | def test_prepend(): 25 | dll = DoublyLinkedList() 26 | 27 | assert dll.head.next == None, "linked list empty, so head should point to None" 28 | 29 | item = 5 30 | dll.prepend(item) 31 | assert dll.head.next.data == item, "head should point to the first node" 32 | 33 | new_item = 10 34 | dll.prepend(new_item) 35 | assert dll.head.next.data == new_item, "head should point to the new node" 36 | 37 | new_node = dll.head.next 38 | old_node = new_node.next 39 | assert new_node.prev == None, "previous node of new_node should be None" 40 | assert old_node.prev == new_node, "previould node of the old_node should be new_node" 41 | assert old_node.data == item, "checking the data of old_node" 42 | assert str(dll) == "10,5", "string representation of dll should match 10,5" 43 | 44 | 45 | def test_search(): 46 | dll = DoublyLinkedList() 47 | 48 | item = 5 49 | assert dll.search(item) == None, "item shouldn't be found in an empty list" 50 | 51 | dll.append(item) 52 | node = dll.search(item) 53 | assert node.data == item, "item should be found in the list" 54 | 55 | dll.append(10) 56 | dll.append(15) 57 | node = dll.search(10) 58 | assert node.data == 10, "10 should be found in the list" 59 | 60 | node = dll.search(20) 61 | assert node == None, "20 should not be found in the list" 62 | 63 | 64 | def test_remove_node(): 65 | dll = DoublyLinkedList() 66 | dll.append(5) 67 | node = dll.search(5) 68 | assert dll.head.next == node, "head should point to node" 69 | dll.remove_node(node) 70 | assert dll.head.next == None, "head should point to None, as list is empty" 71 | 72 | 73 | def test_remove(): 74 | dll = DoublyLinkedList() 75 | dll.append(5) 76 | dll.append(10) 77 | dll.append(15) 78 | 79 | node_5 = dll.search(5) 80 | node_10 = dll.search(10) 81 | node_15 = dll.search(15) 82 | 83 | assert node_10.next == node_15 84 | assert node_15.prev == node_10 85 | 86 | dll.remove(15) 87 | assert node_10.next == None, "now next of 10 should be None" 88 | assert str(dll) == "5,10", "15 should not be in the list anymore" 89 | 90 | dll.remove(5) 91 | assert dll.head.next == node_10, "now head should point to 10" 92 | assert node_10.prev == None, "10 is the first node, so prev should be None" 93 | assert str(dll) == "10", "dll should only contain 10" 94 | -------------------------------------------------------------------------------- /tree.py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, data): 3 | self.data = data 4 | self.parent = None 5 | self.left = None 6 | self.right = None 7 | 8 | def __repr__(self): 9 | return repr(self.data) 10 | 11 | def add_left(self, node): 12 | self.left = node 13 | if node is not None: 14 | node.parent = self 15 | 16 | def add_right(self, node): 17 | self.right = node 18 | if node is not None: 19 | node.parent = self 20 | 21 | 22 | """ 23 | 2 24 | / \ 25 | 7 5 26 | / \ \ 27 | 1 6 8 28 | / \ / \ 29 | 5 10 3 4 30 | """ 31 | def create_tree(): 32 | two = TreeNode(2) 33 | seven = TreeNode(7) 34 | five = TreeNode(5) 35 | two.add_left(seven) 36 | two.add_right(five) 37 | one = TreeNode(1) 38 | six = TreeNode(6) 39 | seven.add_left(one) 40 | seven.add_right(six) 41 | five = TreeNode(5) 42 | ten = TreeNode(10) 43 | six.add_left(five) 44 | six.add_right(10) 45 | eight = TreeNode(8) 46 | five.add_right(eight) 47 | three = TreeNode(3) 48 | four = TreeNode(4) 49 | eight.add_left(three) 50 | eight.add_right(four) 51 | 52 | # now return the root node 53 | return two 54 | 55 | 56 | def pre_order(node): 57 | print(node) 58 | if node.left: 59 | pre_order(node.left) 60 | if node.right: 61 | pre_order(node.right) 62 | 63 | 64 | if __name__ == "__main__": 65 | root = create_tree() 66 | pre_order(root) --------------------------------------------------------------------------------