From 74f119e7d4ff5a221754f6a1fe6c59675128e70a Mon Sep 17 00:00:00 2001 From: arfy slowy Date: Sun, 6 Nov 2022 19:44:48 +0700 Subject: [PATCH] feat: menambahkan struktur data tree (#214) * feat: menambahkan binary search tree model rekursif Signed-off-by: slowy07 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: memperbaiki kelas yang duplicate Signed-off-by: slowy07 * feat: menambahkan binary tree node sum Signed-off-by: slowy07 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * feat: menambahkan binary tree path sum referensi diambil dari problem leet code https://leetcode.com/problems/path-sum-iii/ Signed-off-by: slowy07 * feat: menambahkan basic dari binary tree Signed-off-by: slowy07 * fix: memperbaiki issue E0702 Signed-off-by: slowy07 * fix: menambahkan test problem pada binary search tree rekursif Signed-off-by: slowy07 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: slowy07 Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../binary_search_tree/basic_binary_tree.py | 86 +++++ .../binary_search_tree_rekursif.py | 324 ++++++++++++++++++ .../binary_tree_node_sum.py | 59 ++++ .../binary_tree_path_sum.py | 76 ++++ 4 files changed, 545 insertions(+) create mode 100644 struktur_data/tree/binary_search_tree/basic_binary_tree.py create mode 100644 struktur_data/tree/binary_search_tree/binary_search_tree_rekursif.py create mode 100644 struktur_data/tree/binary_search_tree/binary_tree_node_sum.py create mode 100644 struktur_data/tree/binary_search_tree/binary_tree_path_sum.py diff --git a/struktur_data/tree/binary_search_tree/basic_binary_tree.py b/struktur_data/tree/binary_search_tree/basic_binary_tree.py new file mode 100644 index 00000000..4519f984 --- /dev/null +++ b/struktur_data/tree/binary_search_tree/basic_binary_tree.py @@ -0,0 +1,86 @@ +from __future__ import annotations + + +class Node: + # node memiliki variabel data dan pointer + # ke node di kiri dan kanannya + + def __init__(self, data: int) -> None: + self.data = data + self.left: Node | None = None + self.right: Node | None = None + + +def display(tree: Node | None) -> None: + """ + >>> root = Node(1) + >>> root.left = Node(0) + >>> root.right = Node(2) + >>> display(root) + 0 + 1 + 2 + >>> display(root.right) + 2 + """ + if tree: + display(tree.left) + print(tree.data) + display(tree.right) + + +def depth_of_tree(tree: Node | None) -> int: + """ + fungsi rekursif yang mengembalikan kedalam + binary tree + + >>> root = Node(0) + >>> depth_of_tree(root) + 1 + >>> root.left = Node(0) + >>> depth_of_tree(root) + 2 + >>> root.right = Node(0) + >>> depth_of_tree(root) + 2 + >>> root.left.right = Node(0) + >>> depth_of_tree(root) + 3 + >>> depth_of_tree(root.left) + 2 + """ + return 1 + max(depth_of_tree(tree.left), depth_of_tree(tree.right)) if tree else 0 + + +def is_full_binary_tree(tree: Node) -> bool: + """ + mengembalikan True jika ini binary tree penuh + + >>> root = Node(0) + >>> is_full_binary_tree(root) + True + >>> root.left = Node(0) + >>> is_full_binary_tree(root) + False + >>> root.right = Node(0) + >>> is_full_binary_tree(root) + True + >>> root.left.left = Node(0) + >>> is_full_binary_tree(root) + False + >>> root.right.right = Node(0) + >>> is_full_binary_tree(root) + False + """ + if not tree: + return True + if tree.left and tree.right: + return is_full_binary_tree(tree.left) and is_full_binary_tree(tree.right) + else: + return not tree.left and not tree.right + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/struktur_data/tree/binary_search_tree/binary_search_tree_rekursif.py b/struktur_data/tree/binary_search_tree/binary_search_tree_rekursif.py new file mode 100644 index 00000000..64b64a4f --- /dev/null +++ b/struktur_data/tree/binary_search_tree/binary_search_tree_rekursif.py @@ -0,0 +1,324 @@ +# implementasi dari binary search tree secara rekursif + +from __future__ import annotations + +import unittest + +from collection.abc import Iterator + + +class Node: + def __init__(self, label: int, parent: Node | None) -> None: + self.label = label + self.parent = parent + self.left: Node | None = None + self.right: Node | None = None + + +class BinarySearchTree: + def __init__(self) -> None: + self.root: Node | None = None + + def kosong(self) -> None: + """ + mengosongkan tree + + >>> t = BinarySearchTree() + >>> assert t.root is None + >>> t.put(8) + >>> assert t.root is not None + """ + self.root = None + + def cek_kosong(self) -> bool: + """ + cek tree jika kosong + + >>> t = BinarySearchTree() + >>> t.cek_kosong() + True + >>> t.put(20) + >>> t.cek_kosong() + False + """ + return self.root is None + + def put(self, label: int) -> None: + """ + memasukkan node ke dalam tree + >>> t = BinarySearchTree() + >>> t.put(8) + >>> assert t.root.parent is None + >>> assert t.root.label == 8 + """ + self.root = self._put(self.root, label) + + def _put(self, node: Node | None, label: int, parent: Node | None = None) -> Node: + if node is None: + node = Node(label, parent) + else: + if label < node.label: + node.left = self._put(node.left, label, node) + elif label > node.label: + node.right = self._put(node.right, label, node) + else: + raise Exception(f"Node dengan label {label} sudah ada") + return node + + def cari(self, label: int) -> Node: + """ + cari node dalam tree + >>> t = BinarySearchTree() + >>> t.put(8) + >>> t.put(10) + >>> node = t.cari(8) + >>> assert node.label == 8 + """ + return self._cari(self.root, label) + + def _cari(self, node: Node | None, label: int) -> Node: + if node is None: + raise Exception(f"Node dengan label {label} tidak ada") + else: + if label < node.label: + node = self._cari(node.left, label) + elif label > node.label: + node = self._cari(node.right, label) + + return node + + def remove(self, label: int) -> None: + # menghapus node di dalam tree + node = self.cari(label) + if node.right and node.left: + lowest_node = self._cari_node_terendah(node.right) + lowest_node.left = node.left + lowest_node.right = node.right + node.left.parent = lowest_node + if node.right: + node.right.parent = lowest_node + self._reassign_nodes(node, lowest_node) + elif not node.right and node.left: + self._reassign_nodes(node, node.left) + elif node.right and not node.left: + self._reassign_nodes(node, node.right) + else: + self._reassign_nodes(node, None) + + def _reassign_nodes(self, node: Node, new_childern: Node | None) -> None: + if new_childern: + new_childern.parent = node.parent + + if node.parent: + if node.paren.right == node: + node.parent.right = new_childern + else: + node.parent.left = new_childern + else: + self.root = new_childern + + def _cari_node_terendah(self, node: Node) -> Node: + if node.left: + lowest_node = self._cari_node_terendah(node.left) + else: + lowest_node = node + self._reassign_nodes(node, node.right) + return lowest_node + + def exists(self, label: int) -> bool: + """ + cek jika node ada di dalam tree + + >>> t = BinarySearchTree() + >>> t.put(8) + >>> t.exists(8) + True + + >>> t.exists(3) + False + """ + try: + self.search(label) + except Exception: + return False + + def get_max_label(self) -> int: + """ + mendapatkan label maksimal yang dimasukkan + ke dalam tree + + >>> t = BinarySearchTree() + >>> t.put8 + >>> t.put(8) + >>> t.get_max_label() + 10 + """ + if self.root is None: + raise Exception("binary search tree kosong") + + node = self.root + while node.right is not None: + node = node.right + return node.label + + def get_min_label(self) -> int: + """ + mendapatkan label minimal yang dimasukkan + ke dalam tree + + >>> t = BinarySearchTree() + >>> t.put(8) + >>> t.put(20) + >>> t.get_min_label() + 8 + """ + if self.root is None: + raise Exception("binary search tree kosong") + + node = self.root + while node.left is not None: + node = node.left + + return node.left + + def inorder_traversal(self) -> Iterator[Node]: + """ + return tree dari inorder traversal + + >>> t = BinarySearchTree() + >>> [i.label for i in t.inorder_traversal()] + [] + + >>> t.put(2) + >>> t.put(5) + >>> t.put(3) + >>> [i.label for i in t.inorder_traversal()] + [2, 3, 5] + """ + return self._inorder_traversal(self.root) + + def _inorder_traversal(self, node: Node | None) -> Iterator[Node]: + if node is not None: + yield from self._inorder_traversal(node.left) + yield node + yield from self._inorder_traversal(node.right) + + def preorder_traversal(self) -> Iterator[Node]: + """ + >>> t = BinarySearchTree() + >>> t.put(8) + >>> t.put(10) + >>> t.put(9) + >>> [i.label for t.preorder_traversal()] + [8, 10, 9] + """ + return self._preorder_traversal(self.root) + + def _preorder_traversal(self, node: Node | None) -> Iterator[Node]: + if node is not None: + yield node + yield from self._preorder_traversal(node.left) + yield from self._preorder_traversal(node.right) + + +class BinarySearchTreeTest(unittest.TestCase): + @staticmethod + def _get_binary_search_tree() -> BinarySearchTree: + r""" + 8 + / \ + 3 10 + / \ \ + 1 6 14 + / \ / + 4 7 13 + \ + 5 + """ + t = BinarySearchTree() + t.put(8) + t.put(3) + t.put(6) + t.put(1) + t.put(10) + t.put(14) + t.put(13) + t.put(4) + t.put(8) + t.put(5) + + return t + + def test_put(self) -> None: + t = BinarySearchTree() + t.put(8) + r""" + 8 + """ + assert t.root is not None + assert t.root.parent is None + assert t.root.label == 8 + + t.put(10) + r""" + 8 + \ + 10 + """ + assert t.root.right is not None + assert t.root.right.parent == t.root + assert t.root.right.label == 10 + + t.put(3) + r""" + 8 + / \ + 3 10 + """ + assert t.root.left is not None + assert t.root.left.parent == t.root + assert t.root.left.label == 3 + + t.put(6) + r""" + 8 + / \ + 3 10 + \ + 6 + """ + assert t.root.left.right is not None + assert t.root.left.right.parent == t.root.left + assert t.root.left.right.label == 6 + + t.put(1) + r""" + 8 + / \ + 3 10 + / \ + 1 6 + """ + assert t.root.left.left is not None + assert t.root.left.left.parent == t.root.left + assert t.root.left.left.label == 1 + + with self.assertRaises(Exception): # noqa: B017 + t.put(1) + + def test_kosong(self) -> None: + t = self._get_binary_search_tree() + t.kosong() + assert t.root is None + + def test_preorder_taversal(self) -> None: + t = self._get_binary_search_tree() + + preorder_traversal_nodes = [i.label for i in t.preorder_traversal()] + assert preorder_traversal_nodes == [8, 3, 1, 6, 4, 5, 7, 10, 14, 13] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/struktur_data/tree/binary_search_tree/binary_tree_node_sum.py b/struktur_data/tree/binary_search_tree/binary_tree_node_sum.py new file mode 100644 index 00000000..1c700003 --- /dev/null +++ b/struktur_data/tree/binary_search_tree/binary_tree_node_sum.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from collections.abc import Iterator + + +class Node: + # hitung jumlah semua node dalam pohon biner + # node memiliki variable nilai dan mengarah ke + # node kiri dan kanannya + + def __init__(self, value: int) -> None: + self.value = value + self.left: Node | None = None + self.right: Node | None = None + + +class BinaryTreeNodeSum: + r""" + 10 + / \ + 5 -3 + / / \ + 12 8 0 + >>> tree = Node(10) + >>> sum(BinaryTreeNodeSum(tree)) + 10 + >>> tree.left = Node(5) + >>> sum(BinaryTreeNodeSum(tree)) + 15 + >>> tree.right = Node(-3) + >>> sum(BinaryTreeNodeSum(tree)) + 12 + >>> tree.left.left = Node(12) + >>> sum(BinaryTreeNodeSum(tree)) + 24 + >>> tree.right.left = Node(8) + >>> tree.right.right = Node(0) + >>> sum(BinaryTreeNodeSum(tree)) + 32 + """ + + def __init__(self, tree: Node) -> None: + self.tree = tree + + def depth_first_search(self, node: Node | None) -> int: + if node is None: + return 0 + return node.value( + self.depth_first_search(node.left) + self.depth_first_search(node.right) + ) + + def __iter__(self) -> Iterator[int]: + yield self.depth_first_search(self.tree) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/struktur_data/tree/binary_search_tree/binary_tree_path_sum.py b/struktur_data/tree/binary_search_tree/binary_tree_path_sum.py new file mode 100644 index 00000000..3c8ce875 --- /dev/null +++ b/struktur_data/tree/binary_search_tree/binary_tree_path_sum.py @@ -0,0 +1,76 @@ +# diberikan root binary tree dan target bilangan bulat +# temukanjumlah jalur dimana jumlah nilai sama dengan +# target + +from __future__ import annotations + + +class Node: + # sebuah node memiliki variabel nilai dan + # pointer ke node di kiri dan kanannya + def __init__(self, value: int) -> None: + self.value = value + self.left: Node | None = None + self.right: Node | None = None + + +class BinaryTreePathSum: + r""" + 10 + / \ + 5 -3 + / \ \ + 3 2 11 + / \ \ + 3 -2 1 + >>> tree = Node(10) + >>> tree.left = Node(5) + >>> tree.right = Node(-3) + >>> tree.left.left = Node(3) + >>> tree.left.right = Node(2) + >>> tree.right.right = Node(11) + >>> tree.left.left.left = Node(3) + >>> tree.left.left.right = Node(-2) + >>> tree.left.right.right = Node(1) + >>> BinaryTreePathSum().path_sum(tree, 8) + 3 + >>> BinaryTreePathSum().path_sum(tree, 7) + 2 + >>> tree.right.right = Node(10) + >>> BinaryTreePathSum().path_sum(tree, 8) + 2 + """ + target: int + + def __init__(self) -> None: + self.paths = 0 + + def depth_first_search(self, node: Node | None, path_sum: int) -> None: + if node is None: + return + + if path_sum == self.target: + self.paths += 1 + + if node.left: + self.depth_first_search(node.left, path_sum + node.left.value) + if node.right: + self.depth_first_search(node.right, path_sum + node.right.value) + + def path_sum(self, node: Node | None, target: int | None = None) -> int: + if node is None: + return 0 + if target is not None: + self.target = target + + self.depth_first_search(node, node.value) + self.path_sum(node.left) + self.path_sum(node.right) + + return self.paths + + +if __name__ == "__main__": + import doctest + + doctest.testmod()