├── .gitignore ├── 10_tree_example.py ├── 1_queue_example.py ├── 2_stack_example.py ├── 4_doubly_linked_list_example.py ├── 5_singly_linked_list_example.py ├── 6_selection_sort.py ├── 7_insertion_sort.py ├── 8_bubble_sort_example.py ├── 9_recursive_algorithm_example.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python,pycharm,git 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python,pycharm,git 3 | 4 | ### Git ### 5 | # Created by git for backups. To disable backups in Git: 6 | # $ git config --global mergetool.keepBackup false 7 | *.orig 8 | 9 | # Created by git when using merge tools for conflicts 10 | *.BACKUP.* 11 | *.BASE.* 12 | *.LOCAL.* 13 | *.REMOTE.* 14 | *_BACKUP_*.txt 15 | *_BASE_*.txt 16 | *_LOCAL_*.txt 17 | *_REMOTE_*.txt 18 | 19 | ### PyCharm ### 20 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 21 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 22 | 23 | # User-specific stuff 24 | .idea/**/workspace.xml 25 | .idea/**/tasks.xml 26 | .idea/**/usage.statistics.xml 27 | .idea/**/dictionaries 28 | .idea/**/shelf 29 | 30 | # AWS User-specific 31 | .idea/**/aws.xml 32 | 33 | # Generated files 34 | .idea/**/contentModel.xml 35 | 36 | # Sensitive or high-churn files 37 | .idea/**/dataSources/ 38 | .idea/**/dataSources.ids 39 | .idea/**/dataSources.local.xml 40 | .idea/**/sqlDataSources.xml 41 | .idea/**/dynamic.xml 42 | .idea/**/uiDesigner.xml 43 | .idea/**/dbnavigator.xml 44 | 45 | # Gradle 46 | .idea/**/gradle.xml 47 | .idea/**/libraries 48 | 49 | # Gradle and Maven with auto-import 50 | # When using Gradle or Maven with auto-import, you should exclude module files, 51 | # since they will be recreated, and may cause churn. Uncomment if using 52 | # auto-import. 53 | # .idea/artifacts 54 | # .idea/compiler.xml 55 | # .idea/jarRepositories.xml 56 | # .idea/modules.xml 57 | # .idea/*.iml 58 | # .idea/modules 59 | # *.iml 60 | # *.ipr 61 | 62 | # CMake 63 | cmake-build-*/ 64 | 65 | # Mongo Explorer plugin 66 | .idea/**/mongoSettings.xml 67 | 68 | # File-based project format 69 | *.iws 70 | 71 | # IntelliJ 72 | out/ 73 | 74 | # mpeltonen/sbt-idea plugin 75 | .idea_modules/ 76 | 77 | # JIRA plugin 78 | atlassian-ide-plugin.xml 79 | 80 | # Cursive Clojure plugin 81 | .idea/replstate.xml 82 | 83 | # SonarLint plugin 84 | .idea/sonarlint/ 85 | 86 | # Crashlytics plugin (for Android Studio and IntelliJ) 87 | com_crashlytics_export_strings.xml 88 | crashlytics.properties 89 | crashlytics-build.properties 90 | fabric.properties 91 | 92 | # Editor-based Rest Client 93 | .idea/httpRequests 94 | 95 | # Android studio 3.1+ serialized cache file 96 | .idea/caches/build_file_checksums.ser 97 | 98 | ### PyCharm Patch ### 99 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 100 | 101 | # *.iml 102 | # modules.xml 103 | # .idea/misc.xml 104 | # *.ipr 105 | 106 | # Sonarlint plugin 107 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 108 | .idea/**/sonarlint/ 109 | 110 | # SonarQube Plugin 111 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 112 | .idea/**/sonarIssues.xml 113 | 114 | # Markdown Navigator plugin 115 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 116 | .idea/**/markdown-navigator.xml 117 | .idea/**/markdown-navigator-enh.xml 118 | .idea/**/markdown-navigator/ 119 | 120 | # Cache file creation bug 121 | # See https://youtrack.jetbrains.com/issue/JBR-2257 122 | .idea/$CACHE_FILE$ 123 | 124 | # CodeStream plugin 125 | # https://plugins.jetbrains.com/plugin/12206-codestream 126 | .idea/codestream.xml 127 | 128 | # Azure Toolkit for IntelliJ plugin 129 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij 130 | .idea/**/azureSettings.xml 131 | 132 | ### Python ### 133 | # Byte-compiled / optimized / DLL files 134 | __pycache__/ 135 | *.py[cod] 136 | *$py.class 137 | 138 | # C extensions 139 | *.so 140 | 141 | # Distribution / packaging 142 | .Python 143 | build/ 144 | develop-eggs/ 145 | dist/ 146 | downloads/ 147 | eggs/ 148 | .eggs/ 149 | lib/ 150 | lib64/ 151 | parts/ 152 | sdist/ 153 | var/ 154 | wheels/ 155 | share/python-wheels/ 156 | *.egg-info/ 157 | .installed.cfg 158 | *.egg 159 | MANIFEST 160 | 161 | # PyInstaller 162 | # Usually these files are written by a python script from a template 163 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 164 | *.manifest 165 | *.spec 166 | 167 | # Installer logs 168 | pip-log.txt 169 | pip-delete-this-directory.txt 170 | 171 | # Unit test / coverage reports 172 | htmlcov/ 173 | .tox/ 174 | .nox/ 175 | .coverage 176 | .coverage.* 177 | .cache 178 | nosetests.xml 179 | coverage.xml 180 | *.cover 181 | *.py,cover 182 | .hypothesis/ 183 | .pytest_cache/ 184 | cover/ 185 | 186 | # Translations 187 | *.mo 188 | *.pot 189 | 190 | # Django stuff: 191 | *.log 192 | local_settings.py 193 | db.sqlite3 194 | db.sqlite3-journal 195 | 196 | # Flask stuff: 197 | instance/ 198 | .webassets-cache 199 | 200 | # Scrapy stuff: 201 | .scrapy 202 | 203 | # Sphinx documentation 204 | docs/_build/ 205 | 206 | # PyBuilder 207 | .pybuilder/ 208 | target/ 209 | 210 | # Jupyter Notebook 211 | .ipynb_checkpoints 212 | 213 | # IPython 214 | profile_default/ 215 | ipython_config.py 216 | 217 | # pyenv 218 | # For a library or package, you might want to ignore these files since the code is 219 | # intended to run in multiple environments; otherwise, check them in: 220 | # .python-version 221 | 222 | # pipenv 223 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 224 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 225 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 226 | # install all needed dependencies. 227 | #Pipfile.lock 228 | 229 | # poetry 230 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 231 | # This is especially recommended for binary packages to ensure reproducibility, and is more 232 | # commonly ignored for libraries. 233 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 234 | #poetry.lock 235 | 236 | # pdm 237 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 238 | #pdm.lock 239 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 240 | # in version control. 241 | # https://pdm.fming.dev/#use-with-ide 242 | .pdm.toml 243 | 244 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 245 | __pypackages__/ 246 | 247 | # Celery stuff 248 | celerybeat-schedule 249 | celerybeat.pid 250 | 251 | # SageMath parsed files 252 | *.sage.py 253 | 254 | # Environments 255 | .env 256 | .venv 257 | env/ 258 | venv/ 259 | ENV/ 260 | env.bak/ 261 | venv.bak/ 262 | 263 | # Spyder project settings 264 | .spyderproject 265 | .spyproject 266 | 267 | # Rope project settings 268 | .ropeproject 269 | 270 | # mkdocs documentation 271 | /site 272 | 273 | # mypy 274 | .mypy_cache/ 275 | .dmypy.json 276 | dmypy.json 277 | 278 | # Pyre type checker 279 | .pyre/ 280 | 281 | # pytype static type analyzer 282 | .pytype/ 283 | 284 | # Cython debug symbols 285 | cython_debug/ 286 | 287 | # PyCharm 288 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 289 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 290 | # and can be added to the global gitignore or merged into this file. For a more nuclear 291 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 292 | #.idea/ 293 | 294 | ### Python Patch ### 295 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 296 | poetry.toml 297 | 298 | # ruff 299 | .ruff_cache/ 300 | 301 | # LSP config files 302 | pyrightconfig.json 303 | 304 | # End of https://www.toptal.com/developers/gitignore/api/python,pycharm,git 305 | 306 | /.idea 307 | -------------------------------------------------------------------------------- /10_tree_example.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | 4 | class TreeNode: 5 | 6 | def __init__(self, value: Any, parent: Any | None = None) -> None: 7 | self.parent = parent 8 | self.value = value 9 | self.__children = [] 10 | 11 | def add_child(self, value) -> None: 12 | new_child = TreeNode(value=value, parent=self) 13 | self.__children.append(new_child) 14 | 15 | def children(self) -> list: 16 | return self.__children 17 | 18 | def get_all_children(self) -> list: 19 | all_children = [] 20 | depth_count = 0 21 | for child in self.__children: 22 | depth_count += 1 23 | all_children.append(child) 24 | all_children.extend(child.get_all_children()) 25 | return all_children 26 | 27 | def depth(self) -> int: 28 | count = 0 29 | node = self 30 | while node and node.parent: 31 | node = node.parent 32 | count += 1 33 | return count 34 | 35 | def get_depth(self) -> int: 36 | if not self.__children: 37 | return 0 38 | else: 39 | child_depths = [child.get_depth() for child in self.__children] 40 | return max(child_depths) + 1 41 | 42 | def get_height(self) -> int: 43 | if not self.__children: 44 | return 0 45 | else: 46 | child_heights = [child.get_height() for child in self.__children] 47 | return max(child_heights) + 1 48 | 49 | def remove(self) -> None: 50 | parent = self.parent 51 | children = self.children() 52 | if parent: 53 | parent.__children.extend(children) 54 | parent.__children.remove(self) 55 | del self 56 | 57 | 58 | class Tree: 59 | 60 | def __init__(self, value: Any) -> None: self.__root = self.__create_root(value=value) 61 | 62 | @staticmethod 63 | def __create_root(value: Any) -> TreeNode: return TreeNode(value=value) 64 | 65 | @property 66 | def root(self): 67 | return self.__root 68 | 69 | def __str__(self): return f'root name: {self.__root.value}' 70 | 71 | 72 | if __name__ == '__main__': 73 | tree = Tree(value='root') 74 | tree.root.add_child(value='first_child') 75 | tree.root.add_child(value='second_child') 76 | print(tree.root.children()) 77 | tree.root.children()[0].add_child(value='third_children') 78 | 79 | print(tree.root.get_all_children()) 80 | print(tree.root.get_all_children()[0].children()[0].depth()) 81 | print(tree.root.get_all_children()[0].remove()) 82 | print(tree.root.get_all_children()) 83 | print(tree.root.get_depth()) 84 | -------------------------------------------------------------------------------- /1_queue_example.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | 4 | class Queue(list): 5 | 6 | def add(self, value) -> None: 7 | self.append(value) 8 | 9 | def size(self) -> int: 10 | return len(self) 11 | 12 | def get_first(self) -> Any: 13 | try: 14 | return self[0] 15 | except IndexError: 16 | raise IndexError('Queue index out of range') 17 | 18 | def get_last(self) -> Any: 19 | try: 20 | return self[-1] 21 | except IndexError: 22 | raise IndexError('Queue index out of range') 23 | 24 | def take(self) -> Any: 25 | first = self.get_first() 26 | del self[0] 27 | return first 28 | 29 | def head(self) -> Any: 30 | return self.get_first() 31 | 32 | def tail(self) -> Any: 33 | return self.get_last() 34 | 35 | 36 | if __name__ == '__main__': 37 | pass 38 | -------------------------------------------------------------------------------- /2_stack_example.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | 4 | class Stack(list): 5 | 6 | def add(self, value) -> None: 7 | self.append(value) 8 | 9 | def size(self) -> int: 10 | return len(self) 11 | 12 | def get_last(self) -> Any: 13 | try: 14 | return self[-1] 15 | except IndexError: 16 | raise IndexError('Stack index out of range') 17 | 18 | def pop(self) -> Any: 19 | try: 20 | return super().pop() 21 | except IndexError: 22 | raise IndexError('pop from empty stack.') 23 | 24 | def top(self) -> Any: 25 | return self.get_last() 26 | 27 | 28 | if __name__ == '__main__': 29 | pass 30 | -------------------------------------------------------------------------------- /4_doubly_linked_list_example.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | 4 | class Node: 5 | 6 | def __init__(self, data: Any) -> None: 7 | self.data = data 8 | self.next = None 9 | self.pre = None 10 | 11 | def __str__(self): 12 | return f'data: {self.data}' 13 | 14 | 15 | class DoubleLinkedList: 16 | 17 | def __init__(self) -> None: 18 | self.__head = None 19 | self.__tail = None 20 | self.__size = 0 21 | 22 | def size(self) -> int: 23 | return self.__size 24 | 25 | def __update_size(self) -> None: 26 | self.__size += 1 27 | 28 | def insure_index(self, index: int) -> None: 29 | if index > self.size() - 1: 30 | raise IndexError 31 | 32 | def head(self) -> Node: 33 | return self.__head 34 | 35 | def tail(self) -> Node: 36 | return self.__tail 37 | 38 | def __set_head(self, node: Node) -> None: 39 | self.__head = node 40 | 41 | def __set_tail(self, node: Node) -> None: 42 | self.__tail = node 43 | 44 | def append(self, data: Any) -> None: 45 | new_node = Node(data=data) 46 | 47 | if self.head() is None and self.tail() is None: 48 | self.__set_head(node=new_node) 49 | self.__set_tail(node=new_node) 50 | self.__update_size() 51 | return 52 | 53 | self.tail().next = new_node 54 | new_node.pre = self.tail() 55 | self.__set_tail(node=new_node) 56 | self.__update_size() 57 | 58 | def get_node(self, index: int) -> Node: 59 | self.insure_index(index=index) 60 | 61 | count = 0 62 | current = self.head() 63 | 64 | while index != count: 65 | current = current.next 66 | count += 1 67 | 68 | return current 69 | 70 | def get(self, index: int) -> Any: 71 | node = self.get_node(index=index) 72 | return node.data 73 | 74 | def remove(self, index: int) -> None: 75 | current = self.get_node(index=index) 76 | 77 | if current.pre: 78 | current.pre.next = current.next 79 | else: 80 | self.__set_head(node=current.next) 81 | 82 | if current.next: 83 | current.next.pre = current.pre 84 | else: 85 | self.__set_tail(node=current.pre) 86 | 87 | del current 88 | 89 | def insert(self, index: int, data: Any) -> None: 90 | current = self.get_node(index=index) 91 | 92 | new_node = Node(data=data) 93 | new_node.next = current 94 | new_node.pre = current.pre 95 | 96 | if current.next: 97 | pass 98 | 99 | if current.pre: 100 | current.pre.next = new_node 101 | else: 102 | self.__set_head(node=new_node) 103 | 104 | self.__update_size() 105 | 106 | def pop(self): 107 | if self.tail(): 108 | if self.tail().pre: 109 | self.tail().pre.next = None 110 | self.__set_tail(node=self.tail().pre) 111 | data = self.tail().data 112 | else: 113 | data = self.tail().data 114 | self.__head = None 115 | self.__tail = None 116 | 117 | return data 118 | 119 | return ValueError('Double linked list is empty.') 120 | 121 | def __str__(self) -> str: 122 | my_list = [] 123 | 124 | current = self.head() 125 | 126 | while current: 127 | my_list.append(current.data) 128 | current = current.next 129 | 130 | return str(my_list) 131 | 132 | 133 | if __name__ == '__main__': 134 | d_list = DoubleLinkedList() 135 | 136 | d_list.append(data='a') 137 | d_list.append(data='b') 138 | d_list.append(data='c') 139 | 140 | d_list.insert(index=1, data='d') 141 | d_list.insert(index=1, data='j') 142 | d_list.remove(index=1) 143 | d_list.remove(index=1) 144 | d_list.remove(index=1) 145 | 146 | print(d_list) 147 | 148 | # print(d_list.get(index=0)) 149 | print(d_list.pop()) 150 | print(d_list) 151 | print(d_list.pop()) 152 | print(d_list.pop()) 153 | -------------------------------------------------------------------------------- /5_singly_linked_list_example.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Optional 2 | 3 | 4 | class Node: 5 | def __init__(self, data): 6 | self.data = data 7 | self.next = None 8 | 9 | def __str__(self): 10 | return f'data: {self.data}' 11 | 12 | 13 | class SinglyLinkedList: 14 | 15 | def __init__(self) -> None: 16 | self.__head = None 17 | self.__tail = None 18 | self.__size = 0 19 | 20 | def size(self) -> int: 21 | return self.__size 22 | 23 | def __update_size(self) -> None: 24 | self.__size += 1 25 | 26 | def insure_index(self, index: int) -> None: 27 | if index > self.size() - 1: 28 | raise IndexError 29 | 30 | def head(self) -> Node: 31 | return self.__head 32 | 33 | def tail(self) -> Node: 34 | return self.__tail 35 | 36 | def __set_head(self, node: Node) -> None: 37 | self.__head = node 38 | 39 | def __set_tail(self, node: Node) -> None: 40 | self.__tail = node 41 | 42 | def append(self, data: Any) -> None: 43 | new_node = Node(data=data) 44 | 45 | if self.head() is None: 46 | self.__set_head(node=new_node) 47 | 48 | if self.tail() is None: 49 | self.__set_tail(node=new_node) 50 | else: 51 | self.tail().next = new_node 52 | self.__set_tail(node=new_node) 53 | 54 | self.__update_size() 55 | 56 | def get_node(self, index: int) -> Node: 57 | self.insure_index(index=index) 58 | 59 | count = 0 60 | current = self.head() 61 | 62 | while index != count: 63 | current = current.next 64 | count += 1 65 | 66 | return current 67 | 68 | def get(self, index: int) -> Any: 69 | node = self.get_node(index=index) 70 | return node.data 71 | 72 | def set(self, index: int, data: Any) -> None: 73 | self.insure_index(index=index) 74 | 75 | count = 0 76 | current = self.head() 77 | 78 | while index != count: 79 | current = current.next 80 | count += 1 81 | 82 | current.data = data 83 | 84 | def remove(self, index: int) -> None: 85 | self.insure_index(index=index) 86 | 87 | count = 0 88 | previous = None 89 | current = self.head() 90 | 91 | while index != count: 92 | previous = current 93 | current = current.next 94 | count += 1 95 | 96 | if previous: 97 | previous.next = current.next 98 | else: 99 | self.__set_head(current.next) 100 | del current 101 | 102 | def pop(self, index: int = 0) -> Any: 103 | self.insure_index(index=index) 104 | 105 | count = 0 106 | previous = None 107 | current = self.head() 108 | 109 | while index != count: 110 | previous = current 111 | current = current.next 112 | count += 1 113 | 114 | if previous: 115 | previous.next = current.next 116 | else: 117 | self.__set_head(node=current.next) 118 | 119 | data = current.data 120 | del current 121 | return data 122 | 123 | def __str__(self) -> str: 124 | my_list = [] 125 | 126 | current = self.head() 127 | 128 | while current: 129 | my_list.append(current.data) 130 | current = current.next 131 | 132 | return str(my_list) 133 | 134 | 135 | if __name__ == '__main__': 136 | singly_list = SinglyLinkedList() 137 | singly_list.append(data='eggs') 138 | singly_list.append(data='bread') 139 | singly_list.append(data='cereal') 140 | singly_list.set(index=0, data='boobs') 141 | # singly_list.remove(3) 142 | singly_list.pop() 143 | singly_list.pop() 144 | print(singly_list) 145 | 146 | -------------------------------------------------------------------------------- /6_selection_sort.py: -------------------------------------------------------------------------------- 1 | unsorted_list = [10, 5, 8, 4, 6, 1, 3, 7, 2, 9, -1] 2 | 3 | print(unsorted_list) 4 | 5 | 6 | for i in range(len(unsorted_list)): 7 | min_index = i 8 | for j in range(i + 1, len(unsorted_list)): 9 | if unsorted_list[j] < unsorted_list[min_index]: 10 | min_index = j 11 | 12 | (unsorted_list[i], unsorted_list[min_index]) = (unsorted_list[min_index], unsorted_list[i]) 13 | 14 | print(unsorted_list) 15 | -------------------------------------------------------------------------------- /7_insertion_sort.py: -------------------------------------------------------------------------------- 1 | unsorted_list = [10, 5, 8, 4, 6, 1, 3, 7, 2, 9, -1] 2 | 3 | 4 | for i in range(1, len(unsorted_list)): 5 | 6 | temp = unsorted_list[i] 7 | 8 | j = i - 1 9 | while j >= 0 and temp < unsorted_list[j]: 10 | unsorted_list[j + 1] = unsorted_list[j] 11 | j -= 1 12 | unsorted_list[j + 1] = temp 13 | 14 | 15 | print(unsorted_list) 16 | -------------------------------------------------------------------------------- /8_bubble_sort_example.py: -------------------------------------------------------------------------------- 1 | unsorted_list = [10, 5, 8, 4, 6, 1, 3, 7, 2, 9, -1] 2 | 3 | print(unsorted_list) 4 | 5 | 6 | for _ in range(1, len(unsorted_list)): 7 | for j in range(0, len(unsorted_list) - 1): 8 | if unsorted_list[j] > unsorted_list[j + 1]: 9 | unsorted_list[j], unsorted_list[j + 1] = unsorted_list[j + 1], unsorted_list[j] 10 | 11 | 12 | print(unsorted_list) 13 | -------------------------------------------------------------------------------- /9_recursive_algorithm_example.py: -------------------------------------------------------------------------------- 1 | def rec_factorial(number) -> int: 2 | if number == 1: 3 | return number 4 | 5 | return number * rec_factorial(number - 1) 6 | 7 | 8 | print(rec_factorial(number=1000)) 9 | 10 | 11 | def sum_int_list(array: list, length: int) -> int: 12 | if length <= 0: 13 | return 0 14 | return array[length - 1] + sum_int_list(array=array, length=length - 1) 15 | 16 | 17 | def find_number(array: list, number: int) -> bool: 18 | if len(array) == 1: 19 | return bool(array[0] == number) 20 | 21 | half_len = len(array) // 2 22 | if array[half_len] == number: 23 | return True 24 | 25 | if array[half_len] < number: 26 | return find_number(array=array[half_len:], number=number) 27 | else: 28 | return find_number(array=array[0:half_len], number=number) 29 | 30 | 31 | def find_max(array: list): 32 | if len(array) == 1: 33 | return array[0] 34 | 35 | half_len = len(array) // 2 36 | first_half = find_max(array=array[:half_len]) 37 | second_half = find_max(array=array[half_len:]) 38 | 39 | return max([first_half, second_half]) 40 | 41 | 42 | if __name__ == '__main__': 43 | # print(find_number([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], number=5)) 44 | print(find_max(array=[1, 2, 5, 4, 88, 41, 6, 9, 0, 11, -666])) 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![data-structures.png](https://miro.medium.com/v2/resize:fit:1200/1*KpDOKMFAgDWaGTQHL0r70g.png) 4 |
5 | 6 | 1-Data Structures 7 | 8 | 2-Array 9 | 10 | 3-Array List 11 | 12 | 4-Queue 13 | 14 | 5-Stack 15 | 16 | 6-Python list 17 | 18 | 7-How list work in python? 19 | 20 | 8-Introducing the linked list 21 | 22 | 9-Types of linked list 23 | 24 | 10-Singly linked list 25 | 26 | 11-Doubly linked list 27 | 28 | 12-Sorting algorithms 29 | 30 | 13-Selection sort 31 | 32 | 14-Insertion sort 33 | 34 | 15-Bubble sort 35 | 36 | 37 |
38 | 39 |

