├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── assets └── py_dsa.jpg ├── setup.py ├── src └── py_dsa │ ├── algorithms.py │ └── data_structures.py └── test_py_dsa.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ["9600064291@paytm"] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *$py.class 4 | *.__pycache__/ 5 | # Distribution / packaging 6 | .Python 7 | build/ 8 | develop-eggs/ 9 | dist/ 10 | downloads/ 11 | eggs/ 12 | .eggs/ 13 | lib/ 14 | lib64/ 15 | parts/ 16 | sdist/ 17 | var/ 18 | wheels/ 19 | share/python-wheels/ 20 | *.egg-info/ 21 | .installed.cfg 22 | *.egg 23 | MANIFEST 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .nox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *.cover 45 | *.py,cover 46 | .hypothesis/ 47 | .pytest_cache/ 48 | cover/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Scrapy stuff: 55 | .scrapy 56 | 57 | # Sphinx documentation 58 | docs/_build/ 59 | 60 | # PyBuilder 61 | .pybuilder/ 62 | target/ 63 | 64 | 65 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 66 | __pypackages__/ 67 | 68 | # Celery stuff 69 | celerybeat-schedule 70 | celerybeat.pid 71 | 72 | # SageMath parsed files 73 | *.sage.py 74 | 75 | # Environments 76 | .env 77 | .venv 78 | env/ 79 | venv/ 80 | ENV/ 81 | env.bak/ 82 | venv.bak/ 83 | 84 | # Rope project settings 85 | .ropeproject 86 | 87 | # mkdocs documentation 88 | /site 89 | 90 | # mypy 91 | .mypy_cache/ 92 | .dmypy.json 93 | dmypy.json 94 | 95 | # Pyre type checker 96 | .pyre/ 97 | 98 | # pytype static type analyzer 99 | .pytype/ 100 | 101 | # Cython debug symbols 102 | cython_debug/ 103 | 104 | # VS Code 105 | .vscode 106 | 107 | // extra files 108 | sample.py -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Vaidhyanathan S M 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.py 2 | include LICENSE 3 | recursive-include src *.py 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## py-dsa 2 | 3 | ![GitHub followers](https://img.shields.io/github/followers/smv1999?style=for-the-badge) 4 | ![GitHub forks](https://img.shields.io/github/forks/smv1999/py_dsa?style=for-the-badge) 5 | ![GitHub Repo stars](https://img.shields.io/github/stars/smv1999/py_dsa?style=for-the-badge) 6 | ![Lines of code](https://img.shields.io/tokei/lines/github/smv1999/py_dsa?style=for-the-badge) 7 | ![GitHub](https://img.shields.io/github/license/smv1999/py_dsa?color=blue&style=for-the-badge) 8 | ![PyPI](https://img.shields.io/pypi/v/py_dsa?color=blue&style=for-the-badge) 9 | 10 | ### Introduction 11 | 12 | The py-dsa module contains all the data structures and algorithms implementations. 13 | 14 | ### Installation 15 | 16 | You can install the module using pip as shown below. 17 | 18 | ```bash 19 | pip install py-dsa 20 | ``` 21 | ### Usage 22 | 23 | Consider the following examples : 24 | 25 | ```python 26 | from py_dsa.data_structures import * 27 | 28 | test_linkedlist = LinkedList() 29 | test_linkedlist.add_first(10) 30 | test_linkedlist.add_first(20) 31 | test_linkedlist.add_first(30) 32 | test_linkedlist.remove_last() 33 | test_linkedlist.reverse_list() 34 | test_linkedlist.print_list() 35 | """ 36 | Output : 37 | 20 38 | 30 39 | """ 40 | ``` 41 | 42 | ```python 43 | from py_dsa.data_structures import * 44 | 45 | test_tree = Tree() 46 | test_tree.add(10) 47 | test_tree.add(5) 48 | test_tree.add(30) 49 | print(test_tree.height()) 50 | test_tree.invert_tree() 51 | test_tree.print_tree(traversal='postorder') 52 | """ 53 | Output: 54 | 2 55 | 30 56 | 5 57 | 10 58 | """ 59 | ``` 60 | 61 | ```python 62 | from py_dsa.algorithms import * 63 | 64 | a = [1, 2, 3, 4.5] 65 | s = Searching() 66 | print(s.linear_search(a, 3)) 67 | """ 68 | Output : 69 | 2 70 | """ 71 | ``` 72 | ### Testing 73 | 74 | To install py-dsa, along with the tools you need to develop and run tests. Run the following command : 75 | ```bash 76 | $ pip install -e .[dev] 77 | ``` 78 | 79 | For running the tests, type the following command : 80 | 81 | ```bash 82 | py.test 83 | ``` 84 | 85 | ### Bugs/Requests 86 | 87 | Please use the [GitHub issue tracker](https://github.com/smv1999/py_dsa/issues) to submit bugs or request features. 88 | 89 | ### License 90 | 91 | Copyright Vaidhyanathan S M, 2021 92 | 93 | Distributed under the terms of the [MIT](https://github.com/smv1999/py_dsa/blob/main/LICENSE) license, py-dsa is free and open source software. 94 | 95 | 96 | -------------------------------------------------------------------------------- /assets/py_dsa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smv1999/py_dsa/bb1a3c7e705f70c47a2001161a68ec75dd1aec9f/assets/py_dsa.jpg -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setup( 7 | name='py_dsa', 8 | version='0.0.3', 9 | description='The py_dsa package contains all the data structures and algorithms implementations', 10 | packages=['py_dsa'], 11 | py_modules=['py_dsa.algorithms', 'py_dsa.data_structures'], 12 | package_dir={'': 'src'}, 13 | extras_require={ 14 | "dev": [ 15 | "pytest >= 3.7", 16 | "check-manifest", 17 | "twine" 18 | ] 19 | }, 20 | classifiers=[ 21 | "Programming Language :: Python :: 3", 22 | "Programming Language :: Python :: 3.7", 23 | "Programming Language :: Python :: 3.8", 24 | "Topic :: Software Development :: Libraries :: Python Modules", 25 | "License :: OSI Approved :: MIT License", 26 | "Operating System :: OS Independent" 27 | ], 28 | python_requires='>=3', 29 | long_description=long_description, 30 | long_description_content_type="text/markdown", 31 | author="Vaidhyanathan S M", 32 | author_email="vaidhyanathan.sm@gmail.com", 33 | url="https://github.com/smv1999/py_dsa" 34 | ) 35 | -------------------------------------------------------------------------------- /src/py_dsa/algorithms.py: -------------------------------------------------------------------------------- 1 | class Searching: 2 | def linear_search(self, a, k): 3 | """ 4 | Returns an integer 5 | 6 | Index is returned, if the key element is found in the list. Otherwise, -1 is returned. 7 | """ 8 | for i in range(len(a)): 9 | if a[i] == k: 10 | return i 11 | return -1 12 | 13 | def binary_search(self, a, k): 14 | """ 15 | Returns an integer 16 | 17 | Index is returned, if the key element is found in the list. Otherwise, -1 is returned. 18 | """ 19 | first = 0 20 | last = len(a) - 1 21 | while first <= last: 22 | mid = (first + last) // 2 23 | if a[mid] == k: 24 | return mid 25 | elif a[mid] < k: 26 | first = mid + 1 27 | elif a[mid] > k: 28 | last = mid - 1 29 | return -1 30 | 31 | def jump_search(self, a, k): 32 | """ 33 | Returns an integer 34 | 35 | Index is returned, if the key element is found in the list. Otherwise, -1 is returned. 36 | """ 37 | step = 0 38 | while a[step] < k: 39 | step += 1 40 | i = step 41 | while i < len(a): 42 | if a[i] == k: 43 | return i 44 | i += step 45 | return -1 46 | 47 | 48 | class Sorting: 49 | def bubble_sort(self, a): 50 | """ 51 | Returns None 52 | 53 | Internally sorts the given list by using the bubble sort algorithm - repeatedly swapping the adjacent elements if they are in unsorted order 54 | """ 55 | n = len(a) 56 | for i in range(n-1): 57 | for j in range(0, n - i - 1): 58 | if a[j] >= a[j+1]: 59 | a[j], a[j+1] = a[j+1], a[j] 60 | 61 | def insertion_sort(self, a): 62 | """ 63 | Returns None 64 | 65 | Internally sorts the given list by using the insertion sort algorithm - list is virtually split into sorted and unsorted part. Values from the unsorted part are picked and placed at the correct position in the sorted part. 66 | """ 67 | n = len(a) 68 | for i in range(1, n): 69 | temp = a[i] 70 | j = i - 1 71 | while(j >= 0 and temp < a[j]): 72 | a[j+1] = a[j] 73 | j -= 1 74 | a[j+1] = temp 75 | 76 | def selection_sort(self, a): 77 | """ 78 | Returns None 79 | 80 | Internally sorts the given list by using the selection sort algorithm - repeatedly picking the smallest element from the unsorted part and placing it at the correct position in the sorted part. 81 | """ 82 | n = len(a) 83 | for i in range(n-1): 84 | min_index = i 85 | for j in range(i+1, n): 86 | if a[j] < a[min_index]: 87 | min_index = j 88 | if min_index != i: 89 | a[i], a[min_index] = a[min_index], a[i] 90 | 91 | def quick_sort(self, a): 92 | """ 93 | Returns None 94 | 95 | Internally sorts the given list by using the quick sort algorithm - the list is split into two parts. The left part is sorted, the right part is sorted recursively. 96 | """ 97 | n = len(a) 98 | self.__quick_sort_helper(a, 0, n-1) 99 | 100 | def __quick_sort_helper(self, a, low, high): 101 | if low < high: 102 | pivot = self.__partition(a, low, high) 103 | self.__quick_sort_helper(a, low, pivot-1) 104 | self.__quick_sort_helper(a, pivot+1, high) 105 | 106 | def __partition(self, a, low, high): 107 | pivot = a[high] 108 | i = low - 1 109 | for j in range(low, high): 110 | if a[j] <= pivot: 111 | i += 1 112 | a[i], a[j] = a[j], a[i] 113 | a[i+1], a[high] = a[high], a[i+1] 114 | return i+1 115 | 116 | def merge_sort(self, a): 117 | """ 118 | Returns None 119 | 120 | Internally sorts the given list by using the merge sort algorithm - the list is split into two parts. The left part is sorted, the right part is sorted recursively. 121 | """ 122 | if len(a) > 1: 123 | 124 | mid = len(a)//2 125 | 126 | L = a[:mid] 127 | 128 | R = a[mid:] 129 | 130 | self.merge_sort(L) 131 | 132 | self.merge_sort(R) 133 | 134 | i = j = k = 0 135 | 136 | while i < len(L) and j < len(R): 137 | if L[i] < R[j]: 138 | a[k] = L[i] 139 | i += 1 140 | else: 141 | a[k] = R[j] 142 | j += 1 143 | k += 1 144 | 145 | while i < len(L): 146 | a[k] = L[i] 147 | i += 1 148 | k += 1 149 | 150 | while j < len(R): 151 | a[k] = R[j] 152 | j += 1 153 | k += 1 154 | 155 | def heap_sort(self, a): 156 | """ 157 | Returns None 158 | 159 | Internally sorts the given list by using the heap sort algorithm - the list is first converted into a heap. Then, the heap is repeatedly reduced to a sorted list. 160 | """ 161 | n = len(a) 162 | 163 | for i in range(n//2 - 1, -1, -1): 164 | self.__heapify(a, n, i) 165 | 166 | for i in range(n-1, 0, -1): 167 | a[i], a[0] = a[0], a[i] 168 | self.__heapify(a, i, 0) 169 | 170 | def __heapify(self, a, n, i): 171 | largest = i 172 | l = 2 * i + 1 173 | r = 2 * i + 2 174 | if l < n and a[l] > a[largest]: 175 | largest = l 176 | if r < n and a[r] > a[largest]: 177 | largest = r 178 | if largest != i: 179 | a[i], a[largest] = a[largest], a[i] 180 | self.__heapify(a, n, largest) 181 | 182 | def radix_sort(self, a): 183 | """ 184 | Returns None 185 | 186 | Internally sorts the given list by using the radix sort algorithm - the list is first converted into a list of digits. Then, the list is repeatedly sorted by the digits. 187 | """ 188 | max1 = max(a) 189 | 190 | exp = 1 191 | while max1 / exp > 0: 192 | self.__counting_sort(a, exp) 193 | exp *= 10 194 | 195 | def __counting_sort(self, a, exp): 196 | n = len(a) 197 | b = [0] * n 198 | count = [0] * 10 199 | for i in range(0, n): 200 | index = (a[i] // exp) 201 | count[(index) % 10] += 1 202 | for i in range(1, 10): 203 | count[i] += count[i-1] 204 | for i in range(n-1, -1, -1): 205 | index = (a[i] // exp) 206 | b[count[(index) % 10] - 1] = a[i] 207 | count[(index) % 10] -= 1 208 | for i in range(0, n): 209 | a[i] = b[i] 210 | 211 | def shell_sort(self, a): 212 | """ 213 | Returns None 214 | 215 | Internally sorts the given list by using the shell sort algorithm - the list is first converted into a list of sublists. Then, the list is repeatedly sorted by the sublists. 216 | """ 217 | n = len(a) 218 | h = 1 219 | while h < n//3: 220 | h = 3*h + 1 221 | while h >= 1: 222 | for i in range(h, n): 223 | for j in range(i, h-1, -h): 224 | if a[j] < a[j-h]: 225 | a[j], a[j-h] = a[j-h], a[j] 226 | h = h//3 227 | -------------------------------------------------------------------------------- /src/py_dsa/data_structures.py: -------------------------------------------------------------------------------- 1 | class Stack: 2 | """Methods that perform various stack operations""" 3 | 4 | def __init__(self): 5 | """Initialize the stack""" 6 | self.stack = [] 7 | 8 | def is_empty(self): 9 | """Check if the stack is empty""" 10 | return self.stack == [] 11 | 12 | def push(self, item): 13 | """Push an item onto the stack""" 14 | self.stack.append(item) 15 | 16 | def pop(self): 17 | """Pop the last item off the stack and return it""" 18 | if self.is_empty(): 19 | return None 20 | else: 21 | return self.stack.pop() 22 | 23 | def peek(self): 24 | """Return the last element of the list""" 25 | if self.is_empty(): 26 | return None 27 | else: 28 | return self.stack[-1] 29 | 30 | def size(self): 31 | """Return the size of the list""" 32 | return len(self.stack) 33 | 34 | 35 | class Queue: 36 | """Methods that perform various Queue operations""" 37 | 38 | def __init__(self): 39 | """Initialize the queue""" 40 | self.queue = [] 41 | 42 | def is_empty(self): 43 | """Check if the queue is empty""" 44 | return self.queue == [] 45 | 46 | def enqueue(self, item): 47 | """Add an item to the end of the queue""" 48 | self.queue.append(item) 49 | 50 | def dequeue(self): 51 | """Remove and return the first element of the list""" 52 | if self.is_empty(): 53 | return None 54 | else: 55 | return self.queue.pop(0) 56 | 57 | def size(self): 58 | """Return the size of the list""" 59 | return len(self.queue) 60 | 61 | 62 | class Node: 63 | """Method that creates a node""" 64 | 65 | def __init__(self, data): 66 | """Initialize the node""" 67 | self.data = data 68 | self.next = None 69 | 70 | 71 | class LinkedList: 72 | """Methods that perform various operations on Linked List using Node class""" 73 | 74 | def __init__(self): 75 | """Initialize the list""" 76 | self.head = None 77 | 78 | def is_empty(self): 79 | """Check if the list is empty""" 80 | return self.head == None 81 | 82 | def add_first(self, data): 83 | """Add an item to the beginning of the list""" 84 | new_node = Node(data) 85 | new_node.next = self.head 86 | self.head = new_node 87 | 88 | def add_last(self, data): 89 | """Add an item to the end of the list""" 90 | new_node = Node(data) 91 | if self.head == None: 92 | self.head = new_node 93 | else: 94 | current = self.head 95 | while current.next != None: 96 | current = current.next 97 | current.next = new_node 98 | 99 | def add_in_between(self, data, prev_node, next_node): 100 | """Add an item to the middle of the list""" 101 | new_node = Node(data) 102 | new_node.next = next_node 103 | prev_node.next = new_node 104 | 105 | def remove_first(self): 106 | """Remove and return the first element of the list""" 107 | if self.is_empty(): 108 | return None 109 | else: 110 | self.head = self.head.next 111 | 112 | def remove_last(self): 113 | """Remove and return the last element of the list""" 114 | if self.is_empty(): 115 | return None 116 | else: 117 | current = self.head 118 | prev = None 119 | while current.next != None: 120 | prev = current 121 | current = current.next 122 | prev.next = None 123 | 124 | def remove_in_between(self, prev_node, next_node): 125 | """Remove and return the middle element of the list""" 126 | if self.is_empty(): 127 | return None 128 | else: 129 | prev_node.next = next_node 130 | 131 | def print_list(self): 132 | """Print the list""" 133 | if self.is_empty(): 134 | print("List is empty") 135 | else: 136 | current = self.head 137 | while current != None: 138 | print(current.data) 139 | current = current.next 140 | 141 | def reverse_list(self): 142 | """Reverses the list""" 143 | if self.is_empty(): 144 | return None 145 | else: 146 | current = self.head 147 | prev = None 148 | while current != None: 149 | next = current.next 150 | current.next = prev 151 | prev = current 152 | current = next 153 | self.head = prev 154 | 155 | 156 | class TreeNode: 157 | """Method that creates a Tree Node""" 158 | 159 | def __init__(self, data): 160 | """Initialize the node""" 161 | self.data = data 162 | self.left = None 163 | self.right = None 164 | 165 | 166 | class Tree: 167 | """Methods that perform various operations on Binary Tree using Tree Node""" 168 | 169 | def __init__(self): 170 | """Initialize the tree""" 171 | self.root = None 172 | 173 | def is_empty(self): 174 | """Check if the tree is empty""" 175 | return self.root == None 176 | 177 | def add(self, data): 178 | """Add an item to the binary tree""" 179 | if self.is_empty(): 180 | self.root = TreeNode(data) 181 | else: 182 | current = self.root 183 | while True: 184 | if data < current.data: 185 | if current.left: 186 | current = current.left 187 | else: 188 | current.left = TreeNode(data) 189 | break 190 | elif data > current.data: 191 | if current.right: 192 | current = current.right 193 | else: 194 | current.right = TreeNode(data) 195 | break 196 | else: 197 | break 198 | 199 | def height(self): 200 | """Return the height of the tree""" 201 | if self.is_empty(): 202 | return 0 203 | else: 204 | return self.__height_node(self.root) 205 | 206 | def __height_node(self, node): 207 | """Return the height of the node""" 208 | if node == None: 209 | return 0 210 | else: 211 | return max(self.__height_node(node.left), self.__height_node(node.right)) + 1 212 | 213 | def search(self, data): 214 | """Search for an item in the tree""" 215 | if self.is_empty(): 216 | return False 217 | else: 218 | current = self.root 219 | while current != None: 220 | if data < current.data: 221 | current = current.left 222 | elif data > current.data: 223 | current = current.right 224 | else: 225 | return True 226 | return False 227 | 228 | def remove(self, data): 229 | """Delete an item from the binary tree""" 230 | if self.is_empty(): 231 | return 232 | else: 233 | self.__remove_node(self.root, data) 234 | 235 | def __remove_node(self, node, data): 236 | """Delete the node""" 237 | if node == None: 238 | return 239 | else: 240 | if data < node.data: 241 | 242 | node.left = self.__remove_node(node.left, data) 243 | elif data > node.data: 244 | node.right = self.__remove_node(node.right, data) 245 | else: 246 | if node.left == None: 247 | temp = node.right 248 | node = None 249 | return temp 250 | elif node.right == None: 251 | temp = node.left 252 | node = None 253 | return temp 254 | temp = self.__min_value_node(node.right) 255 | node.data = temp.data 256 | node.right = self.__remove_node(node.right, temp.data) 257 | return node 258 | 259 | def invert_tree(self): 260 | """Invert the tree""" 261 | if self.is_empty(): 262 | return None 263 | else: 264 | self.__invert_tree(self.root) 265 | 266 | def __invert_tree(self, node): 267 | """Invert the tree""" 268 | if node == None: 269 | return 270 | else: 271 | node.left, node.right = node.right, node.left 272 | self.__invert_tree(node.left) 273 | self.__invert_tree(node.right) 274 | 275 | def __min_value_node(self, node): 276 | """Return the minimum value node""" 277 | while node.left != None: 278 | node = node.left 279 | return node 280 | 281 | def print_tree(self, traversal='inorder'): 282 | """Prints the tree in in-order by default. 283 | Takes an optional argument traversal to print in preorder or postorder 284 | """ 285 | if self.is_empty(): 286 | print("Tree is empty") 287 | else: 288 | current = self.root 289 | self.__print_node(current, traversal) 290 | 291 | def __print_node(self, node, traversal): 292 | """Print the node""" 293 | if node == None: 294 | return 295 | else: 296 | if traversal == 'inorder': 297 | self.__inorder_traversal(self.root) 298 | elif traversal == 'preorder': 299 | self.__preorder_traversal(self.root) 300 | else: 301 | self.__postorder_traversal(self.root) 302 | 303 | def __inorder_traversal(self, node): 304 | """Inorder traversal of the tree""" 305 | if node == None: 306 | return 307 | else: 308 | self.__inorder_traversal(node.left) 309 | print(node.data) 310 | self.__inorder_traversal(node.right) 311 | 312 | def __preorder_traversal(self, node): 313 | """Preorder traversal""" 314 | if node == None: 315 | return 316 | else: 317 | print(node.data) 318 | self.__preorder_traversal(node.left) 319 | self.__preorder_traversal(node.right) 320 | 321 | def __postorder_traversal(self, node): 322 | """Postorder traversal""" 323 | if node == None: 324 | return 325 | else: 326 | self.__postorder_traversal(node.left) 327 | self.__postorder_traversal(node.right) 328 | print(node.data) 329 | 330 | 331 | class Graph: 332 | """Methods that perform various operations on Graph""" 333 | 334 | def __init__(self): 335 | """Initialize the graph""" 336 | self.graph = {} 337 | 338 | def add_vertex(self, vertex): 339 | """Add a vertex to the graph""" 340 | if vertex not in self.graph: 341 | self.graph[vertex] = [] 342 | 343 | def add_edge(self, vertex1, vertex2): 344 | """Add an edge to the graph""" 345 | if vertex1 in self.graph: 346 | self.graph[vertex1].append(vertex2) 347 | else: 348 | self.graph[vertex1] = [vertex2] 349 | 350 | def remove_vertex(self, vertex): 351 | """Remove a vertex from the graph""" 352 | if vertex in self.graph: 353 | del self.graph[vertex] 354 | for key in self.graph: 355 | if vertex in self.graph[key]: 356 | self.graph[key].remove(vertex) 357 | 358 | def remove_edge(self, vertex1, vertex2): 359 | """Remove an edge from the graph""" 360 | if vertex1 in self.graph: 361 | if vertex2 in self.graph[vertex1]: 362 | self.graph[vertex1].remove(vertex2) 363 | 364 | def print_graph(self): 365 | """Print the graph""" 366 | for key in self.graph: 367 | print(key, ":", self.graph[key]) 368 | 369 | def breadth_first_search(self, start): 370 | """Breadth first search""" 371 | visited = [] 372 | queue = [start] 373 | while queue: 374 | vertex = queue.pop(0) 375 | if vertex not in visited: 376 | visited.append(vertex) 377 | queue.extend(self.graph[vertex]) 378 | return visited 379 | 380 | def depth_first_search(self, node, visited=[]): 381 | """Depth first search""" 382 | if node not in visited: 383 | visited.append(node) 384 | for i in self.graph[node]: 385 | self.depth_first_search(i, visited) 386 | return visited 387 | -------------------------------------------------------------------------------- /test_py_dsa.py: -------------------------------------------------------------------------------- 1 | from py_dsa.algorithms import * 2 | from py_dsa.data_structures import * 3 | 4 | search_arr = Searching() 5 | sort_arr = Sorting() 6 | 7 | 8 | def test_py_dsa_algorithms_linear_search(): 9 | assert search_arr.linear_search([1, 2, 3, 4, 5], 4) == 3 10 | 11 | 12 | def test_py_dsa_algorithms_binary_search(): 13 | assert search_arr.binary_search([1, 2, 3, 4, 5], 3) == 2 14 | 15 | 16 | def test_py_dsa_algorithms_jump_search(): 17 | assert search_arr.jump_search([1, 2, 3, 4, 5], 5) == 4 18 | 19 | 20 | def test_py_dsa_algorithms_bubble_sort(): 21 | test_arr = [3, 1, 5, 4, 2] 22 | sort_arr.bubble_sort(test_arr) 23 | assert test_arr == [1, 2, 3, 4, 5] 24 | 25 | 26 | def test_py_dsa_algorithms_insertion_sort(): 27 | test_arr = [3, 1, 5, 4, 2] 28 | sort_arr.insertion_sort(test_arr) 29 | assert test_arr == [1, 2, 3, 4, 5] 30 | 31 | 32 | def test_py_dsa_algorithms_selection_sort(): 33 | test_arr = [3, 1, 5, 4, 2] 34 | sort_arr.selection_sort(test_arr) 35 | assert test_arr == [1, 2, 3, 4, 5] 36 | 37 | 38 | def test_py_dsa_algorithms_quick_sort(): 39 | test_arr = [3, 1, 5, 4, 2] 40 | sort_arr.quick_sort(test_arr) 41 | assert test_arr == [1, 2, 3, 4, 5] 42 | 43 | 44 | def test_py_dsa_algorithms_merge_sort(): 45 | test_arr = [3, 1, 5, 4, 2] 46 | sort_arr.merge_sort(test_arr) 47 | assert test_arr == [1, 2, 3, 4, 5] 48 | 49 | 50 | def test_py_dsa_algorithms_heap_sort(): 51 | test_arr = [3, 1, 5, 4, 2] 52 | sort_arr.heap_sort(test_arr) 53 | assert test_arr == [1, 2, 3, 4, 5] 54 | 55 | 56 | def test_py_dsa_algorithms_radix_sort(): 57 | test_arr = [3, 1, 5, 4, 2] 58 | sort_arr.radix_sort(test_arr) 59 | assert test_arr == [1, 2, 3, 4, 5] 60 | 61 | 62 | def test_py_dsa_algorithms_shell_sort(): 63 | test_arr = [3, 1, 5, 4, 2] 64 | sort_arr.shell_sort(test_arr) 65 | assert test_arr == [1, 2, 3, 4, 5] 66 | 67 | 68 | def test_py_dsa_datastructures_stack(): 69 | test_stack = Stack() 70 | test_stack.push(1) 71 | test_stack.push(2) 72 | test_stack.push(3) 73 | test_stack.push(4) 74 | test_stack.push(5) 75 | assert test_stack.pop() == 5 76 | 77 | 78 | def test_py_dsa_datastructures_queue(): 79 | test_queue = Queue() 80 | test_queue.enqueue(1) 81 | test_queue.enqueue(2) 82 | test_queue.enqueue(3) 83 | test_queue.enqueue(4) 84 | test_queue.enqueue(5) 85 | assert test_queue.dequeue() == 1 86 | 87 | 88 | def test_py_dsa_datastructures_linkedlist(): 89 | test_linkedlist = LinkedList() 90 | test_linkedlist.add_first(10) 91 | test_linkedlist.add_first(20) 92 | test_linkedlist.add_first(30) 93 | assert test_linkedlist.head.data == 30 94 | 95 | def test_py_dsa_datastructures_tree(): 96 | test_tree = Tree() 97 | test_tree.add(10) 98 | test_tree.add(20) 99 | test_tree.add(30) 100 | test_tree.remove(20) 101 | assert test_tree.height() == 2 and test_tree.root.data == 10 102 | 103 | def test_py_dsa_datastructures_graph(): 104 | test_graph = Graph() 105 | test_graph.add_edge(0, 1) 106 | test_graph.add_edge(0, 2) 107 | test_graph.add_edge(1, 2) 108 | test_graph.add_edge(2, 0) 109 | test_graph.add_edge(2, 3) 110 | test_graph.add_edge(3, 3) 111 | assert test_graph.breadth_first_search(2) == [2, 0, 3, 1] and test_graph.depth_first_search(2) == [2, 0, 1, 3] 112 | 113 | --------------------------------------------------------------------------------