ساختمان داده (Data Structure) چیست؟

40 | 41 | تا الان هممون حداقل یک مفهوم سطحی از data structure رو یاد گرفتیم. 42 | توی ویکی پدیا و اکثر وب سایت ها همون توضیح کوتاه رو میده. 43 | 44 | اما بیاید یکم عمیق تر شیم 45 | 46 | خب Structure که مشخصه یک آرایش، ساختار، سازمان دهی کردن هستش. 47 | 48 | دیتا (Data) چیه؟ 49 | 50 | برای درک دیتا بهتره کمی درباره ساز و کار کامپیوتر بدونیم. 51 | 52 | قلب تپنده کامپیوتر CPU هستش 53 | سی پی یو یک چیزی داره به اسم ALU کار اصلی ایشون در واقع انجام محاسبه ها و انجام عملیات های منطقی روی داده ها توی پردازنده اصلی یا همون CPU هستش. 54 | 55 | داده ها از کجا میان ؟ 56 | داده ها از main memory یا همون رم سیستم میان. 57 | تا اونجا که خودتون در جریانید کامپیوتر ها باینری هستن پس اینطور در نظر بگیرید ما کلی صفر و یک توی رم داریم. 58 | 59 | نکته ای که باید توجه کنید اینه کامپیوتر ذاتا فقط عدد میفهمه 60 | 61 | اگه شما با هر زبان برنامه نویسی کار کرده باشید یک چیزی وجود داره به اسم انواع داده (data types) 62 | 63 | هر زبان برنامه نویسی یک سری انواع داده اصلی رو میده. 64 | 65 | اولین نوع داده ای که میشه اشاره کرد Boolean هستش. اینطوریه که اگه صفر بود false هستش و اگه غیر صفر بود true مثلا یک باشه true هستش 66 | کامپیوتر هم اینو خیلی راحت میفهمه، بیسیک ترین نوع داده هستش که با یه بیت هم میشه نمایشش داد 67 | 68 | انواع اعداد رو داریم مثلا int، short, long 69 | 70 | اعداد اعشاری رو داریم مثل float و double 71 | 72 | خب تا اینجا هر نوع تایپی که اشاره کردیم عدد بود و همچنین کل چیزی که CPU میفهمه اعداد هستش 73 | 74 | ولی خب سوال اینجاست. ما که فقط اعداد نداریم مثلا چیزی به اسم رشته (String) رو داریم. پس چطور باهاش کار می کنیم؟ 75 | 76 | رشته چطوری ساخته میشه؟ 77 | رشته به این شکل ساخته میشه که میایم بر اساس هر حرف عددی رو تعریف میکنیم. اگه یادتون باشه چیزی به اسم جدول ascii codes داریم که توی این جدول مشخص شده مثلا A میشه 65 78 | پس چیزی که توی حافظه برای رشته نوشتیم در واقع عدد هستش 79 | 80 | اگه همه چی عدد هستش. چجوری داده های پیچیده تر رو میسازیم؟ 81 | 82 | برای ساخت داده های پیچیده تر میتونیم از composition یعنی ترکیب کردن استفاده کنیم. 83 | یعنی میایم چند تا نوع داده رو با هم ترکیب میکنیم یک نوع داده پیچیده تر رو میسازیم. 84 | 85 | فرض کنید میخوایم اطلاعات دانش آموز رو ذخیره کنیم. برای اینکار نیازه که ما چند توع دیتا تایپ رو با هم ترکیب کنیم تا بتونیم دانشجو رو بسازیم. مثلا دانشجو نام داره که String هستش،‌کد دانشجویی داره و اطلاعات دیگه. پس چیزی که ما میسازیم به این 86 | 87 |
88 | 89 | ```Java 90 | name: str 91 | Student code: int 92 | age: int 93 | ``` 94 | 95 |
96 | تشکیل می شود. توی اینجا ما به Student میگیم Struct که البته توی زمان C بیشتر می شنوید. 97 | 98 | Aggregation: 99 | 100 | یک روش دیگه هم برای مدریت و ساخت انواع جدید، aggregation (البته چند روش دیگه هم داریم) هست. توی Aggregation یک سری از آبجکت ها، اشیا رو کنار هم قرار میدیم تا یک چیز بزرگ تری رو بسازیم 101 | 102 | مثلا تعدادی دانشجو رو کنار هم قرار میدیم و کلاس رو میسازیم 103 | 104 | این آبجکت هایی که کنار هم دیگه میذاریم خودشون بصورت مستقل شخصیت دارن مثلا شما دانشجو رو فرض کنید. بصورت جداگانه هم شخصیت داره اما حالا ما چیزی داریم به اسم کلاس که داخل خودش دانشجو هایی 105 | که توی کلاس هستن رو داره. به این کار که ما آبجکت های مستقل رو کنار هم میذاریم میگن تجمیع و یا Aggregation کردن. 106 | 107 | حالا وقتی که میایم آبجکت ها رو کنار هم قرار میدیم به ساختار و آرایشی نیاز داریم که بتونیم این آبجکت ها رو کنار همدیگه قرار بدیم. اینجاست که ساختمان داده ها (Data structures) به دادمون میرسه🥰 108 | 109 | در واقع اینجا نیاز اینکه ما باید ساختار داده ای و یا ساختار های داده داشته باشیم حس شد و داستان ما از اینجا شروع میشه. 110 | 111 |
112 |

انواع ساختمان داده

113 | ما انواع مختلفی از ساختمان داده داریم که هر کدومشون در جای مناسب کاربرد دارن. در ادامه چند نوع ساختمان داده رو بررسی میکنیم و مثال از نحوه پیاده 114 | کردنش توی پایتون براتون میزنم. 115 |
116 |

ساختمان داده آرایه(Array)

117 | پایه ای ترین نوع ساختمان داده هستش. 118 | توی آرایه ما می تونیم set کنیم چیزی رو اضافه کنیم و یا get کنیم و آبجکتی رو بگیریم و تمام. 119 | 120 | مثلا: 121 |
122 | 123 | ```Java 124 | [1, 2, 5, 9] 125 | 126 | set(1, 9) ---> [1, 9, 5, 9] 127 | 128 | get(0) -----> 1 129 | ``` 130 | 131 |
132 | همچنین باید lenght آرایه رو موقع تعریف کردنش مشخص کنیم که چقدر طول داره. 133 | 134 | آرایه ها خیلی کاربردی هستن ولی یک سری محدودیت ها و معایبی رو دارن. مثلا: 135 | 136 | شما باید موقع تعریف کردنش مشخص کنید که چقدر طول داره. 137 | اگه بخوایم یک عنصری رو توی index مشخص شده جا بدیم در واقع insert کنیم، قابلیتش رو نداره یا اگه بخوایم عنصری رو از index مشخص پاک کنیم بازم قابلیتش رو نداره. 138 | 139 | برای حل این مشکلات ساختمان داده ای به اسم Array list و یا تو بعضی از زبان های برنامه نویسی بهش «وکتور» میگیم که توی پایتون بهش لیست میگیم. 140 | 141 |

ساختمان داده Array List

142 | ساختمان داده Array list رو میتونیم با استفاده از Array پیاده کنیم. 143 | 144 | یک Array list این ویژگی هارو باید داشته باشه: 145 | 146 | بتونیم insert, remove, add, get_size رو انجام بدیم. 147 | 148 | همونطور که اشاره کردیم آرایه در ابتدا اینکه چقدر طول داشته باشه رو از ما میگیره و قابل تغییر دادن هم که نیسن. پس احتمالا حدس زدید کاری که انجام میدیم اینه که 149 | توی Array list اول از همه بیشتر از فضایی که نیازه رو در نظر میگیریم و خالی قرار میدیمش. 150 | بعد کاری که میکنیم اینه وقتی چیزی add میشه ما میایم تو آخر آرایه اون رو اضافه میکنیم. 151 | 152 | اینکه آخر آرایه بلاک چندم هستش رو با استفاده از size که مشخص کردیم میفهمیم. یعنی مثلا اگه یک لیست با سه عنصر بوجود آوردیم سایزش میشه سه و اگه چیزی رو add کنیم با توجه به اینکه 153 | سایز ما سه بوده پس توی size + 1 ذخیرش میکنیم. 154 | پس در کل نحوه ساخت یک آرایه لیست به این صورت است. 155 | 156 | توی Array list ما یک سری مشکلات رو هم داریم. مثلا خیلی از وقتا یا O(n) 157 | مواجه میشویم. برای مثال اگه size لیست ما به اندازه طولی که مشخص کرده بودیم رسید. اونوقت باید بیایم کل داده رو توی یک Array دیگه که طول بزرگ تری دارد کپی کنیم و یا زمانی که index صفر را remove میکنیم اتفاقی که می افتد این است تمامی عناصر یک index 158 | عقب تر می روند که کار ساده ای نیست. 159 | 160 | نحوه پیاده سازی یک Array List رو بصورت عملی انجام نمیدم. بنظرم توضیحات کافیه 161 | 162 | 163 |

صف (Queue)

164 | وقتی حرف از صف زده میشه همیشه یاد صف نونوایی بیوفتید. هر کی اول وارد شده، اولم نون میگیره. توی صف ما میگیم FIFO(First input first output) 165 | کسی که اول وارد شده اول هم خارج میشه. 166 | اما خب صف رو چجوری میتونیم پیادش کنیم؟ 167 | 168 | صف رو میتونیم با استفاده از Array list پیادش کنیم. در واقع میایم از دیزاین پترن Adapter استفاده میکنیم و لیست رو سبک و سنگینش میکنیم تا 169 | برای ما کاربرد صف رو داشته باشه 170 | 171 | صف یه سری متد ها رو باید داشته باشه. مثل: 172 | 173 | متد add, take, head, tail, size 174 | 175 | متد add برای اضافه کردن به صف، متد take برای گرفتن اولین نفر و برگردوندنش و حذف کردنش از صف، 176 | متد head برای نشون دادن اولین نفری که توی صفحه و متد tail برای نشون دادن آخرین نفری که توی صف قرار داره و متد size برای دیدن اینکه 177 | صف ما حالا چند نفر منتظرن داخلش. 178 | 179 | نحوه پیاده کردن یک صف ساده توی پایتون با استفاده از لیست: 180 |
181 | 182 | ```Python 183 | from typing import Any 184 | 185 | 186 | class Queue(list): 187 | 188 | def add(self, value) -> None: 189 | self.append(value) 190 | 191 | def size(self) -> int: 192 | return len(self) 193 | 194 | def get_first(self) -> Any: 195 | try: 196 | return self[0] 197 | except IndexError: 198 | raise IndexError('Queue index out of range') 199 | 200 | def get_last(self) -> Any: 201 | try: 202 | return self[-1] 203 | except IndexError: 204 | raise IndexError('Queue index out of range') 205 | 206 | def take(self) -> Any: 207 | first = self.get_first() 208 | del self[0] 209 | return first 210 | 211 | def head(self) -> Any: 212 | return self.get_first() 213 | 214 | def tail(self) -> Any: 215 | return self.get_last() 216 | ``` 217 | 218 |
219 |

پشته (Stack)

220 | پشته دقیقا برعکس صف هستش و یه جورایی ناعادلانست 😂 221 | 222 | در پشته (Stack) 223 | هر کی اول بره، اول بیرون نمیاد. هر کی آخر بره اول بیرون میاد. توی پشته میگیم LILO(Last input last output) 224 | یعنی همون هر کی آخر بره اول بیرون میاد 225 | 226 | یک سطح رو در نظر بگیرید که داخلش دیتا رو میریزیم. خب مشخصا وقتی میخوایم چیزی برداریم اونی که آخر وارد شده در دسترس هستش. 227 | 228 | برای پیاده سازی پشته مثل صف دوباره از لیست استفاده می کنیم. متد هایی که نیازه داشته باشیم اینا هستن: 229 | 230 | add, size, pop, top 231 | 232 | متد add برای اضافه کردن توی پشته، متد size برای دیدن سایز، متد pop برای گرفتن و حذف کردن آخرین استفاده میشه و متد top برای دیدن 233 | اونی که آخر از همه وارد شد. 234 | 235 | پیاده کردن یک پشته ساده با استفاده از لیست توی پایتون: 236 | 237 |
238 | 239 | 240 | ```Python 241 | from typing import Any 242 | 243 | 244 | class Stack(list): 245 | 246 | def add(self, value) -> None: 247 | self.append(value) 248 | 249 | def size(self) -> int: 250 | return len(self) 251 | 252 | def get_last(self) -> Any: 253 | try: 254 | return self[-1] 255 | except IndexError: 256 | raise IndexError('Stack index out of range') 257 | 258 | def pop(self) -> Any: 259 | try: 260 | return super().pop() 261 | except IndexError: 262 | raise IndexError('pop from empty stack.') 263 | 264 | def top(self) -> Any: 265 | return self.get_last() 266 | ``` 267 | 268 |
269 |

دک (dequeue)

270 | دک رو میشه اینطور در نظر گرفت که صف و پشته رو با هم ترکیب کردیم. یعنی هم میتونیم مانند صف هر کی اول وارد شده، دریافتش کنیم و هم میتونیم مانند پشته هر کی که آخر وارد شده، دریافتش کنیم. 271 | 272 |

نکته:

273 | وقتی که داریم از Array List برای صف و پشته استفاده میکنیم یه سری نکات هستش که باید دربارشون تصمیم بگیریم. 274 | 275 | توی صف وقتی take میکنیم و اولین کسی که وارد شده رو میگریم این اتفاق میوفته: 276 | 277 | اونی که ایندکسش مساوی با 0 هستش رو میگیریم. 278 | وقتی که حذف میکنیم همه عناصر یک ایندکس به عقب میرن. مثلا اگه صف ما سایزش یه میلیون باشه خودتون فکر کنید چقدر اینکار سربار اضافی بوجود میاره. 279 | 280 | برای حل این مشکل ما میتونیم از لیست های پیوندی استفاده کنیم.🥳 281 | 282 | بعد از توضیح ساختار لیست پایتون میریم سراغش... 283 | 284 |

لیست پایتون

285 | ساختار داده ی لیست پایتون بصورت Dynamic Array و یا همون Array list هستند. به همین دلیل در مثال بالا که برای ساخت صف و پشته از لیست پایتون استفاده کردیم گفتبم که به مشکل o(n) 286 | برخورد میکنیم. چون Dynamic array ها بصورت متوالی دیتا رو ذخیره میکنن. 287 | 288 | یعنی وقتی که ما میایم ایندکس 0 رو حذف میکنیم کل عناصر لیست باید یک ایندکس به عقب بروند و یا موقعی که چیزی رو insert می کنیم باید عناصر بعد از آن ایندکس که اضافه کردیم یک ایندکس به جلو بروند و خب این خوب نیست. 289 | 290 | البته این رو هم در نظر بگیرید در تعداد پایین و سایز کم لیست ها خیلیم عالی هستند و خیلی کاربرد داره اما در big data پیشنهاد نمی شود. 291 | 292 | pointer: 293 | 294 | پایتون در واقع داخل لیست ها دیتا رو ذخیره نمی کته. کاری که می کنه اینه pointer اون دیتا رو ذخیره میکنه یعنی اشاره گری که فضای دیتا رو نشون میده. اینکار سبب میشه که یه سری مزیت هارو لیست های پایتون بدست بیاره 295 | 296 | در کتاب Hands on data structures and algorithms with Python اینگونه توضیح داده: 297 | 298 | Contrary to arrays, pointer structures are lists of items that can be spread out in memory. 299 | This is because each item contains one or more links to other items in the structure. The 300 | types of these links are dependent on the type of structures we have. If we are dealing with 301 | linked lists, then we will have links to the next (and possibly previous) items in the 302 | structure. In the case of a tree, we have parent-child links as well as sibling links. 303 | There are several benefits to pointer structures. First of all, they don't require sequential 304 | storage space. Secondly, they can start small and grow arbitrarily as you add more nodes to 305 | the structure. However, this flexibility in pointers comes at a cost. We need additional space 306 | to store the address. For example, if you have a list of integers, each node is going to take 307 | up space by storing an integer, as well as an additional integer for storing the pointer to the 308 | next node 309 | 310 |

لیست پایتون چگونه عمل می کند؟

311 | در این قسمت با چند مثال نحوه کارکرد لیست رو در Cpython بررسی میکنیم. 312 | 313 | قبل از هرچیزی باید معنی size لیست و allocated slots رو بدونید. size در واقع همون مقدار عناصر داخل لیستمون هستش که با len() میشه بدستش آورد. 314 | اما allocated slots مقدار فضایی که موقع ساخن array اختصاص دادیم هستش. همونطور که میدونید لیست پایتون در واقع یک Dynamic array هستش که در نهایت به array می رسد. 315 | 316 | ساختن لیست: 317 | فرض کنید ما یک لیست حالی میسازیم. در این صورت allocated size ما مساوی با 0 است. 318 | 319 | عملیات append: 320 | 321 | اول از همه چک میشود که size لیست از allocated کوچک تر باشد و اگر مساوی بود آرایه رو resize میکنه و یک آرایه بزرگ تر می سازد. 322 | برای resize کردن یک فرمولی براش داره مثل زیر: 323 | 324 | The growth pattern of the list is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, … 325 | 326 | پس الان لیست ما allocated slots مساوی با 4 هستش. یعنی اگه عملیات append رو تا 4 بار انجام بدیم بصورت o(1) 327 | انجام میشود 328 | 329 |
330 | 331 | ![data-structures.png](http://www.laurentluce.com/images/blog/list/list.png) 332 | 333 |
همچنین میتونیم 334 | append رو سه بار دیگم با o(1) 335 | ادامه بدیم 336 |
337 | 338 | ![data-structures.png](http://www.laurentluce.com/images/blog/list/list_4.png) 339 | 340 |
341 | عملیات insert: 342 | 343 | لیست ما الان allocated مساوی با 4 هستش و size هم مساوی با چهار. 344 | در عملیات insert که میخوایم انجام بدیم اول از همه لیست ما resize میشه و allocated slots مساوی با 8 میشه و سپس عملیات insert به شکل زیر انجام میشه. 345 | 346 | 347 |
348 | 349 | ![data-structures.png](http://www.laurentluce.com/images/blog/list/list_insert.png) 350 | 351 |
352 | همانطور که در عکس مشخص است. در insert کردن عناصری که بعد از چیزی که insert کردیم قرار دارند، یک ایندکس به جلو میروند و time complexity ما o(n) 353 | است. 354 | 355 | 356 | عملیات pop و remove: 357 | 358 | در عملیات remove و pop کردن دقیقا چیزی شبیه به insert اتفاق می افتد با این تفاوت عنصر آن ایندکسی که اشاره میکنیم حذف میشود و عناصر بغد آن یک ایندکس به عقب می روند. 359 |
360 | 361 | ![data-structures.png](http://www.laurentluce.com/images/blog/list/list_pop.png) 362 | 363 |
364 | توجه کنید ما resize شدن رو هم داریم. 365 | وقتی که allocated ما نسبت به size از یک مقداری بزرگ تر میشود. resize میشود و کوچک تر میشود. 366 | 367 |
368 | 369 | ![data-structures.png](http://www.laurentluce.com/images/blog/list/list_pop_2.png) 370 | 371 | ![data-structures.png](http://www.laurentluce.com/images/blog/list/list_remove.png) 372 | 373 |
374 |

لیست های پیوندی

375 | لیست های پیوندی، یک نوع دیگری از ساختمان داده هستند که از نظر ظاهری شاید تفاوت چندانی رو نسبت به لیست های پویا حس نکنید. اما در عمل تفاوت های 376 | با لیست های پویا دارد. 377 | 378 | ما در Array داده هارا بصورت متوالی ذخیره می کردیم. یعنی داده ها کاملا چسبیده به هم ذخیره می شدند و اگه میخواستیم عملیاتی مثل insert 379 | رو انجام بدیم، مجبور میشدیم تمامی عناصر بعدش رو جابجا کنیم. 380 | 381 | اما در لیست های پیوندی اینکار لازم نیست و insert و append و remove همیشه با n(1) انجام میشود. 382 | 383 | لیست های پیوندی چگونه عمل می کنند؟ 384 | 385 | لیست های پیوندی در واقع تشکیل شده از composition هایی هستند که در ساده ترین حالت در خود این مقادیر را دارند: 386 | 387 | مقدار value دیتایی که ذخیره کریدم و مقدار next که مکان عنصر بعدی را درونش ذخیره میکنیم. 388 | 389 | همچنین هر لیست پیوندی دو مقدار head و tail را دارد که اول لیست و آخر لیست را مشخص می کند. 390 | 391 | خب احتمالا تا الان حدس زده باشید که در لیست های پیوندی بدست آوردن سایز o(n) هستش و همچنین get کردن و set کردن در یک ایندکس هم o(n) 392 | هست. 393 | 394 | البته می توان size را در کنار head و tail ذخیره کرد تا n(1) شود امابرای بدست آوردن یک ایندکس هیچ چاره ای نداریم و با o(n) مواجه میشویم. 395 | 396 | معایب لیست های پیوندی به یکیش اشاره کردم که گرفتن سایز و یا یک ایندکس o(n) هستش. یکی دیگر از معایبش این است که فضای بیشتری 397 | نسبت به Array ها می گیرد. اما اگه دقت کرده باشید برای صف و پشته خیلی گزینه مناسب تری هستند تا Array ها. 398 | 399 |

انواع لیست های پیوندی

400 | لیست های پیوندی (Linked lists) دو نوع اصلی دارد. یکی از آنها Singly linked list و دیگری Doubly linked list است. 401 | تفاوت این دو با هم در مقادیری هست که ذخیره میکنند. 402 | 403 | در Singly linked list ما فقط next را ذخیره میکنیم. این باعث میشود که نتوانیم بصورت معکوس لیست را پیمایش کنیم و هر لیست مقدار قبل از خودش 404 | را نمی داند. 405 | 406 | اما در Doubly linked list بجز next ما مقدار previous را ذخیره میکنیم 407 | یعنی هم به مقدار بعد و همچنین به مقدار قبلش دسترسی داریم. 408 | 409 | همچنین دو نوع دیگر از لیست های پیوندی به نام های Circular linked lists و Circular doubly linked lists را داریم که اگر 410 | علاقه مند هستید درباره اش بخوانید. اما دو نوع پرکاربرش رو با هم دیگه توی پایتون پیاده می کنید. 411 | 412 |

413 | پیاده سازی Singly linked list 414 |

415 | با توجه به توضیحاتی که دادیم، یک Singly linked list در پایتون پیاده میکنیم. 416 | 417 | توجه: 418 | 419 | یک سری مشکلات در کد من وجود دارد و صرفا مثالی از نحوه پیاده کردنش زدم. اگه دوست دارید میتونید اصلاحش کنید و ریکوئست بزنید. 420 |
421 | 422 | ```Python 423 | from typing import Any 424 | 425 | 426 | class Node: 427 | def __init__(self, data): 428 | self.data = data 429 | self.next = None 430 | 431 | def __str__(self): 432 | return f'data: {self.data}' 433 | 434 | 435 | class SinglyLinkedList: 436 | 437 | def __init__(self) -> None: 438 | self.__head = None 439 | self.__tail = None 440 | self.__size = 0 441 | 442 | def size(self) -> int: 443 | return self.__size 444 | 445 | def __update_size(self) -> None: 446 | self.__size += 1 447 | 448 | def insure_index(self, index: int) -> None: 449 | if index > self.size() - 1: 450 | raise IndexError 451 | 452 | def head(self) -> Node: 453 | return self.__head 454 | 455 | def tail(self) -> Node: 456 | return self.__tail 457 | 458 | def __set_head(self, node: Node) -> None: 459 | self.__head = node 460 | 461 | def __set_tail(self, node: Node) -> None: 462 | self.__tail = node 463 | 464 | def append(self, data: Any) -> None: 465 | new_node = Node(data=data) 466 | 467 | if self.head() is None: 468 | self.__set_head(node=new_node) 469 | 470 | if self.tail() is None: 471 | self.__set_tail(node=new_node) 472 | else: 473 | self.tail().next = new_node 474 | self.__set_tail(node=new_node) 475 | 476 | self.__update_size() 477 | 478 | def get_node(self, index: int) -> Node: 479 | self.insure_index(index=index) 480 | 481 | count = 0 482 | current = self.head() 483 | 484 | while index != count: 485 | current = current.next 486 | count += 1 487 | 488 | return current 489 | 490 | def get(self, index: int) -> Any: 491 | node = self.get_node(index=index) 492 | return node.data 493 | 494 | def set(self, index: int, data: Any) -> None: 495 | self.insure_index(index=index) 496 | 497 | count = 0 498 | current = self.head() 499 | 500 | while index != count: 501 | current = current.next 502 | count += 1 503 | 504 | current.data = data 505 | 506 | def remove(self, index: int) -> None: 507 | self.insure_index(index=index) 508 | 509 | count = 0 510 | previous = None 511 | current = self.head() 512 | 513 | while index != count: 514 | previous = current 515 | current = current.next 516 | count += 1 517 | 518 | if previous: 519 | previous.next = current.next 520 | else: 521 | self.__set_head(current.next) 522 | del current 523 | 524 | def pop(self, index: int = 0) -> Any: 525 | self.insure_index(index=index) 526 | 527 | count = 0 528 | previous = None 529 | current = self.head() 530 | 531 | while index != count: 532 | previous = current 533 | current = current.next 534 | count += 1 535 | 536 | if previous: 537 | previous.next = current.next 538 | else: 539 | self.__set_head(node=current.next) 540 | 541 | data = current.data 542 | del current 543 | return data 544 | 545 | def __str__(self) -> str: 546 | my_list = [] 547 | 548 | current = self.head() 549 | 550 | while current: 551 | my_list.append(current.data) 552 | current = current.next 553 | 554 | return str(my_list) 555 | ``` 556 | 557 |
558 |

559 | پیاده سازی Doubly linked list 560 |

561 | در double linked list به علاوه next ما previous را هم ذخیره میکنیم. در این صورت میتوانیم از آخر به اول پیمایش کنیم و در کل کارمون راحت تر میشه 562 | اما حافظه بیشتری رو نیاز داریم. 563 | 564 | برای مثال از نحوه پیاده سازیش توی پایتون این تکه کد رو زدم، خوشحال میشم برای کامل کردن و بهتر شدنش کمکم کنید. 565 |
566 | 567 | ```Python 568 | from typing import Any 569 | 570 | 571 | class Node: 572 | 573 | def __init__(self, data: Any) -> None: 574 | self.data = data 575 | self.next = None 576 | self.pre = None 577 | 578 | def __str__(self): 579 | return f'data: {self.data}' 580 | 581 | 582 | class DoubleLinkedList: 583 | 584 | def __init__(self) -> None: 585 | self.__head = None 586 | self.__tail = None 587 | self.__size = 0 588 | 589 | def size(self) -> int: 590 | return self.__size 591 | 592 | def __update_size(self) -> None: 593 | self.__size += 1 594 | 595 | def insure_index(self, index: int) -> None: 596 | if index > self.size() - 1: 597 | raise IndexError 598 | 599 | def head(self) -> Node: 600 | return self.__head 601 | 602 | def tail(self) -> Node: 603 | return self.__tail 604 | 605 | def __set_head(self, node: Node) -> None: 606 | self.__head = node 607 | 608 | def __set_tail(self, node: Node) -> None: 609 | self.__tail = node 610 | 611 | def append(self, data: Any) -> None: 612 | new_node = Node(data=data) 613 | 614 | if self.head() is None and self.tail() is None: 615 | self.__set_head(node=new_node) 616 | self.__set_tail(node=new_node) 617 | self.__update_size() 618 | return 619 | 620 | self.tail().next = new_node 621 | new_node.pre = self.tail() 622 | self.__set_tail(node=new_node) 623 | self.__update_size() 624 | 625 | def get_node(self, index: int) -> Node: 626 | self.insure_index(index=index) 627 | 628 | count = 0 629 | current = self.head() 630 | 631 | while index != count: 632 | current = current.next 633 | count += 1 634 | 635 | return current 636 | 637 | def get(self, index: int) -> Any: 638 | node = self.get_node(index=index) 639 | return node.data 640 | 641 | def remove(self, index: int) -> None: 642 | current = self.get_node(index=index) 643 | 644 | if current.pre: 645 | current.pre.next = current.next 646 | else: 647 | self.__set_head(node=current.next) 648 | 649 | if current.next: 650 | current.next.pre = current.pre 651 | else: 652 | self.__set_tail(node=current.pre) 653 | 654 | del current 655 | 656 | def insert(self, index: int, data: Any) -> None: 657 | current = self.get_node(index=index) 658 | 659 | new_node = Node(data=data) 660 | new_node.next = current 661 | new_node.pre = current.pre 662 | 663 | if current.next: 664 | pass 665 | 666 | if current.pre: 667 | current.pre.next = new_node 668 | else: 669 | self.__set_head(node=new_node) 670 | 671 | self.__update_size() 672 | 673 | def pop(self): 674 | if self.tail(): 675 | if self.tail().pre: 676 | self.tail().pre.next = None 677 | self.__set_tail(node=self.tail().pre) 678 | data = self.tail().data 679 | else: 680 | data = self.tail().data 681 | self.__head = None 682 | self.__tail = None 683 | 684 | return data 685 | 686 | return ValueError('Double linked list is empty.') 687 | 688 | def __str__(self) -> str: 689 | my_list = [] 690 | 691 | current = self.head() 692 | 693 | while current: 694 | my_list.append(current.data) 695 | current = current.next 696 | 697 | return str(my_list) 698 | ``` 699 | 700 |
701 | 702 |

الگوریتم مرتب سازی

703 | در ادامه آموزش ساختمان داده ما باید با یک سری مفاهیم الگوریتمی و پیچیدگی زمانی و فضایی آشنایی داشته باشیم. برای همین این قسمت کوتاه رو قرار دادم. 704 | ازتون توقع دارم که درباره sorting algorithms و time complexity آشنایی خوبی داشته باشید. توضیحاتی که برای الگوریتم قرار دادم کامل نیست و فقط مرور و گذری سریع هستش. 705 | همچنین درباره الگوریتم بازگشتی نیز مطالعه کنید. 706 | 707 | برای توضیح الگوریتم مرتب سازی من توضیح ویکی پدیا رو قرار میدم و در ادامه چند تا الگوریتم مرتب سازی رو با هم پیاده می کنیم. 708 | 709 | الگوریتم مرتب‌سازی، در دانش رایانه و ریاضی، الگوریتمی است که فهرستی از داده‌ها را به ترتیبی مشخص می‌چیند. 710 | 711 | پرکاربردترین ترتیب‌ها، ترتیب‌های عددی و واژه‌نامه‌ای هستند. مرتب‌سازی کارا در بهینه‌سازی الگوریتم‌هایی که به فهرست‌های مرتب شده نیاز دارند (مثل جستجو و ترکیب)، اهمیت زیادی دارد. 712 | 713 | از آغاز علم رایانه مسائل مرتب‌سازی بررسی‌های فراوانی را متوجه خود ساختند؛ شاید به این علت که در عین ساده بودن، حل آن به صورت کارا پیچیده است. برای نمونه مرتب‌سازی حبابی در سال ۱۹۵۶ به وجود آمد. در حالی که بسیاری این را یک مسئلهٔ حل شده می‌پندارند، الگوریتم کارآمد جدیدی همچنان ابداع می‌شوند (مثلاً مرتب‌سازی کتابخانه‌ای در سال ۲۰۰۴ مطرح شد). 714 | 715 | مبحث مرتب‌سازی در کلاس‌های معرفی علم رایانه بسیار پرکاربرد است؛ مبحثی که در آن وجود الگوریتم‌های فراوان به آشنایی با ایده‌های کلی و مراحل طراحی الگوریتم‌های گوناگون کمک می‌کند؛ مانند تحلیل الگوریتم، داده‌ساختارها، الگوریتم‌های تصادفی، تحلیل بدترین و بهترین حالت و حالت میانگین، هزینهٔ زمان و حافظه، و حد پایین. 716 | 717 |

718 | الگوریتم مرتب سازی انتخابی 719 |

720 | در این الگوریتم ما به این شکل عمل می کنیم که خونه به خونه و ایندکس به ایندکس مرتب کردن رو انتجام میدهیم. 721 | 722 | یعنی اول بررسی میکنیم که کوچ ترین عدد چیست و در ایندکس صفر قرارش میدیم و بعدش بررسی میکنیم کوچک ترین برای ایدکس اول کدام عدد است و ... 723 | 724 | این نوع مرتب سازی inplace هستش. یعنی برای sort نیازی نیست که آرایه جدیدی بوجود بیاریم و توی همون حافظه و آرایه ای که هست کار خودشو 725 | انجام میده. 726 |
727 | 728 | ![data-structures.png](https://upload.wikimedia.org/wikipedia/commons/9/94/Selection-Sort-Animation.gif) 729 | 730 |
731 | نحوه پیاده سازی در پایتون: 732 |
733 | 734 | ```Python 735 | unsorted_list = [10, 5, 8, 4, 6, 1, 3, 7, 2, 9, -1] 736 | 737 | print(unsorted_list) 738 | 739 | 740 | for i in range(len(unsorted_list)): 741 | min_index = i 742 | for j in range(i + 1, len(unsorted_list)): 743 | if unsorted_list[j] < unsorted_list[min_index]: 744 | min_index = j 745 | 746 | (unsorted_list[i], unsorted_list[min_index]) = (unsorted_list[min_index], unsorted_list[i]) 747 | 748 | print(unsorted_list) 749 | ``` 750 | 751 |
752 |

753 | الگوریتم مرتب سازی درجی 754 |

755 | ویکی پدیا بخوبی نخوه کار کردن این الگوریتم را توضیح داده است: 756 | 757 | این الگوریتم به این صورت عمل می‌کند که تمام عناصر لیست را یکی یکی برمی‌دارد و آن را در موقعیت مناسب در بخش مرتب شده قرار می‌دهد. انتخاب عنصر مورد نظر، اختیاری است و می‌تواند توسط هر الگوریتم انتخابی، انتخاب شود. مرتب‌سازی درجی به صورت درجا عمل می‌کند. نتیجه عمل بعد از k مرحله، حاوی k عنصر انتخاب شده به صورت مرتب شده‌است. 758 | 759 | معمول‌ترین نسخه از این الگوریتم که روی آرایه‌ها عمل می‌کند، به این صورت است: 760 | 761 | فرض کنید یک تابع به اسم Insert داریم که داده را در بخش مرتب شده در ابتدای آرایه درج می‌کند. این تابع با شروع از انتهای سری شروع به کار می‌کند و هر عنصر را به سمت راست منتقل می‌کند تا وقتی که جای مناسب برای عنصر جدید پیدا شود. این تابع این اثر جانبی را دارد که عنصر دقیقاً بعد از بخش مرتب شده را رونویسی می‌کند. 762 | برای اعمال مرتب‌ساز درجی، از انتهای سمت چپ آرایه شروع می‌کنیم و با صدا زدن تابع insert، هر عنصر را به موقعیت درستش می‌بریم. آن بخشی که عنصر فعلی را در آن می‌کنیم، در ابتدای آرایه و در اندیس‌هایی است که آن‌ها را قبلاً آزمایش کرده‌ایم. هر بار صدا زدن تابع insert، یک عنصر را رونویسی می‌کند، اما این مسئله مشکلی ایجاد نمی‌کند، زیرا این داده، همانی است که الان در حال درج آن هستیم. 763 |
764 | 765 | ![data-structures.png](https://upload.wikimedia.org/wikipedia/commons/0/0f/Insertion-sort-example-300px.gif) 766 | 767 | 768 |
769 | مثال در پایتون: 770 |
771 | 772 | ```Python 773 | unsorted_list = [10, 5, 8, 4, 6, 1, 3, 7, 2, 9, -1] 774 | 775 | 776 | for i in range(1, len(unsorted_list)): 777 | 778 | temp = unsorted_list[i] 779 | 780 | j = i - 1 781 | while j >= 0 and temp < unsorted_list[j]: 782 | unsorted_list[j + 1] = unsorted_list[j] 783 | j -= 1 784 | unsorted_list[j + 1] = temp 785 | 786 | 787 | print(unsorted_list) 788 | 789 | ``` 790 | 791 |
792 |

793 | الگوریتم مرتب سازی حبابی 794 |

795 | مرتب‌سازی حبابی یک الگوریتم مرتب‌سازی ساده‌است که فهرست را پشت سرهم پیمایش می‌کند تا هر بار عناصر کنارهم را با هم سنجیده و اگر در جای نادرست بودند جابه‌جایشان کند. در این الگوریتم این کار باید تا زمانی که هیچ جابه‌جایی در فهرست رخ ندهد، ادامه یابد و در آن زمان فهرست مرتب شده‌است. این مرتب‌سازی از آن رو حبابی نامیده می‌شود که هر عنصر با عنصر کناری خود سنجیده‌شده و درصورتی که از آن کوچک‌تر باشد جای خود را به آن می‌دهد و این کار همچنان پیش می‌رود تا کوچک‌ترین عنصر به پایین فهرست برسد و دیگران نیز به ترتیب در جای خود قرار گیرند (یا به رتبه‌ای بالاتر روند یا به پایین‌تر فهرست رانده شوند) این عمل همانند پویش حباب به بالای مایع است. 796 |
797 | 798 | ![data-structures.png](https://upload.wikimedia.org/wikipedia/commons/c/c8/Bubble-sort-example-300px.gif) 799 | 800 |
801 | نمونه کد در پایتون 802 |
803 | 804 | ```Python 805 | unsorted_list = [10, 5, 8, 4, 6, 1, 3, 7, 2, 9, -1] 806 | 807 | print(unsorted_list) 808 | 809 | 810 | for _ in range(1, len(unsorted_list)): 811 | for j in range(0, len(unsorted_list) - 1): 812 | if unsorted_list[j] > unsorted_list[j + 1]: 813 | unsorted_list[j], unsorted_list[j + 1] = unsorted_list[j + 1], unsorted_list[j] 814 | 815 | 816 | print(unsorted_list) 817 | ``` 818 | 819 |
820 |

درخت ها

821 | تا الان هر ساختمان داده ای که بررسی کردیم ساختار لیست داشتند. مانند linked list, queue, stack, dynamic array. حالا وقتشه یک نوع ساختار داده ی دیگه ای رو بررسی کنیم 822 | که به ساختار داده ی درخت شناخته میشن. 823 | 824 | همونظور که از اسمش پیداست در این نوع ساختار داده ما یک ریشه (root) داریم و این ریشنه می تواند فرزندانی رو داشته باشند. همچنین 825 | فرزندان هم می توانند فرزندی رو برای خودشون داشته باشن اما هر فرزند فقط و فقط یک ریشه دارد. 826 |
827 | 828 | ![data-structures.png](https://miro.medium.com/v2/resize:fit:677/1*Z89j_NoDx9HkFcPHy3rPZg.png) 829 | 830 |
831 | در درخت با چند تا مفهوم روبرو میشوید که اینها هستند: 832 | 833 | ریشه: 834 | 835 | با توجه به عکس ریشه شماره دو میشود. ریشه خودش پدر ندارد و بالاتر از همه قرار میگیره 836 | 837 | گره میانی: 838 | 839 | با توجه به عکس 9و 12و 8و ... گره میانی به حساب می آیند. با اینکه خودشان فرزند هستند اما خودشان هم فرزند هایی رو دارند. 840 | 841 | برگ: 842 | 843 | برگ به فرزندی اشاره داره که خودش فرزندی ندارد و به قول معروف شجره نامه تو این قسمت تموم میشه. 844 | 845 | عمق: 846 | 847 | اگر عمق ریشه را 0 در نظر بگیریم، عمق 9 برابر با 1 است و عمق 2 برابر با 2 می شود. عمیق به مکان قرارگیری نسبت به ریشه اشاره دارد که 848 | هرچقدر به ریشه نردیک تر باشد عمق نیز کمتر میشود. 849 | 850 | همچنین ما انواع مختلفی از درخت هارو داریم مثل Binary tree و B-tree که در ادامه بصورت جزئی بررسی میکنیم. 851 | 852 |

پیاده سازی ساختار داده درخت در پایتون

853 | با توجه به توضیحاتی که دادیم. یک ساختار درختی ریشته و فرزندانی را دارد. در یک مثال ساده پایتونی به این شکل می توانیم پیاده سازی کنیم: 854 |
855 | 856 | ```Python 857 | from typing import Any 858 | 859 | 860 | class TreeNode: 861 | 862 | def __init__(self, value: Any, parent: Any | None = None) -> None: 863 | self.parent = parent 864 | self.value = value 865 | self.__children = [] 866 | 867 | def add_child(self, value) -> None: 868 | new_child = TreeNode(value=value, parent=self) 869 | self.__children.append(new_child) 870 | 871 | def children(self) -> list: 872 | return self.__children 873 | 874 | def get_all_children(self) -> list: 875 | all_children = [] 876 | depth_count = 0 877 | for child in self.__children: 878 | depth_count += 1 879 | all_children.append(child) 880 | all_children.extend(child.get_all_children()) 881 | return all_children 882 | 883 | def depth(self) -> int: 884 | count = 0 885 | node = self 886 | while node and node.parent: 887 | node = node.parent 888 | count += 1 889 | return count 890 | 891 | def get_depth(self) -> int: 892 | if not self.__children: 893 | return 0 894 | else: 895 | child_depths = [child.get_depth() for child in self.__children] 896 | return max(child_depths) + 1 897 | 898 | def get_height(self) -> int: 899 | if not self.__children: 900 | return 0 901 | else: 902 | child_heights = [child.get_height() for child in self.__children] 903 | return max(child_heights) + 1 904 | 905 | def remove(self) -> None: 906 | parent = self.parent 907 | children = self.children() 908 | if parent: 909 | parent.__children.extend(children) 910 | parent.__children.remove(self) 911 | del self 912 | 913 | 914 | class Tree: 915 | 916 | def __init__(self, value: Any) -> None: self.__root = self.__create_root(value=value) 917 | 918 | @staticmethod 919 | def __create_root(value: Any) -> TreeNode: return TreeNode(value=value) 920 | 921 | @property 922 | def root(self): 923 | return self.__root 924 | 925 | def __str__(self): return f'root name: {self.__root.value}' 926 | ``` 927 | 928 |
929 | طبق نیازمندی ها. باید بتوانیم در یک ساختار داده ی درختی به فرزندان دسترسی داشته باشیم و همچنین عمق و ارتفاع را هم بتوانیم بدست بیاوریم. 930 | 931 |

932 | ساختار داده ی درخت دودویی و درخت کامل: 933 |

934 | در یک درخت دودویی هر گره فقط می تواند دو یک یا دو فرزند داشته باشد. ساختار درخت کامل نیز به درختی گفته میشود که تمامی فرزندانش 935 | بجز برگ ها دو فرزند دارند. 936 | 937 | در پیاده سازی ساختار داده ی درختی خیلی از الگوریتم بازگشتی استفاده میشود. 938 |
939 | 940 | ![data-structures.png](https://media.geeksforgeeks.org/wp-content/uploads/20220630154756/img2.jpg) 941 | 942 |
943 | همچنین می توان یک درخت دودویی را در یک آرایه پیاده سازی کرد. 944 |
945 | 946 | ![data-structures.png](https://i.stack.imgur.com/7MkVp.png) 947 | 948 |
949 |

درخت جستچوی دودویی

950 | 951 |
952 | --------------------------------------------------------------------------------