From 9eac1275f65f4f7f70e2d23bcc325ae9eed4f1fb Mon Sep 17 00:00:00 2001 From: a16su <33782391+a16su@users.noreply.github.com> Date: Tue, 20 Dec 2022 15:52:00 +0800 Subject: [PATCH 001/158] add binary_tree and avl_tree python code --- codes/python/chapter_tree/avl_tree.py | 281 ++++++++++++++++++ .../python/chapter_tree/binary_search_tree.py | 167 ++++++++++- codes/python/chapter_tree/binary_tree.py | 32 +- codes/python/chapter_tree/binary_tree_bfs.py | 39 ++- codes/python/chapter_tree/binary_tree_dfs.py | 74 ++++- codes/python/include/binary_tree.py | 2 +- docs/chapter_tree/avl_tree.md | 210 ++++++++++++- docs/chapter_tree/binary_search_tree.md | 104 +++++++ docs/chapter_tree/binary_tree.md | 80 ++++- 9 files changed, 967 insertions(+), 22 deletions(-) create mode 100644 codes/python/chapter_tree/avl_tree.py diff --git a/codes/python/chapter_tree/avl_tree.py b/codes/python/chapter_tree/avl_tree.py new file mode 100644 index 000000000..4e6e35727 --- /dev/null +++ b/codes/python/chapter_tree/avl_tree.py @@ -0,0 +1,281 @@ +import sys, os.path as osp +import typing + +sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) +from include import * + + +class AVLTreeNode: + def __init__( + self, + val=None, + height: int = 0, + left: typing.Optional["AVLTreeNode"] = None, + right: typing.Optional["AVLTreeNode"] = None, + ): + self.val = val + self.height = height + self.left = left + self.right = right + + def __str__(self): + val = self.val + left_val = self.left.val if self.left else None + right_val = self.right.val if self.right else None + return "".format( + val, left_val, right_val + ) + + +class AVLTree: + def __init__(self, root: typing.Optional[AVLTreeNode] = None): + self.root = root + + @staticmethod + def height(node: typing.Optional[AVLTreeNode]) -> int: + """ + 获取结点高度 + Args: + node:起始结点 + + Returns: 高度 or -1 + + """ + # 空结点高度为 -1 ,叶结点高度为 0 + if node is not None: + return node.height + return -1 + + def __update_height(self, node: AVLTreeNode): + """ + 更新结点高度 + Args: + node: 要更新高度的结点 + + Returns: None + + """ + # 结点高度等于最高子树高度 + 1 + node.height = max([self.height(node.left), self.height(node.right)]) + 1 + + def balance_factor(self, node: AVLTreeNode) -> int: + """ + 获取结点平衡因子 + Args: + node: 要获取平衡因子的结点 + + Returns: 平衡因子 + + """ + # 空结点平衡因子为 0 + if node is None: + return 0 + # 结点平衡因子 = 左子树高度 - 右子树高度 + return self.height(node.left) - self.height(node.right) + + def __right_rotate(self, node: AVLTreeNode) -> AVLTreeNode: + child = node.left + grand_child = child.right + # 以 child 为原点,将 node 向右旋转 + child.right = node + node.left = grand_child + # 更新结点高度 + self.__update_height(node) + self.__update_height(child) + # 返回旋转后子树的根节点 + return child + + def __left_rotate(self, node: AVLTreeNode) -> AVLTreeNode: + child = node.right + grand_child = child.left + # 以 child 为原点,将 node 向左旋转 + child.left = node + node.right = grand_child + # 更新结点高度 + self.__update_height(node) + self.__update_height(child) + # 返回旋转后子树的根节点 + return child + + def rotate(self, node: AVLTreeNode): + """ + 执行旋转操作,使该子树重新恢复平衡 + Args: + node: 要旋转的根结点 + + Returns: 旋转后的根结点 + + """ + # 获取结点 node 的平衡因子 + balance_factor = self.balance_factor(node) + # 左偏树 + if balance_factor > 1: + if self.balance_factor(node.left) >= 0: + # 右旋 + return self.__right_rotate(node) + else: + # 先左旋后右旋 + node.left = self.__left_rotate(node.left) + return self.__right_rotate(node) + # 右偏树 + elif balance_factor < -1: + if self.balance_factor(node.right) <= 0: + # 左旋 + return self.__left_rotate(node) + else: + # 先右旋后左旋 + node.right = self.__right_rotate(node.right) + return self.__left_rotate(node) + # 平衡树,无需旋转,直接返回 + return node + + def insert(self, val) -> AVLTreeNode: + """ + 插入结点 + Args: + val: 结点的值 + + Returns: + node: 插入结点后的根结点 + """ + self.root = self.insert_helper(self.root, val) + return self.root + + def insert_helper( + self, node: typing.Optional[AVLTreeNode], val: int + ) -> AVLTreeNode: + """ + 递归插入结点(辅助函数) + Args: + node: 要插入的根结点 + val: 要插入的结点的值 + + Returns: 插入结点后的根结点 + + """ + if node is None: + return AVLTreeNode(val) + # 1. 查找插入位置,并插入结点 + if val < node.val: + node.left = self.insert_helper(node.left, val) + elif val > node.val: + node.right = self.insert_helper(node.right, val) + else: + # 重复结点不插入,直接返回 + return node + # 更新结点高度 + self.__update_height(node) + # 2. 执行旋转操作,使该子树重新恢复平衡 + return self.rotate(node) + + def remove(self, val: int): + """ + 删除结点 + Args: + val: 要删除的结点的值 + + Returns: + + """ + root = self.remove_helper(self.root, val) + return root + + def remove_helper( + self, node: typing.Optional[AVLTreeNode], val: int + ) -> typing.Optional[AVLTreeNode]: + """ + 递归删除结点(辅助函数) + Args: + node: 删除的起始结点 + val: 要删除的结点的值 + + Returns: 删除目标结点后的起始结点 + + """ + if node is None: + return None + # 1. 查找结点,并删除之 + if val < node.val: + node.left = self.remove_helper(node.left, val) + elif val > node.val: + node.right = self.remove_helper(node.right, val) + else: + if node.left is None or node.right is None: + child = node.left or node.right + # 子结点数量 = 0 ,直接删除 node 并返回 + if child is None: + return None + # 子结点数量 = 1 ,直接删除 node + else: + node = child + else: # 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 + temp = self.min_node(node.right) + node.right = self.remove_helper(node.right, temp.val) + node.val = temp.val + # 更新结点高度 + self.__update_height(node) + # 2. 执行旋转操作,使该子树重新恢复平衡 + return self.rotate(node) + + def min_node( + self, node: typing.Optional[AVLTreeNode] + ) -> typing.Optional[AVLTreeNode]: + # 获取最小结点 + if node is None: + return None + # 循环访问左子结点,直到叶结点时为最小结点,跳出 + while node.left is not None: + node = node.left + return node + + def search(self, val: int): + cur = self.root + while cur is not None: + if cur.val < val: + cur = cur.right + elif cur.val > val: + cur = cur.left + else: + break + return cur + + +if __name__ == "__main__": + + def test_insert(tree: AVLTree, val: int): + tree.insert(val) + print("\n插入结点 {} 后,AVL 树为".format(val)) + print_tree(tree.root) + + def test_remove(tree: AVLTree, val: int): + tree.remove(val) + print("\n删除结点 {} 后,AVL 树为".format(val)) + print_tree(tree.root) + + # 初始化空 AVL 树 + avl_tree = AVLTree() + + # 插入结点 + # 请关注插入结点后,AVL 树是如何保持平衡的 + test_insert(avl_tree, 1) + test_insert(avl_tree, 2) + test_insert(avl_tree, 3) + test_insert(avl_tree, 4) + test_insert(avl_tree, 5) + test_insert(avl_tree, 8) + test_insert(avl_tree, 7) + test_insert(avl_tree, 9) + test_insert(avl_tree, 10) + test_insert(avl_tree, 6) + + # 插入重复结点 + test_insert(avl_tree, 7) + + # 删除结点 + # 请关注删除结点后,AVL 树是如何保持平衡的 + test_remove(avl_tree, 8) # 删除度为 0 的结点 + test_remove(avl_tree, 5) # 删除度为 1 的结点 + test_remove(avl_tree, 4) # 删除度为 2 的结点 + + result_node = avl_tree.search(7) + print("\n查找到的结点对象为 {},结点值 = {}".format(result_node, result_node.val)) diff --git a/codes/python/chapter_tree/binary_search_tree.py b/codes/python/chapter_tree/binary_search_tree.py index f9dd9ec95..211e7c0df 100644 --- a/codes/python/chapter_tree/binary_search_tree.py +++ b/codes/python/chapter_tree/binary_search_tree.py @@ -1,10 +1,173 @@ -''' +""" File: binary_search_tree.py Created Time: 2022-11-25 Author: Krahets (krahets@163.com) -''' +""" import sys, os.path as osp + sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * + +class BinarySearchTree: + """ + 二叉搜索树 + """ + + def __init__(self, nums) -> None: + nums.sort() + self.__root = self.buildTree(nums, 0, len(nums) - 1) + + def buildTree(self, nums, start_index, end_index): + if start_index > end_index: + return None + mid = (start_index + end_index) // 2 + root = TreeNode(nums[mid]) + root.left = self.buildTree( + nums=nums, start_index=start_index, end_index=mid - 1 + ) + root.right = self.buildTree(nums=nums, start_index=mid + 1, end_index=end_index) + return root + + def get_root(self): + return self.__root + + def search(self, num): + """ + 查找结点 + """ + cur = self.get_root() + # 循环查找,越过叶结点后跳出 + while cur is not None: + # 目标结点在 root 的右子树中 + if cur.val < num: + cur = cur.right + # 目标结点在 root 的左子树中 + elif cur.val > num: + cur = cur.left + # 找到目标结点,跳出循环 + else: + break + return cur + + def insert(self, num): + """ + 插入结点 + """ + root = self.get_root() + # 若树为空,直接提前返回 + if root is None: + return None + + cur = root + pre = None + + # 循环查找,越过叶结点后跳出 + while cur is not None: + # 找到重复结点,直接返回 + if cur.val == num: + return None + pre = cur + + if cur.val < num: # 插入位置在 root 的右子树中 + cur = cur.right + else: # 插入位置在 root 的左子树中 + cur = cur.left + + # 插入结点 val + node = TreeNode(num) + if pre.val < num: + pre.right = node + else: + pre.left = node + return node + + def remove(self, num): + """ + 删除结点 + """ + root = self.get_root() + # 若树为空,直接提前返回 + if root is None: + return None + + cur = root + pre = None + + # 循环查找,越过叶结点后跳出 + while cur is not None: + # 找到待删除结点,跳出循环 + if cur.val == num: + break + pre = cur + if cur.val < num: # 待删除结点在 root 的右子树中 + cur = cur.right + else: # 待删除结点在 root 的左子树中 + cur = cur.left + + # 若无待删除结点,则直接返回 + if cur is None: + return None + + # 子结点数量 = 0 or 1 + if cur.left is None or cur.right is None: + # 当子结点数量 = 0 / 1 时, child = null / 该子结点 + child = cur.left or cur.right + # 删除结点 cur + if pre.left == cur: + pre.left = child + else: + pre.right = child + # 子结点数量 = 2 + else: + # 获取中序遍历中 cur 的下一个结点 + nex = self.min(cur.right) + tmp = nex.val + # 递归删除结点 nex + self.remove(nex.val) + # 将 nex 的值复制给 cur + cur.val = tmp + return cur + + def min(self, root): + """ + 获取最小结点 + """ + if root is None: + return root + + # 循环访问左子结点,直到叶结点时为最小结点,跳出 + while root.left is not None: + root = root.left + return root + + +if __name__ == "__main__": + # 初始化二叉搜索树 + nums = list(range(1, 16)) + bst = BinarySearchTree(nums=nums) + print("\n初始化的二叉树为\n") + print_tree(bst.get_root()) + + # 查找结点 + node = bst.search(5) + print("\n查找到的结点对象为: {},结点值 = {}".format(node, node.val)) + + # 插入结点 + ndoe = bst.insert(16) + print("\n插入结点 16 后,二叉树为\n") + print_tree(bst.get_root()) + + # 删除结点 + bst.remove(1) + print("\n删除结点 1 后,二叉树为\n") + print_tree(bst.get_root()) + + bst.remove(2) + print("\n删除结点 2 后,二叉树为\n") + print_tree(bst.get_root()) + + bst.remove(4) + print("\n删除结点 4 后,二叉树为\n") + print_tree(bst.get_root()) diff --git a/codes/python/chapter_tree/binary_tree.py b/codes/python/chapter_tree/binary_tree.py index 78afa868e..2a892fae8 100644 --- a/codes/python/chapter_tree/binary_tree.py +++ b/codes/python/chapter_tree/binary_tree.py @@ -1,10 +1,38 @@ -''' +""" File: binary_tree.py Created Time: 2022-11-25 Author: Krahets (krahets@163.com) -''' +""" import sys, os.path as osp + sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * + +""" Driver Code """ +if __name__ == "__main__": + # 初始化二叉树 + # 初始化节点 + n1 = TreeNode(val=1) + n2 = TreeNode(val=2) + n3 = TreeNode(val=3) + n4 = TreeNode(val=4) + n5 = TreeNode(val=5) + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 + print_tree(n1) + + # 插入与删除结点 + P = TreeNode(0) + + # 在 n1 -> n2 中间插入节点 P + n1.left = P + P.left = n2 + print_tree(n1) + + # 删除结点 + n1.left = n2 + print_tree(n1) diff --git a/codes/python/chapter_tree/binary_tree_bfs.py b/codes/python/chapter_tree/binary_tree_bfs.py index 43f192206..57d15f919 100644 --- a/codes/python/chapter_tree/binary_tree_bfs.py +++ b/codes/python/chapter_tree/binary_tree_bfs.py @@ -1,10 +1,45 @@ -''' +""" File: binary_tree_bfs.py Created Time: 2022-11-25 Author: Krahets (krahets@163.com) -''' +""" import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * + +def hierOrder(root): + # 初始化队列,加入根结点 + queue = collections.deque() + queue.append(root) + # 初始化一个列表,用于保存遍历序列 + result = [] + while queue: + # 队列出队 + node = queue.popleft() + # 保存节点值 + result.append(node.val) + if node.left is not None: + # 左子结点入队 + queue.append(node.left) + if node.right is not None: + # 右子结点入队 + queue.append(node.right) + return result + + +""" Driver Code """ +if __name__ == "__main__": + # 初始化二叉树 + # 这里借助了一个从数组直接生成二叉树的函数 + root = list_to_tree( + arr=[1, 2, 3, 4, 5, 6, 7, None, None, None, None, None, None, None, None] + ) + print("\n初始化二叉树\n") + print_tree(root) + + # 层序遍历 + result = hierOrder(root) + print("\n层序遍历的结点打印序列 = ", result) + assert result == [1, 2, 3, 4, 5, 6, 7] \ No newline at end of file diff --git a/codes/python/chapter_tree/binary_tree_dfs.py b/codes/python/chapter_tree/binary_tree_dfs.py index f8415ef56..6c5b92a3f 100644 --- a/codes/python/chapter_tree/binary_tree_dfs.py +++ b/codes/python/chapter_tree/binary_tree_dfs.py @@ -1,10 +1,80 @@ -''' +""" File: binary_tree_dfs.py Created Time: 2022-11-25 Author: Krahets (krahets@163.com) -''' +""" import sys, os.path as osp sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * + +result = [] + + +def preOrder(root): + """ + 前序遍历二叉树 + """ + if root is None: + return + + # 访问优先级:根结点 -> 左子树 -> 右子树 + result.append(root.val) + preOrder(root=root.left) + preOrder(root=root.right) + + +def inOrder(root): + """ + 中序遍历二叉树 + """ + if root is None: + return + + # 访问优先级:左子树 -> 根结点 -> 右子树 + inOrder(root=root.left) + result.append(root.val) + inOrder(root=root.right) + + +def postOrder(root): + """ + 后序遍历二叉树 + """ + if root is None: + return + + # 访问优先级:左子树 -> 右子树 -> 根结点 + postOrder(root=root.left) + postOrder(root=root.right) + result.append(root.val) + + +""" Driver Code """ +if __name__ == "__main__": + # 初始化二叉树 + # 这里借助了一个从数组直接生成二叉树的函数 + root = list_to_tree( + arr=[1, 2, 3, 4, 5, 6, 7, None, None, None, None, None, None, None, None] + ) + print("\n初始化二叉树\n") + print_tree(root) + + # 前序遍历 + result = [] + preOrder(root) + print("\n前序遍历的结点打印序列 = ", result) + assert result == [1, 2, 4, 5, 3, 6, 7] + + # 中序遍历 + result = [] + inOrder(root) + print("\n中序遍历的结点打印序列 = ", result) + assert result == [4, 2, 5, 1, 6, 3, 7] + + # 后序遍历 + result = [] + postOrder(root) + print("\n后序遍历的结点打印序列 = ", result) + assert result == [4, 5, 2, 6, 7, 3, 1] \ No newline at end of file diff --git a/codes/python/include/binary_tree.py b/codes/python/include/binary_tree.py index 24acb47d4..bfe5b5f27 100644 --- a/codes/python/include/binary_tree.py +++ b/codes/python/include/binary_tree.py @@ -9,7 +9,7 @@ import collections class TreeNode: """Definition for a binary tree node """ - def __init__(self, val=0, left=None, right=None): + def __init__(self, val=None, left=None, right=None): self.val = val self.left = left self.right = right diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index bfa2391de..ef88975b2 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -48,7 +48,24 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Python" ```python title="avl_tree.py" - + class AVLTreeNode: + def __init__( + self, + val=None, + height: int = 0, + left: typing.Optional["AVLTreeNode"] = None, + right: typing.Optional["AVLTreeNode"] = None + ): + self.val = val + self.height = height + self.left = left + self.right = right + + def __str__(self): + val = self.val + left_val = self.left.val if self.left else None + right_val = self.right.val if self.right else None + return "".format(val, left_val, right_val) ``` === "Go" @@ -108,7 +125,31 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Python" ```python title="avl_tree.py" - + def height(node: typing.Optional[AVLTreeNode]) -> int: + """ + 获取结点高度 + Args: + node:起始结点 + + Returns: 高度 or -1 + + """ + # 空结点高度为 -1 ,叶结点高度为 0 + if node is not None: + return node.height + return -1 + + def update_height(node: AVLTreeNode): + """ + 更新结点高度 + Args: + node: 要更新高度的结点 + + Returns: None + + """ + # 结点高度等于最高子树高度 + 1 + node.height = max([height(node.left), height(node.right)]) + 1 ``` === "Go" @@ -166,7 +207,20 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Python" ```python title="avl_tree.py" - + def balance_factor(node: AVLTreeNode) -> int: + """ + 获取结点平衡因子 + Args: + node: 要获取平衡因子的结点 + + Returns: 平衡因子 + + """ + # 空结点平衡因子为 0 + if node is None: + return 0 + # 结点平衡因子 = 左子树高度 - 右子树高度 + return height(node.left) - height(node.right) ``` === "Go" @@ -255,7 +309,17 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - + def rightRotate(node: AVLTreeNode): + child = node.left + grand_child = child.right + # 以 child 为原点,将 node 向右旋转 + child.right = node + node.left = grand_child + # 更新结点高度 + update_height(node) + update_height(child) + # 返回旋转后子树的根节点 + return child ``` === "Go" @@ -323,7 +387,17 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - + def leftRotate(node: AVLTreeNode): + child = node.right + grand_child = child.left + # 以 child 为原点,将 node 向左旋转 + child.left = node + node.right = grand_child + # 更新结点高度 + update_height(node) + update_height(child) + # 返回旋转后子树的根节点 + return child ``` === "Go" @@ -432,7 +506,37 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - + def rotate(node: AVLTreeNode): + """ + 执行旋转操作,使该子树重新恢复平衡 + Args: + node: 要旋转的根结点 + + Returns: 旋转后的根结点 + + """ + # 获取结点 node 的平衡因子 + factor = balance_factor(node) + # 左偏树 + if factor > 1: + if balance_factor(node.left) >= 0: + # 右旋 + return right_rotate(node) + else: + # 先左旋后右旋 + node.left = left_rotate(node.left) + return right_rotate(node) + # 右偏树 + elif factor < -1: + if balance_factor(node.right) <= 0: + # 左旋 + return left_rotate(node) + else: + # 先右旋后左旋 + node.right = right_rotate(node.right) + return left_rotate(node) + # 平衡树,无需旋转,直接返回 + return node ``` === "Go" @@ -507,7 +611,42 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - + def insert(val) -> AVLTreeNode: + """ + 插入结点 + Args: + val: 结点的值 + + Returns: + node: 插入结点后的根结点 + """ + root = insert_helper(root, val) + return root + + def insert_helper(node: typing.Optional[AVLTreeNode], val: int) -> AVLTreeNode: + """ + 递归插入结点(辅助函数) + Args: + node: 要插入的根结点 + val: 要插入的结点的值 + + Returns: 插入结点后的根结点 + + """ + if node is None: + return AVLTreeNode(val) + # 1. 查找插入位置,并插入结点 + if val < node.val: + node.left = insert_helper(node.left, val) + elif val > node.val: + node.right = insert_helper(node.right, val) + else: + # 重复结点不插入,直接返回 + return node + # 更新结点高度 + update_height(node) + # 2. 执行旋转操作,使该子树重新恢复平衡 + return rotate(node) ``` === "Go" @@ -604,7 +743,62 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - + def remove(val: int): + """ + 删除结点 + Args: + val: 要删除的结点的值 + + Returns: + + """ + root = remove_helper(root, val) + return root + + def remove_helper(node: typing.Optional[AVLTreeNode], val: int) -> typing.Optional[AVLTreeNode]: + """ + 递归删除结点(辅助函数) + Args: + node: 删除的起始结点 + val: 要删除的结点的值 + + Returns: 删除目标结点后的起始结点 + + """ + if node is None: + return None + # 1. 查找结点,并删除之 + if val < node.val: + node.left = remove_helper(node.left, val) + elif val > node.val: + node.right = remove_helper(node.right, val) + else: + if node.left is None or node.right is None: + child = node.left or node.right + # 子结点数量 = 0 ,直接删除 node 并返回 + if child is None: + return None + # 子结点数量 = 1 ,直接删除 node + else: + node = child + else: # 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 + temp = min_node(node.right) + node.right = remove_helper(node.right, temp.val) + node.val = temp.val + # 更新结点高度 + update_height(node) + # 2. 执行旋转操作,使该子树重新恢复平衡 + return rotate(node) + + + def min_node(node: typing.Optional[AVLTreeNode]) -> typing.Optional[AVLTreeNode]: + # 获取最小结点 + if node is None: + return None + # 循环访问左子结点,直到叶结点时为最小结点,跳出 + while node.left is not None: + node = node.left + return node ``` === "Go" diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index 9ff364329..a9328ddc6 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -82,6 +82,23 @@ comments: true === "Python" ```python title="binary_search_tree.py" + def search(self, num): + """ + 查找结点 + """ + cur = self.get_root() + # 循环查找,越过叶结点后跳出 + while cur is not None: + # 目标结点在 root 的右子树中 + if cur.val < num: + cur = cur.right + # 目标结点在 root 的左子树中 + elif cur.val > num: + cur = cur.left + # 找到目标结点,跳出循环 + else: + break + return cur ``` @@ -228,7 +245,37 @@ comments: true === "Python" ```python title="binary_search_tree.py" + def insert(self, num): + """ + 插入结点 + """ + root = self.get_root() + # 若树为空,直接提前返回 + if root is None: + return None + cur = root + pre = None + + # 循环查找,越过叶结点后跳出 + while cur is not None: + # 找到重复结点,直接返回 + if cur.val == num: + return None + pre = cur + + if cur.val < num: # 插入位置在 root 的右子树中 + cur = cur.right + else: # 插入位置在 root 的左子树中 + cur = cur.left + + # 插入结点 val + node = TreeNode(num) + if pre.val < num: + pre.right = node + else: + pre.left = node + return node ``` === "Go" @@ -483,7 +530,64 @@ comments: true === "Python" ```python title="binary_search_tree.py" + def remove(self, num): + """ + 删除结点 + """ + root = self.get_root() + # 若树为空,直接提前返回 + if root is None: + return None + + cur = root + pre = None + # 循环查找,越过叶结点后跳出 + while cur is not None: + # 找到待删除结点,跳出循环 + if cur.val == num: + break + pre = cur + if cur.val < num: # 待删除结点在 root 的右子树中 + cur = cur.right + else: # 待删除结点在 root 的左子树中 + cur = cur.left + + # 若无待删除结点,则直接返回 + if cur is None: + return None + + # 子结点数量 = 0 or 1 + if cur.left is None or cur.right is None: + # 当子结点数量 = 0 / 1 时, child = null / 该子结点 + child = cur.left or cur.right + # 删除结点 cur + if pre.left == cur: + pre.left = child + else: + pre.right = child + # 子结点数量 = 2 + else: + # 获取中序遍历中 cur 的下一个结点 + nex = self.min(cur.right) + tmp = nex.val + # 递归删除结点 nex + self.remove(nex.val) + # 将 nex 的值复制给 cur + cur.val = tmp + return cur + + def min(self, root): + """ + 获取最小结点 + """ + if root is None: + return root + + # 循环访问左子结点,直到叶结点时为最小结点,跳出 + while root.left is not None: + root = root.left + return root ``` === "Go" diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 243a0753f..ab67e6f80 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -33,9 +33,9 @@ comments: true === "Python" ```python title="" - """ 链表结点类 """ class TreeNode: - def __init__(self, val=0, left=None, right=None): + """ 链表结点类 """ + def __init__(self, val=None, left=None, right=None): self.val = val # 结点值 self.left = left # 左子结点指针 self.right = right # 右子结点指针 @@ -190,7 +190,18 @@ comments: true === "Python" ```python title="binary_tree.py" - + # 初始化二叉树 + # 初始化节点 + n1 = TreeNode(val=1) + n2 = TreeNode(val=2) + n3 = TreeNode(val=3) + n4 = TreeNode(val=4) + n5 = TreeNode(val=5) + # 构建引用指向(即指针) + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 ``` === "Go" @@ -288,7 +299,13 @@ comments: true === "Python" ```python title="binary_tree.py" - + # 插入与删除结点 + p = TreeNode(0) + # 在 n1 -> n2 中间插入结点 P + n1.left = p + p.left = n2 + # 删除节点 P + n1.left = n2 ``` === "Go" @@ -406,7 +423,24 @@ comments: true === "Python" ```python title="binary_tree_bfs.py" - + def hierOrder(root): + # 初始化队列,加入根结点 + queue = collections.deque() + queue.append(root) + # 初始化一个列表,用于保存遍历序列 + result = [] + while queue: + # 队列出队 + node = queue.popleft() + # 保存节点值 + result.append(node.val) + if node.left is not None: + # 左子结点入队 + queue.append(node.left) + if node.right is not None: + # 右子结点入队 + queue.append(node.right) + return result ``` === "Go" @@ -578,7 +612,43 @@ comments: true === "Python" ```python title="binary_tree_dfs.py" + def preOrder(root): + """ + 前序遍历二叉树 + """ + if root is None: + return + + # 访问优先级:根结点 -> 左子树 -> 右子树 + result.append(root.val) + preOrder(root=root.left) + preOrder(root=root.right) + + def inOrder(root): + """ + 中序遍历二叉树 + """ + if root is None: + return + + # 访问优先级:左子树 -> 根结点 -> 右子树 + inOrder(root=root.left) + result.append(root.val) + inOrder(root=root.right) + + + def postOrder(root): + """ + 后序遍历二叉树 + """ + if root is None: + return + + # 访问优先级:左子树 -> 右子树 -> 根结点 + postOrder(root=root.left) + postOrder(root=root.right) + result.append(root.val) ``` === "Go" From 0a72d37578ccf799d6af4d7010b7f86bb7d7b69d Mon Sep 17 00:00:00 2001 From: Meng Du Date: Wed, 21 Dec 2022 18:57:28 +0800 Subject: [PATCH 002/158] create c folder and finished array --- codes/c/chapter_array_and_linkedlist/array.c | 124 +++++++++++++++++++ codes/c/include/PrintUtil.h | 30 +++++ codes/c/include/include.h | 11 ++ 3 files changed, 165 insertions(+) create mode 100644 codes/c/chapter_array_and_linkedlist/array.c create mode 100644 codes/c/include/PrintUtil.h create mode 100644 codes/c/include/include.h diff --git a/codes/c/chapter_array_and_linkedlist/array.c b/codes/c/chapter_array_and_linkedlist/array.c new file mode 100644 index 000000000..6217a566f --- /dev/null +++ b/codes/c/chapter_array_and_linkedlist/array.c @@ -0,0 +1,124 @@ +/* + * File: array.c + * Created Time: 2022-12-20 + * Author: MolDuM (moldum@163.com) + */ + +#include "../include/include.h" + +/* 随机返回一个数组元素 */ +int randomAccess(int* nums, int size) { + // 在区间 [0, size) 中随机抽取一个数字 + int randomIndex = rand() % size; + // 获取并返回随机元素 + int randomNum = nums[randomIndex]; + return randomNum; +} + +/* 扩展数组长度 */ +int* extend(int* nums, int size, int enlarge) { + // 初始化一个扩展长度后的数组 + int* res = (int *)malloc(sizeof(int) * (size + enlarge)); + // 将原数组中的所有元素复制到新数组 + for (int i = 0; i < size; i++) { + res[i] = nums[i]; + } + // 初始化扩展后的空间 + for (int i = size; i < size + enlarge; i++) { + res[i] = 0; + } + // 返回扩展后的新数组 + return res; +} + +/* 在数组的索引 index 处插入元素 num */ +void insert(int* nums, int size, int num, int index) { + // 把索引 index 以及之后的所有元素向后移动一位 + for (int i = size - 1; i > index; i--) { + nums[i] = nums[i - 1]; + } + // 将 num 赋给 index 处元素 + nums[index] = num; +} + +/* 删除索引 index 处元素 */ +void removeItem(int* nums, int size, int index) { + // 把索引 index 之后的所有元素向前移动一位 + for (int i = index; i < size - 1; i++) { + nums[i] = nums[i + 1]; + } +} + +/* 遍历数组 */ +void traverse(int* nums, int size) { + int count = 0; + // 通过索引遍历数组 + for (int i = 0; i < size; i++) { + count++; + } +} + +/* 在数组中查找指定元素 */ +int find(int* nums, int size, int target) { + for (int i = 0; i < size; i++) { + if (nums[i] == target) + return i; + } + return -1; +} + + +/* Driver Code */ +int main() { + /* 初始化数组 */ + int size = 5; + int arr[5]; + printf("数组 arr = "); + printArray(arr, size); + + int arr2[5] = { 1, 3, 2, 5, 4 }; + printf("数组 arr2 = "); + printArray(arr2, size); + + int* nums = (int *)malloc(sizeof(int) * size); + nums[0] = 1; + nums[1] = 3; + nums[2] = 2; + nums[3] = 5; + nums[4] = 4; + printf("数组 nums = "); + printArray(nums, size); + + /* 随机访问 */ + int randomNum = randomAccess(nums, size); + printf("在 nums 中获取随机元素 %d", randomNum); + + /* 长度扩展 */ + int enlarge = 3; + int* res = extend(nums, size, enlarge); + int* temp = nums; + nums = res; + free(temp); + size += enlarge; + printf("将数组长度扩展至 8 ,得到 nums = "); + printArray(nums, size); + + /* 插入元素 */ + insert(nums, size, 6, 3); + printf("在索引 3 处插入数字 6 ,得到 nums = "); + printArray(nums, size); + + /* 删除元素 */ + removeItem(nums, size, 2); + printf("删除索引 2 处的元素,得到 nums = "); + printArray(nums, size); + + /* 遍历数组 */ + traverse(nums, size); + + /* 查找元素 */ + int index = find(nums, size, 3); + printf("在 nums 中查找元素 3 ,得到索引 = %d\n", index); + + return 0; +} diff --git a/codes/c/include/PrintUtil.h b/codes/c/include/PrintUtil.h new file mode 100644 index 000000000..e471f0388 --- /dev/null +++ b/codes/c/include/PrintUtil.h @@ -0,0 +1,30 @@ +/* + * File: PrintUtil.h + * Created Time: 2022-12-21 + * Author: MolDum (moldum@163.com) + */ + +#include +#include +#include + +// #include "ListNode.h" +// #include "TreeNode.h" + +/** + * @brief Print an Array + * + * @param arr + * @param n + */ + +static void printArray(int* arr, int n) +{ + printf("["); + for (int i = 0; i < n - 1; i++) { + printf("%d, ", arr[i]); + } + printf("%d]\n", arr[n-1]); +} + + diff --git a/codes/c/include/include.h b/codes/c/include/include.h new file mode 100644 index 000000000..aba6b4924 --- /dev/null +++ b/codes/c/include/include.h @@ -0,0 +1,11 @@ +/* + * File: include.h + * Created Time: 2022-12-20 + * Author: MolDuM (moldum@163.com) + */ + +#include +#include +#include + +#include "PrintUtil.h" \ No newline at end of file From 770e3ca4ce052cba5936a9a0874cc2b64a1876e4 Mon Sep 17 00:00:00 2001 From: L-Super <40905056+L-Super@users.noreply.github.com> Date: Mon, 26 Dec 2022 10:17:35 +0800 Subject: [PATCH 003/158] Update bubble_sort.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C++使用std::swap()交换数组,同时添加C语言代码,作为原始C++代码的补充 --- docs/chapter_sorting/bubble_sort.md | 49 ++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index f44b11924..522f17af5 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -85,9 +85,7 @@ comments: true for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; + std::swap(nums[j], nums[j+1]); } } } @@ -170,7 +168,24 @@ comments: true === "C" ```c title="bubble_sort.c" - + /* 冒泡排序 */ + void bubble_sort(int nums[], int size) + { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = 0; i < size - 1; i++) + { + // 内循环:冒泡操作 + for (int j = 0; j < size - 1 - i; j++) + { + if (nums[j] > nums[j + 1]) + { + int temp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = temp; + } + } + } + } ``` === "C#" @@ -250,9 +265,7 @@ comments: true for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; + std::swap(nums[j], nums[j+1]); flag = true; // 记录交换元素 } } @@ -352,7 +365,27 @@ comments: true === "C" ```c title="bubble_sort.c" - + /* 冒泡排序 */ + void bubble_sort(int nums[], int size) + { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = 0; i < size - 1; i++) + { + bool flag = false; + // 内循环:冒泡操作 + for (int j = 0; j < size - 1 - i; j++) + { + if (nums[j] > nums[j + 1]) + { + int temp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = temp; + flag = true; + } + } + if(!falg) break; + } + } ``` === "C#" From 75be76cebeaa6f6458349d69c5e1885cff613646 Mon Sep 17 00:00:00 2001 From: L-Super <40905056+L-Super@users.noreply.github.com> Date: Mon, 26 Dec 2022 12:11:21 +0800 Subject: [PATCH 004/158] Update bubble_sort.md fixed `falg` to `flag` --- docs/chapter_sorting/bubble_sort.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index 522f17af5..f71dce77d 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -383,7 +383,7 @@ comments: true flag = true; } } - if(!falg) break; + if(!flag) break; } } ``` From 2ee6fcfef114070012cdb5bb35cdaed25bd7af7a Mon Sep 17 00:00:00 2001 From: Listening <120311070@qq.com> Date: Mon, 26 Dec 2022 12:43:37 +0800 Subject: [PATCH 005/158] add bubble sort in C code --- codes/c/chapter_sorting/bubble_sort.c | 72 +++++++++++++++++++++++++++ codes/c/include/include.h | 2 + 2 files changed, 74 insertions(+) create mode 100644 codes/c/chapter_sorting/bubble_sort.c create mode 100644 codes/c/include/include.h diff --git a/codes/c/chapter_sorting/bubble_sort.c b/codes/c/chapter_sorting/bubble_sort.c new file mode 100644 index 000000000..3d23a5420 --- /dev/null +++ b/codes/c/chapter_sorting/bubble_sort.c @@ -0,0 +1,72 @@ +/** + * @file bubble_sort.c + * @author Listening (https://github.com/L-Super) + * @brief + * @date 2022-12-26 + * + */ + +#include "../include/include.h" + +/* 冒泡排序 */ +void bubble_sort(int nums[], int size) +{ + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = 0; i < size - 1; i++) + { + // 内循环:冒泡操作 + for (int j = 0; j < size - 1 - i; j++) + { + if (nums[j] > nums[j + 1]) + { + int temp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = temp; + } + } + } +} + +/* 冒泡排序(标志优化)*/ +void bubble_sort_with_flag(int nums[], int size) +{ + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = 0; i < size - 1; i++) + { + bool flag = false; + // 内循环:冒泡操作 + for (int j = 0; j < size - 1 - i; j++) + { + if (nums[j] > nums[j + 1]) + { + int temp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = temp; + flag = true; + } + } + if (!flag) + break; + } +} + +int main() +{ + int nums[6] = {4, 1, 3, 1, 5, 2}; + printf("冒泡排序后:\n"); + bubble_sort(nums, 6); + for (int i = 0; i < 6; i++) + { + printf("%d ", nums[i]); + } + + printf("优化版冒泡排序后:\n"); + bubble_sort_with_flag(nums, 6); + for (int i = 0; i < 6; i++) + { + printf("%d ", nums[i]); + } + printf("\n"); + + return 0; +} \ No newline at end of file diff --git a/codes/c/include/include.h b/codes/c/include/include.h new file mode 100644 index 000000000..44843254e --- /dev/null +++ b/codes/c/include/include.h @@ -0,0 +1,2 @@ +#include +#include \ No newline at end of file From edf1029ac4e59aa923f59ba0508bbea4eda5185d Mon Sep 17 00:00:00 2001 From: Listening <120311070@qq.com> Date: Mon, 26 Dec 2022 13:34:50 +0800 Subject: [PATCH 006/158] fixed the format of the file header --- codes/c/chapter_sorting/bubble_sort.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/codes/c/chapter_sorting/bubble_sort.c b/codes/c/chapter_sorting/bubble_sort.c index 3d23a5420..f59b81d00 100644 --- a/codes/c/chapter_sorting/bubble_sort.c +++ b/codes/c/chapter_sorting/bubble_sort.c @@ -1,9 +1,7 @@ /** - * @file bubble_sort.c - * @author Listening (https://github.com/L-Super) - * @brief - * @date 2022-12-26 - * + * File: bubble_sort.c + * Created Time: 2022-12-26 + * Author: Listening (https://github.com/L-Super) */ #include "../include/include.h" From ae9b010894caeecef658dd47c0cd7e524f98fef8 Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Mon, 26 Dec 2022 23:28:45 +0800 Subject: [PATCH 007/158] feat: add Swift installation --- docs/chapter_preface/installation.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/chapter_preface/installation.md b/docs/chapter_preface/installation.md index b58457c7d..c6c1b2ee9 100644 --- a/docs/chapter_preface/installation.md +++ b/docs/chapter_preface/installation.md @@ -41,3 +41,8 @@ comments: true 1. 下载并安装 [.Net 6.0](https://dotnet.microsoft.com/en-us/download) ; 2. 在 VSCode 的插件市场中搜索 `c#` ,安装 c# 。 + +## Swift 环境 + +1. 下载并安装 [Swift](https://www.swift.org/download/); +2. 在 VSCode 的插件市场中搜索 `swift`,安装 [Swift for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang)。 From 7e1ff8f74177be403888bbf6a10a3e0d2ab0ac4c Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Mon, 26 Dec 2022 23:29:37 +0800 Subject: [PATCH 008/158] feat: add Swift codes for time complexity article --- .../time_complexity.swift | 170 +++++++++++ .../worst_best_time_complexity.swift | 37 +++ .../time_complexity.md | 276 ++++++++++++++++++ 3 files changed, 483 insertions(+) create mode 100644 codes/swift/chapter_computational_complexity/time_complexity.swift create mode 100644 codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift diff --git a/codes/swift/chapter_computational_complexity/time_complexity.swift b/codes/swift/chapter_computational_complexity/time_complexity.swift new file mode 100644 index 000000000..38bac4fae --- /dev/null +++ b/codes/swift/chapter_computational_complexity/time_complexity.swift @@ -0,0 +1,170 @@ +/* + * File: time_complexity.swift + * Created Time: 2022-12-26 + * Author: nuomi1 (nuomi1@qq.com) + */ + +// 常数阶 +func constant(n: Int) -> Int { + var count = 0 + let size = 100_000 + for _ in 0 ..< size { + count += 1 + } + return count +} + +// 线性阶 +func linear(n: Int) -> Int { + var count = 0 + for _ in 0 ..< n { + count += 1 + } + return count +} + +// 线性阶(遍历数组) +func arrayTraversal(nums: [Int]) -> Int { + var count = 0 + // 循环次数与数组长度成正比 + for _ in nums { + count += 1 + } + return count +} + +// 平方阶 +func quadratic(n: Int) -> Int { + var count = 0 + // 循环次数与数组长度成平方关系 + for _ in 0 ..< n { + for _ in 0 ..< n { + count += 1 + } + } + return count +} + +// 平方阶(冒泡排序) +func bubbleSort(nums: inout [Int]) -> Int { + var count = 0 // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for i in sequence(first: nums.count - 1, next: { $0 > 0 ? $0 - 1 : nil }) { + // 内循环:冒泡操作 + for j in 0 ..< i { + if nums[j] > nums[j + 1] { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = tmp + count += 3 // 元素交换包含 3 个单元操作 + } + } + } + return count +} + +// 指数阶(循环实现) +func exponential(n: Int) -> Int { + var count = 0 + var base = 1 + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for _ in 0 ..< n { + for _ in 0 ..< base { + count += 1 + } + base *= 2 + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count +} + +// 指数阶(递归实现) +func expRecur(n: Int) -> Int { + if n == 1 { + return 1 + } + return expRecur(n: n - 1) + expRecur(n: n - 1) + 1 +} + +// 对数阶(循环实现) +func logarithmic(n: Int) -> Int { + var count = 0 + var n = n + while n > 1 { + n = n / 2 + count += 1 + } + return count +} + +// 对数阶(递归实现) +func logRecur(n: Int) -> Int { + if n <= 1 { + return 0 + } + return logRecur(n: n / 2) + 1 +} + +// 线性对数阶 +func linearLogRecur(n: Double) -> Int { + if n <= 1 { + return 1 + } + var count = linearLogRecur(n: n / 2) + linearLogRecur(n: n / 2) + for _ in 0 ..< Int(n) { + count += 1 + } + return count +} + +// 阶乘阶(递归实现) +func factorialRecur(n: Int) -> Int { + if n == 0 { + return 1 + } + var count = 0 + // 从 1 个分裂出 n 个 + for _ in 0 ..< n { + count += factorialRecur(n: n - 1) + } + return count +} + +func main() { + // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 + let n = 8 + print("输入数据大小 n =", n) + + var count = constant(n: n) + print("常数阶的计算操作数量 =", count) + + count = linear(n: n) + print("线性阶的计算操作数量 =", count) + count = arrayTraversal(nums: Array(repeating: 0, count: n)) + print("线性阶(遍历数组)的计算操作数量 =", count) + + count = quadratic(n: n) + print("平方阶的计算操作数量 =", count) + var nums = Array(sequence(first: n, next: { $0 > 0 ? $0 - 1 : nil })) // [n,n-1,...,2,1] + count = bubbleSort(nums: &nums) + print("平方阶(冒泡排序)的计算操作数量 =", count) + + count = exponential(n: n) + print("指数阶(循环实现)的计算操作数量 =", count) + count = expRecur(n: n) + print("指数阶(递归实现)的计算操作数量 =", count) + + count = logarithmic(n: n) + print("对数阶(循环实现)的计算操作数量 =", count) + count = logRecur(n: n) + print("对数阶(递归实现)的计算操作数量 =", count) + + count = linearLogRecur(n: Double(n)) + print("线性对数阶(递归实现)的计算操作数量 =", count) + + count = factorialRecur(n: n) + print("阶乘阶(递归实现)的计算操作数量 =", count) +} + +main() diff --git a/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift b/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift new file mode 100644 index 000000000..73db6954d --- /dev/null +++ b/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift @@ -0,0 +1,37 @@ +/* + * File: worst_best_time_complexity.swift + * Created Time: 2022-12-26 + * Author: nuomi1 (nuomi1@qq.com) + */ + +// 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 +func randomNumbers(n: Int) -> [Int] { + // 生成数组 nums = { 1, 2, 3, ..., n } + var nums = Array(1 ... n) + // 随机打乱数组元素 + nums.shuffle() + return nums +} + +// 查找数组 nums 中数字 1 所在索引 +func findOne(nums: [Int]) -> Int { + for i in nums.indices { + if nums[i] == 1 { + return i + } + } + return -1 +} + +// Driver Code +func main() { + for _ in 0 ..< 10 { + let n = 100 + let nums = randomNumbers(n: n) + let index = findOne(nums: nums) + print("数组 [ 1, 2, ..., n ] 被打乱后 =", nums) + print("数字 1 的索引为", index) + } +} + +main() diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 8841a613c..aafb0d893 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -111,6 +111,21 @@ $$ } ``` +=== "Swift" + + ```swift title="" + // 在某运行平台下 + func algorithm(_ n: Int) { + var a = 2 // 1 ns + a = a + 1 // 1 ns + a = a * 2 // 10 ns + // 循环 n 次 + for _ in 0 ..< n { // 1 ns + print(0) // 5 ns + } + } + ``` + 但实际上, **统计算法的运行时间既不合理也不现实。** 首先,我们不希望预估时间和运行平台绑定,毕竟算法需要跑在各式各样的平台之上。其次,我们很难获知每一种操作的运行时间,这为预估过程带来了极大的难度。 ## 统计时间增长趋势 @@ -246,6 +261,29 @@ $$ } ``` +=== "Swift" + + ```swift title="" + // 算法 A 时间复杂度:常数阶 + func algorithmA(_ n: Int) { + print(0) + } + + // 算法 B 时间复杂度:线性阶 + func algorithmB(_ n: Int) { + for _ in 0 ..< n { + print(0) + } + } + + // 算法 C 时间复杂度:常数阶 + func algorithmC(_ n: Int) { + for _ in 0 ..< 1000000 { + print(0) + } + } + ``` + ![time_complexity_first_example](time_complexity.assets/time_complexity_first_example.png)

Fig. 算法 A, B, C 的时间增长趋势

@@ -352,6 +390,20 @@ $$ } ``` +=== "Swift" + + ```swift title="" + func algorithm(n: Int) { + var a = 1 // +1 + a = a + 1 // +1 + a = a * 2 // +1 + // 循环 n 次 + for _ in 0 ..< n { // +1 + print(0) // +1 + } + } + ``` + $T(n)$ 是个一次函数,说明时间增长趋势是线性的,因此易得时间复杂度是线性阶。 我们将线性阶的时间复杂度记为 $O(n)$ ,这个数学符号被称为「大 $O$ 记号 Big-$O$ Notation」,代表函数 $T(n)$ 的「渐近上界 asymptotic upper bound」。 @@ -516,6 +568,25 @@ $$ } ``` +=== "Swift" + + ```swift title="" + func algorithm(n: Int) { + var a = 1 // +0(技巧 1) + a = a + n // +0(技巧 1) + // +n(技巧 2) + for _ in 0 ..< (5 * n + 1) { + print(0) + } + // +n*n(技巧 3) + for _ in 0 ..< (2 * n) { + for _ in 0 ..< (n + 1) { + print(0) + } + } + } + ``` + ### 2. 判断渐近上界 **时间复杂度由多项式 $T(n)$ 中最高阶的项来决定**。这是因为在 $n$ 趋于无穷大时,最高阶的项将处于主导作用,其它项的影响都可以被忽略。 @@ -643,6 +714,20 @@ $$ } ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 常数阶 + func constant(n: Int) -> Int { + var count = 0 + let size = 100000 + for _ in 0 ..< size { + count += 1 + } + return count + } + ``` + ### 线性阶 $O(n)$ 线性阶的操作数量相对输入数据大小成线性级别增长。线性阶常出现于单层循环。 @@ -726,6 +811,19 @@ $$ } ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 线性阶 + func linear(n: Int) -> Int { + var count = 0 + for _ in 0 ..< n { + count += 1 + } + return count + } + ``` + 「遍历数组」和「遍历链表」等操作,时间复杂度都为 $O(n)$ ,其中 $n$ 为数组或链表的长度。 !!! tip @@ -820,6 +918,20 @@ $$ } ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 线性阶(遍历数组) + func arrayTraversal(nums: [Int]) -> Int { + var count = 0 + // 循环次数与数组长度成正比 + for _ in nums { + count += 1 + } + return count + } + ``` + ### 平方阶 $O(n^2)$ 平方阶的操作数量相对输入数据大小成平方级别增长。平方阶常出现于嵌套循环,外层循环和内层循环都为 $O(n)$ ,总体为 $O(n^2)$ 。 @@ -922,6 +1034,22 @@ $$ } ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 平方阶 + func quadratic(n: Int) -> Int { + var count = 0 + // 循环次数与数组长度成平方关系 + for _ in 0 ..< n { + for _ in 0 ..< n { + count += 1 + } + } + return count + } + ``` + ![time_complexity_constant_linear_quadratic](time_complexity.assets/time_complexity_constant_linear_quadratic.png)

Fig. 常数阶、线性阶、平方阶的时间复杂度

@@ -1066,6 +1194,29 @@ $$ ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 平方阶(冒泡排序) + func bubbleSort(nums: inout [Int]) -> Int { + var count = 0 // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for i in sequence(first: nums.count - 1, next: { $0 > 0 ? $0 - 1 : nil }) { + // 内循环:冒泡操作 + for j in 0 ..< i { + if nums[j] > nums[j + 1] { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = tmp + count += 3 // 元素交换包含 3 个单元操作 + } + } + } + return count + } + ``` + ### 指数阶 $O(2^n)$ !!! note @@ -1182,6 +1333,25 @@ $$ } ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 指数阶(循环实现) + func exponential(n: Int) -> Int { + var count = 0 + var base = 1 + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for _ in 0 ..< n { + for _ in 0 ..< base { + count += 1 + } + base *= 2 + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count + } + ``` + ![time_complexity_exponential](time_complexity.assets/time_complexity_exponential.png)

Fig. 指数阶的时间复杂度

@@ -1258,6 +1428,18 @@ $$ } ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 指数阶(递归实现) + func expRecur(n: Int) -> Int { + if n == 1 { + return 1 + } + return expRecur(n: n - 1) + expRecur(n: n - 1) + 1 + } + ``` + ### 对数阶 $O(\log n)$ 对数阶与指数阶正好相反,后者反映“每轮增加到两倍的情况”,而前者反映“每轮缩减到一半的情况”。对数阶仅次于常数阶,时间增长的很慢,是理想的时间复杂度。 @@ -1354,6 +1536,21 @@ $$ } ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 对数阶(循环实现) + func logarithmic(n: Int) -> Int { + var count = 0 + var n = n + while n > 1 { + n = n / 2 + count += 1 + } + return count + } + ``` + ![time_complexity_logarithmic](time_complexity.assets/time_complexity_logarithmic.png)

Fig. 对数阶的时间复杂度

@@ -1430,6 +1627,18 @@ $$ } ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 对数阶(递归实现) + func logRecur(n: Int) -> Int { + if n <= 1 { + return 0 + } + return logRecur(n: n / 2) + 1 + } + ``` + ### 线性对数阶 $O(n \log n)$ 线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 $O(\log n)$ 和 $O(n)$ 。 @@ -1531,6 +1740,22 @@ $$ } ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 线性对数阶 + func linearLogRecur(n: Double) -> Int { + if n <= 1 { + return 1 + } + var count = linearLogRecur(n: n / 2) + linearLogRecur(n: n / 2) + for _ in 0 ..< Int(n) { + count += 1 + } + return count + } + ``` + ![time_complexity_logarithmic_linear](time_complexity.assets/time_complexity_logarithmic_linear.png)

Fig. 线性对数阶的时间复杂度

@@ -1640,6 +1865,23 @@ $$ } ``` +=== "Swift" + + ```swift title="time_complexity.swift" + // 阶乘阶(递归实现) + func factorialRecur(n: Int) -> Int { + if n == 0 { + return 1 + } + var count = 0 + // 从 1 个分裂出 n 个 + for _ in 0 ..< n { + count += factorialRecur(n: n - 1) + } + return count + } + ``` + ![time_complexity_factorial](time_complexity.assets/time_complexity_factorial.png)

Fig. 阶乘阶的时间复杂度

@@ -1872,6 +2114,40 @@ $$ } ``` +=== "Swift" + + ```swift title="" + // 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 + func randomNumbers(n: Int) -> [Int] { + // 生成数组 nums = { 1, 2, 3, ..., n } + var nums = Array(1 ... n) + // 随机打乱数组元素 + nums.shuffle() + return nums + } + + // 查找数组 nums 中数字 1 所在索引 + func findOne(nums: [Int]) -> Int { + for i in nums.indices { + if nums[i] == 1 { + return i + } + } + return -1 + } + + // Driver Code + func main() { + for _ in 0 ..< 10 { + let n = 100 + let nums = randomNumbers(n: n) + let index = findOne(nums: nums) + print("数组 [ 1, 2, ..., n ] 被打乱后 =", nums) + print("数字 1 的索引为", index) + } + } + ``` + !!! tip 我们在实际应用中很少使用「最佳时间复杂度」,因为往往只有很小概率下才能达到,会带来一定的误导性。反之,「最差时间复杂度」最为实用,因为它给出了一个“效率安全值”,让我们可以放心地使用算法。 From 845a68d6f1cc932a6057a8624ebb886ac614b8ee Mon Sep 17 00:00:00 2001 From: danielsss Date: Tue, 27 Dec 2022 09:30:20 +1100 Subject: [PATCH 009/158] Add the TypeScript code and docs for Chapter of Binary Search --- docs/chapter_searching/binary_search.md | 59 ++++++++++++++++++++----- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index a74c07aaf..32ae411c0 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -116,17 +116,17 @@ $$ === "Go" ```go title="binary_search.go" - /* 二分查找(左闭右开) */ - func binarySearch1(nums []int, target int) int { - // 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - i, j := 0, len(nums) - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - for i < j { + /* 二分查找(双闭区间) */ + func binarySearch(nums []int, target int) int { + // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + i, j := 0, len(nums)-1 + // 循环,当搜索区间为空时跳出(当 i > j 时为空) + for i <= j { m := (i + j) / 2 // 计算中点索引 m - if nums[m] < target { // 此情况说明 target 在区间 [m+1, j) 中 + if nums[m] < target { // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1 - } else if nums[m] > target { // 此情况说明 target 在区间 [i, m) 中 - j = m + } else if nums[m] > target { // 此情况说明 target 在区间 [i, m-1] 中 + j = m - 1 } else { // 找到目标元素,返回其索引 return m } @@ -161,7 +161,23 @@ $$ === "TypeScript" ```typescript title="binary_search.ts" - + /* 二分查找(左闭右开) */ + const binarySearch1 = function (nums: number[], target: number): number { + // 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + let i = 0, j = nums.length; + // 循环,当搜索区间为空时跳出(当 i = j 时为空) + while (i < j) { + let m = Math.floor(i + (j - i) / 2); // 计算中点索引 m + if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中 + i = m + 1; + } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m) 中 + j = m; + } else { // 找到目标元素,返回其索引 + return m; + } + } + return -1; // 未找到目标元素,返回 -1 + } ``` === "C" @@ -309,7 +325,23 @@ $$ === "TypeScript" ```typescript title="binary_search.ts" - + /* 二分查找(左闭右开) */ + const binarySearch1 = function (nums: number[], target: number): number { + // 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + let i = 0, j = nums.length; + // 循环,当搜索区间为空时跳出(当 i = j 时为空) + while (i < j) { + let m = Math.floor(i + (j - i) / 2); // 计算中点索引 m + if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中 + i = m + 1; + } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m) 中 + j = m; + } else { // 找到目标元素,返回其索引 + return m; + } + } + return -1; // 未找到目标元素,返回 -1 + } ``` === "C" @@ -407,7 +439,10 @@ $$ === "TypeScript" ```typescript title="" - + // (i + j) 有可能超出 Number 的取值范围 + let m = Math.floor((i + j) / 2); + // 更换为此写法则不会越界 + let m = Math.floor(i + (j - i) / 2); ``` === "C" From 67d7cba977e0f97b3a648ea53fdd2011bc50a95b Mon Sep 17 00:00:00 2001 From: danielsss Date: Tue, 27 Dec 2022 09:30:34 +1100 Subject: [PATCH 010/158] Add the TypeScript code and docs for Chapter of Binary Search --- .editorconfig | 16 ++++++ .../chapter_searching/binary_search.ts | 54 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 .editorconfig create mode 100644 codes/typescript/chapter_searching/binary_search.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..53ad327c8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{ts,js,py,java,go,c,cpp,cs}] +charset = utf-8 + +# 4 space indentation +[*.{ts,js,py,java,go,c,cpp,cs}] +indent_style = space +indent_size = 4 \ No newline at end of file diff --git a/codes/typescript/chapter_searching/binary_search.ts b/codes/typescript/chapter_searching/binary_search.ts new file mode 100644 index 000000000..0a96720d9 --- /dev/null +++ b/codes/typescript/chapter_searching/binary_search.ts @@ -0,0 +1,54 @@ +/* +* File: binary_search.ts +* Created Time: 2022-12-27 +* Author: Daniel (better.sunjian@gmail.com) +*/ + +/* 二分查找(双闭区间) */ +const binarySearch = function (nums: number[], target: number): number { + // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + let i = 0, j = nums.length - 1; + // 循环,当搜索区间为空时跳出(当 i > j 时为空) + while (i <= j) { + let m = Math.floor(i + (j - i) / 2); // 计算中点索引 m + if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j] 中 + i = m + 1; + } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m-1] 中 + j = m - 1; + } else { // 找到目标元素,返回其索引 + return m; + } + } + return -1; // 未找到目标元素,返回 -1 +} + +/* 二分查找(左闭右开) */ +const binarySearch1 = function (nums: number[], target: number): number { + // 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + let i = 0, j = nums.length; + // 循环,当搜索区间为空时跳出(当 i = j 时为空) + while (i < j) { + const m = Math.floor(i + (j - i) / 2); // 计算中点索引 m + if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中 + i = m + 1; + } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m) 中 + j = m; + } else { // 找到目标元素,返回其索引 + return m; + } + } + return -1; // 未找到目标元素,返回 -1 +} + + +/* Driver Code */ +const target = 6; +const nums = [ 1, 3, 6, 8, 12, 15, 23, 67, 70, 92 ]; + +/* 二分查找(双闭区间) */ +let index = binarySearch(nums, target); +console.info('目标元素 6 的索引 = %d', index); + +/* 二分查找(左闭右开) */ +index = binarySearch1(nums, target); +console.info('目标元素 6 的索引 = %d', index); From 6264edcadb9af2692ac10c7b61ddf31478a29e43 Mon Sep 17 00:00:00 2001 From: danielsss Date: Tue, 27 Dec 2022 09:35:08 +1100 Subject: [PATCH 011/158] Removed .editorconfig --- .editorconfig | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 53ad327c8..000000000 --- a/.editorconfig +++ /dev/null @@ -1,16 +0,0 @@ -# EditorConfig is awesome: https://EditorConfig.org - -# Unix-style newlines with a newline ending every file -[*] -end_of_line = lf -insert_final_newline = true - -# Matches multiple files with brace expansion notation -# Set default charset -[*.{ts,js,py,java,go,c,cpp,cs}] -charset = utf-8 - -# 4 space indentation -[*.{ts,js,py,java,go,c,cpp,cs}] -indent_style = space -indent_size = 4 \ No newline at end of file From e0d3572a47295831ae5445c4f950a41ca4b3c9bb Mon Sep 17 00:00:00 2001 From: danielsss Date: Tue, 27 Dec 2022 09:45:33 +1100 Subject: [PATCH 012/158] Fixed format of doc --- docs/chapter_searching/binary_search.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index 32ae411c0..a059d75da 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -161,18 +161,18 @@ $$ === "TypeScript" ```typescript title="binary_search.ts" - /* 二分查找(左闭右开) */ - const binarySearch1 = function (nums: number[], target: number): number { - // 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - let i = 0, j = nums.length; - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while (i < j) { - let m = Math.floor(i + (j - i) / 2); // 计算中点索引 m - if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中 + /* 二分查找(双闭区间) */ + const binarySearch = function (nums: number[], target: number): number { + // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + let i = 0, j = nums.length - 1; + // 循环,当搜索区间为空时跳出(当 i > j 时为空) + while (i <= j) { + let m = Math.floor(i + (j - i) / 2);// 计算中点索引 m + if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1; - } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m) 中 - j = m; - } else { // 找到目标元素,返回其索引 + } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m-1] 中 + j = m - 1; + } else { // 找到目标元素,返回其索引 return m; } } From 88ce287a6f602f38d21c733505e735aeeebaeaca Mon Sep 17 00:00:00 2001 From: danielsss Date: Tue, 27 Dec 2022 09:47:17 +1100 Subject: [PATCH 013/158] Fixed format of doc --- docs/chapter_searching/binary_search.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index a059d75da..a04b95157 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -331,12 +331,12 @@ $$ let i = 0, j = nums.length; // 循环,当搜索区间为空时跳出(当 i = j 时为空) while (i < j) { - let m = Math.floor(i + (j - i) / 2); // 计算中点索引 m - if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中 + let m = Math.floor(i + (j - i) / 2);// 计算中点索引 m + if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中 i = m + 1; - } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m) 中 + } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m) 中 j = m; - } else { // 找到目标元素,返回其索引 + } else { // 找到目标元素,返回其索引 return m; } } From b1be0aab15f2b943ef3ac506e766957479296e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=9A=E5=9B=BD=E7=8E=AE?= Date: Tue, 27 Dec 2022 11:25:30 +0800 Subject: [PATCH 014/158] docs(array): sample code for golang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本次提交包含如下示例代码。 - 遍历数组; - 初始化数组; - 扩展数组长度; - 在数组中查找指定元素; - 随机返回一个数组元素; - 删除索引 index 处元素; - 在数组的索引 index 处插入元素 num。 所有数组约定长度为 5。原因如下: 在 goalng 中,必须声明数组的长度,例如:nums := [5]int{1,2,3,4,5}。如果不声明长度,则被称为切片。 使用的注释没有按照 golang 的编程惯例,而是倾向于使用文档上下文的注释约定。 所以所有函数注释均使用了 `/* ... */`,而不是双斜杠 `//`。 --- .../go/chapter_array_and_linkedlist/array.go | 83 ++++++++++ .../array_test.go | 145 ++++++++++++++++++ docs/chapter_array_and_linkedlist/array.md | 82 +++++++++- 3 files changed, 303 insertions(+), 7 deletions(-) create mode 100644 codes/go/chapter_array_and_linkedlist/array.go create mode 100644 codes/go/chapter_array_and_linkedlist/array_test.go diff --git a/codes/go/chapter_array_and_linkedlist/array.go b/codes/go/chapter_array_and_linkedlist/array.go new file mode 100644 index 000000000..26eef2a91 --- /dev/null +++ b/codes/go/chapter_array_and_linkedlist/array.go @@ -0,0 +1,83 @@ +package chapter_array_and_linkedlist + +import ( + "fmt" + "math/rand" +) + +// ExpectSize 预期大小。 在 Go 中,声明数组长度必须是常量表达式, +// 所以这里我们约定,扩展后的长度为 6 +const ExpectSize = 6 + +/* 随机返回一个数组元素 */ +func randomAccess(nums [5]int) (ans int) { + // 在区间 [0, nums.length) 中随机抽取一个数字 + randomIndex := rand.Intn(len(nums)) + // 获取并返回随机元素 + ans = nums[randomIndex] + return +} + +/* 扩展数组长度 */ +func extend(nums [5]int) [ExpectSize]int { + // 初始化一个扩展长度后的数组 + var res [ExpectSize]int + // 将原数组中的所有元素复制到新数组 + for i := 0; i < len(nums); i++ { + res[i] = nums[i] + } + // 返回扩展后的新数组 + return res +} + +/* 在数组的索引 index 处插入元素 num */ +func insert(nums *[5]int, num int, index int) { + // 把索引 index 以及之后的所有元素向后移动一位 + // 如果超出了数组长度,会被直接舍弃 + for i := len(nums) - 1; i > index; i-- { + nums[i] = nums[i-1] + } + // 将 num 赋给 index 处元素 + nums[index] = num +} + +/* 删除索引 index 处元素 */ +func remove(nums *[5]int, index int) { + // 越界检查 + if index >= len(nums) { + return + } + // 把索引 index 之后的所有元素向前移动一位 + for i := index; i < len(nums); i++ { + if i+1 >= len(nums) { + nums[len(nums)-1] = 0 + break + } + nums[i] = nums[i+1] + } +} + +/* 遍历数组 */ +func traverse(nums [5]int) { + var count int + // 通过索引遍历数组 + for i := 0; i < len(nums); i++ { + count++ + } + // 直接遍历数组 + for index, val := range nums { + fmt.Printf("index:%v value:%v\n", index, val) + } +} + +/* 在数组中查找指定元素 */ +func find(nums [5]int, target int) (ans int) { + ans = -1 + for i := 0; i < len(nums); i++ { + if nums[i] == target { + ans = i + break + } + } + return +} diff --git a/codes/go/chapter_array_and_linkedlist/array_test.go b/codes/go/chapter_array_and_linkedlist/array_test.go new file mode 100644 index 000000000..1f5510345 --- /dev/null +++ b/codes/go/chapter_array_and_linkedlist/array_test.go @@ -0,0 +1,145 @@ +package chapter_array_and_linkedlist + +import ( + "reflect" + "testing" +) + +func TestInitArray(t *testing.T) { + t.Parallel() + var arr [5]int + for _, v := range arr { + if v != 0 { + t.Fatalf("array init exception") + } + } +} + +func TestRandomAccess(t *testing.T) { + t.Parallel() + min, max := 1, 5 + nums := [5]int{min, 2, 3, 4, max} + ans := randomAccess(nums) + if ans < min || ans > max { + t.Fatalf("Expected range is greater than min: %v and less than max: %v, got ans: %v", min, max, ans) + } +} + +func TestExtend(t *testing.T) { + t.Parallel() + nums := [5]int{1, 2, 3, 4, 5} + newNums := extend(nums) + if len(newNums) != ExpectSize { + t.Fatalf("Expected len: %v, got: %v", ExpectSize, len(nums)) + } +} + +func TestInsert(t *testing.T) { + t.Parallel() + nums := [5]int{1, 2, 3, 4, 5} + insert(&nums, 5, 0) + if nums[0] != 5 { + t.Fatalf("Expected index[0] val: 5, got: %v", nums[0]) + } +} + +func TestRemove(t *testing.T) { + t.Parallel() + type fields struct { + index int + after [5]int + before [5]int + } + + tests := []struct { + name string + fields fields + }{ + { + name: "remove index[0]", + fields: struct { + index int + after [5]int + before [5]int + }{ + index: 0, + after: [5]int{2, 3, 4, 5, 0}, + before: [5]int{1, 2, 3, 4, 5}, + }, + }, + { + name: "remove end", + fields: struct { + index int + after [5]int + before [5]int + }{ + index: 4, + after: [5]int{1, 2, 3, 4, 0}, + before: [5]int{1, 2, 3, 4, 5}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := tt.fields + remove(&v.before, v.index) + if !reflect.DeepEqual(v.before, v.after) { + t.Errorf("remove(&v.before, v.index) = %v, want %v", v.before, v.after) + } + }) + } +} + +func TestTraverse(t *testing.T) { + t.Parallel() + nums := [5]int{1, 2, 3, 4, 5} + traverse(nums) +} + +func TestFind(t *testing.T) { + t.Parallel() + type fields struct { + target int + nums [5]int + } + + tests := []struct { + name string + fields fields + want int + }{ + { + name: "element exists", + fields: struct { + target int + nums [5]int + }{ + target: 1, + nums: [5]int{1, 2, 3, 4, 5}, + }, + want: 0, + }, + { + name: "element does not exist", + fields: struct { + target int + nums [5]int + }{ + target: 6, + nums: [5]int{1, 2, 3, 4, 5}, + }, + want: -1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := tt.fields + if got := find(v.nums, v.target); got != tt.want { + t.Errorf("find(v.nums, v.target) = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 3228e678f..52d23024f 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -43,7 +43,9 @@ comments: true === "Go" ```go title="array.go" - + /* 初始化数组 */ + var arr [5]int // {0, 0, 0, 0, 0} + nums := [5]int{1, 3, 2, 5, 4} ``` === "JavaScript" @@ -133,7 +135,14 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - + /* 随机返回一个数组元素 */ + func randomAccess(nums [5]int) (ans int) { + // 在区间 [0, nums.length) 中随机抽取一个数字 + randomIndex := rand.Intn(len(nums)) + // 获取并返回随机元素 + ans = nums[randomIndex] + return + } ``` === "JavaScript" @@ -236,7 +245,20 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - + // 在 Go 中,声明数组长度必须是常量表达式, + // 所以这里我们约定,扩展后的长度为 6 + const expectSize = 6 + // 扩展数组长度 + func extend(nums [5]int) [expectSize]int { + // 初始化一个扩展长度后的数组 + var res [expectSize]int + // 将原数组中的所有元素复制到新数组 + for i := 0; i < len(nums); i++ { + res[i] = nums[i] + } + // 返回扩展后的新数组 + return res + } ``` === "JavaScript" @@ -370,7 +392,32 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - + /* 在数组的索引 index 处插入元素 num */ + func insert(nums [5]int, num int, index int) { + // 把索引 index 以及之后的所有元素向后移动一位 + // 如果超出了数组长度,会被直接舍弃 + for i := len(nums) - 1; i > index; i-- { + nums[i] = nums[i-1] + } + // 将 num 赋给 index 处元素 + nums[index] = num + } + + /* 删除索引 index 处元素 */ + func remove(nums [5]int, index int) { + // 越界检查 + if index >= len(nums) { + return + } + // 把索引 index 之后的所有元素向前移动一位 + for i := index; i < len(nums); i++ { + if i+1 >= len(nums) { + nums[len(nums)-1] = 0 + break + } + nums[i] = nums[i+1] + } + } ``` === "JavaScript" @@ -499,7 +546,18 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - + /* 遍历数组 */ + func traverse(nums [5]int) { + var count int + // 通过索引遍历数组 + for i := 0; i < len(nums); i++ { + count++ + } + // 直接遍历数组 + for index, val := range nums { + fmt.Printf("index:%d value:%d\n", index, val) + } + } ``` === "JavaScript" @@ -604,7 +662,17 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - + /* 在数组中查找指定元素 */ + func find(nums [5]int, target int) (ans int){ + ans = -1 + for i := 0; i < len(nums); i++ { + if nums[i] == target { + ans = i + break + } + } + return + } ``` === "JavaScript" @@ -660,4 +728,4 @@ elementAddr = firtstElementAddr + elementLength * elementIndex **二分查找。** 例如前文查字典的例子,我们可以将字典中的所有字按照拼音顺序存储在数组中,然后使用与日常查纸质字典相同的“翻开中间,排除一半”的方式,来实现一个查电子字典的算法。 -**深度学习。** 神经网络中大量使用了向量、矩阵、张量之间的线性代数运算,这些数据都是以数组的形式构建的。数组是神经网络编程中最常使用的数据结构。 +**深度学习。** 神经网络中大量使用了向量、矩阵、张量之间的线性代数运算,这些数据都是以数组的形式构建的。数组是神经网络编程中最常使用的数据结构。 \ No newline at end of file From eaa48b6b9f2865c78551c1d865b66147248534ae Mon Sep 17 00:00:00 2001 From: danielsss Date: Tue, 27 Dec 2022 16:52:19 +1100 Subject: [PATCH 015/158] Fixed wrong parameter of Hash Map Set --- docs/chapter_hashing/hash_map.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index 2e2e57263..d8244d80a 100644 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -646,7 +646,7 @@ $$ /* 添加操作 */ public set(key: number, val: string) { let index = this.hashFunc(key); - this.bucket[index] = new Entry(index, val); + this.bucket[index] = new Entry(key, val); } /* 删除操作 */ From 27bad89eeb517e9a50bd664d6477f59e2661db5d Mon Sep 17 00:00:00 2001 From: danielsss Date: Tue, 27 Dec 2022 16:59:25 +1100 Subject: [PATCH 016/158] Update binary search docs --- codes/typescript/chapter_searching/binary_search.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codes/typescript/chapter_searching/binary_search.ts b/codes/typescript/chapter_searching/binary_search.ts index 0a96720d9..74f011b78 100644 --- a/codes/typescript/chapter_searching/binary_search.ts +++ b/codes/typescript/chapter_searching/binary_search.ts @@ -29,9 +29,9 @@ const binarySearch1 = function (nums: number[], target: number): number { // 循环,当搜索区间为空时跳出(当 i = j 时为空) while (i < j) { const m = Math.floor(i + (j - i) / 2); // 计算中点索引 m - if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中 + if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1; - } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m) 中 + } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m] 中 j = m; } else { // 找到目标元素,返回其索引 return m; From f79089d7295505644b73d3c4f7231015aea0a014 Mon Sep 17 00:00:00 2001 From: danielsss Date: Tue, 27 Dec 2022 17:06:39 +1100 Subject: [PATCH 017/158] Fixed inconsistent brackets --- .../chapter_searching/binary_search.ts | 2 +- docs/chapter_searching/binary_search.md | 40 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/codes/typescript/chapter_searching/binary_search.ts b/codes/typescript/chapter_searching/binary_search.ts index 74f011b78..2a03994b4 100644 --- a/codes/typescript/chapter_searching/binary_search.ts +++ b/codes/typescript/chapter_searching/binary_search.ts @@ -10,7 +10,7 @@ const binarySearch = function (nums: number[], target: number): number { let i = 0, j = nums.length - 1; // 循环,当搜索区间为空时跳出(当 i > j 时为空) while (i <= j) { - let m = Math.floor(i + (j - i) / 2); // 计算中点索引 m + const m = Math.floor(i + (j - i) / 2); // 计算中点索引 m if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1; } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m-1] 中 diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index a04b95157..35d6a3210 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -167,12 +167,12 @@ $$ let i = 0, j = nums.length - 1; // 循环,当搜索区间为空时跳出(当 i > j 时为空) while (i <= j) { - let m = Math.floor(i + (j - i) / 2);// 计算中点索引 m - if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j] 中 + const m = Math.floor(i + (j - i) / 2); // 计算中点索引 m + if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1; - } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m-1] 中 + } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m-1] 中 j = m - 1; - } else { // 找到目标元素,返回其索引 + } else { // 找到目标元素,返回其索引 return m; } } @@ -224,9 +224,9 @@ $$ // 循环,当搜索区间为空时跳出(当 i = j 时为空) while (i < j) { int m = (i + j) / 2; // 计算中点索引 m - if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中 + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1; - else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中 + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m] 中 j = m; else // 找到目标元素,返回其索引 return m; @@ -246,9 +246,9 @@ $$ // 循环,当搜索区间为空时跳出(当 i = j 时为空) while (i < j) { int m = (i + j) / 2; // 计算中点索引 m - if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中 + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1; - else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中 + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m] 中 j = m; else // 找到目标元素,返回其索引 return m; @@ -268,9 +268,9 @@ $$ # 循环,当搜索区间为空时跳出(当 i = j 时为空) while i < j: m = (i + j) // 2 # 计算中点索引 m - if nums[m] < target: # 此情况说明 target 在区间 [m+1, j) 中 + if nums[m] < target: # 此情况说明 target 在区间 [m+1, j] 中 i = m + 1 - elif nums[m] > target: # 此情况说明 target 在区间 [i, m) 中 + elif nums[m] > target: # 此情况说明 target 在区间 [i, m] 中 j = m else: # 找到目标元素,返回其索引 return m @@ -287,9 +287,9 @@ $$ // 循环,当搜索区间为空时跳出(当 i = j 时为空) for i < j { m := (i + j) / 2 // 计算中点索引 m - if nums[m] < target { // 此情况说明 target 在区间 [m+1, j) 中 + if nums[m] < target { // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1 - } else if nums[m] > target { // 此情况说明 target 在区间 [i, m) 中 + } else if nums[m] > target { // 此情况说明 target 在区间 [i, m] 中 j = m } else { // 找到目标元素,返回其索引 return m @@ -310,9 +310,9 @@ $$ // 循环,当搜索区间为空时跳出(当 i = j 时为空) while (i < j) { let m = parseInt((i + j) / 2); // 计算中点索引 m ,在 JS 中需使用 parseInt 函数取整 - if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中 + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1; - else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中 + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m] 中 j = m; else // 找到目标元素,返回其索引 return m; @@ -331,12 +331,12 @@ $$ let i = 0, j = nums.length; // 循环,当搜索区间为空时跳出(当 i = j 时为空) while (i < j) { - let m = Math.floor(i + (j - i) / 2);// 计算中点索引 m - if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中 + const m = Math.floor(i + (j - i) / 2); // 计算中点索引 m + if (nums[m] < target) { // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1; - } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m) 中 + } else if (nums[m] > target) { // 此情况说明 target 在区间 [i, m] 中 j = m; - } else { // 找到目标元素,返回其索引 + } else { // 找到目标元素,返回其索引 return m; } } @@ -362,9 +362,9 @@ $$ while (i < j) { int m = (i + j) / 2; // 计算中点索引 m - if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中 + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 i = m + 1; - else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中 + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m] 中 j = m; else // 找到目标元素,返回其索引 return m; From 8e9bfabae2804568a5ae80b0ea9b35d1aabb4f4e Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Tue, 27 Dec 2022 17:10:23 +0800 Subject: [PATCH 018/158] Fix binary search tree. --- docs/chapter_tree/binary_search_tree.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index c2551e749..f33294bf5 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -761,14 +761,14 @@ comments: true - **查找元素:** 由于数组是无序的,因此需要遍历数组来确定,使用 $O(n)$ 时间; - **插入元素:** 只需将元素添加至数组尾部即可,使用 $O(1)$ 时间; -- **删除元素:** 先查找元素,使用 $O(\log n)$ 时间,再在数组中删除该元素,使用 $O(n)$ 时间; +- **删除元素:** 先查找元素,使用 $O(n)$ 时间,再在数组中删除该元素,使用 $O(n)$ 时间; - **获取最小 / 最大元素:** 需要遍历数组来确定,使用 $O(n)$ 时间; 为了得到先验信息,我们也可以预先将数组元素进行排序,得到一个「排序数组」,此时操作效率为: -- **查找元素:** 由于数组已排序,可以使用二分查找,使用 $O(\log n)$ 时间; -- **插入元素:** 为了保持数组是有序的,需插入到数组某位置,平均使用 $O(n)$ 时间; -- **删除元素:** 与无序数组中的情况相同,使用 $O(n)$ 时间; +- **查找元素:** 由于数组已排序,可以使用二分查找,平均使用 $O(\log n)$ 时间; +- **插入元素:** 先查找插入位置,使用 $O(\log n)$ 时间,再插入到指定位置,使用 $O(n)$ 时间; +- **删除元素:** 先查找元素,使用 $O(\log n)$ 时间,再在数组中删除该元素,使用 $O(n)$ 时间; - **获取最小 / 最大元素:** 数组头部和尾部元素即是最小和最大元素,使用 $O(1)$ 时间; 观察发现,无序数组和有序数组中的各项操作的时间复杂度是“偏科”的,即有的快有的慢;**而二叉搜索树的各项操作的时间复杂度都是对数阶,在数据量 $n$ 很大时有巨大优势**。 From 8b401c2acbd11b637fa0d413b64dc5b07c3b8882 Mon Sep 17 00:00:00 2001 From: a16su <33782391+a16su@users.noreply.github.com> Date: Tue, 27 Dec 2022 18:34:12 +0800 Subject: [PATCH 019/158] fix format error --- codes/python/chapter_tree/avl_tree.py | 159 +++++--------- .../python/chapter_tree/binary_search_tree.py | 76 +++---- codes/python/chapter_tree/binary_tree.py | 4 +- codes/python/chapter_tree/binary_tree_bfs.py | 21 +- codes/python/chapter_tree/binary_tree_dfs.py | 60 +++--- codes/python/include/binary_tree.py | 16 +- docs/chapter_tree/avl_tree.md | 194 ++++++------------ docs/chapter_tree/binary_search_tree.md | 35 ++-- docs/chapter_tree/binary_tree.md | 46 +++-- 9 files changed, 248 insertions(+), 363 deletions(-) diff --git a/codes/python/chapter_tree/avl_tree.py b/codes/python/chapter_tree/avl_tree.py index 4e6e35727..20cad94bc 100644 --- a/codes/python/chapter_tree/avl_tree.py +++ b/codes/python/chapter_tree/avl_tree.py @@ -1,3 +1,9 @@ +""" +File: avl_tree.py +Created Time: 2022-12-20 +Author: a16su (lpluls001@gmail.com) +""" + import sys, os.path as osp import typing @@ -5,75 +11,36 @@ sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * -class AVLTreeNode: - def __init__( - self, - val=None, - height: int = 0, - left: typing.Optional["AVLTreeNode"] = None, - right: typing.Optional["AVLTreeNode"] = None, - ): - self.val = val - self.height = height - self.left = left - self.right = right - - def __str__(self): - val = self.val - left_val = self.left.val if self.left else None - right_val = self.right.val if self.right else None - return "".format( - val, left_val, right_val - ) - - class AVLTree: - def __init__(self, root: typing.Optional[AVLTreeNode] = None): + def __init__(self, root: typing.Optional[TreeNode] = None): self.root = root - @staticmethod - def height(node: typing.Optional[AVLTreeNode]) -> int: - """ - 获取结点高度 - Args: - node:起始结点 + """ 获取结点高度 """ - Returns: 高度 or -1 - - """ + def height(self, node: typing.Optional[TreeNode]) -> int: # 空结点高度为 -1 ,叶结点高度为 0 if node is not None: return node.height return -1 - def __update_height(self, node: AVLTreeNode): - """ - 更新结点高度 - Args: - node: 要更新高度的结点 + """ 更新结点高度 """ - Returns: None - - """ + def __update_height(self, node: TreeNode): # 结点高度等于最高子树高度 + 1 node.height = max([self.height(node.left), self.height(node.right)]) + 1 - def balance_factor(self, node: AVLTreeNode) -> int: - """ - 获取结点平衡因子 - Args: - node: 要获取平衡因子的结点 + """ 获取平衡因子 """ - Returns: 平衡因子 - - """ + def balance_factor(self, node: TreeNode) -> int: # 空结点平衡因子为 0 if node is None: return 0 # 结点平衡因子 = 左子树高度 - 右子树高度 return self.height(node.left) - self.height(node.right) - def __right_rotate(self, node: AVLTreeNode) -> AVLTreeNode: + """ 右旋操作 """ + + def __right_rotate(self, node: TreeNode) -> TreeNode: child = node.left grand_child = child.right # 以 child 为原点,将 node 向右旋转 @@ -85,7 +52,9 @@ class AVLTree: # 返回旋转后子树的根节点 return child - def __left_rotate(self, node: AVLTreeNode) -> AVLTreeNode: + """ 左旋操作 """ + + def __left_rotate(self, node: TreeNode) -> TreeNode: child = node.right grand_child = child.left # 以 child 为原点,将 node 向左旋转 @@ -97,15 +66,9 @@ class AVLTree: # 返回旋转后子树的根节点 return child - def rotate(self, node: AVLTreeNode): - """ - 执行旋转操作,使该子树重新恢复平衡 - Args: - node: 要旋转的根结点 + """ 执行旋转操作,使该子树重新恢复平衡 """ - Returns: 旋转后的根结点 - - """ + def __rotate(self, node: TreeNode) -> TreeNode: # 获取结点 node 的平衡因子 balance_factor = self.balance_factor(node) # 左偏树 @@ -129,76 +92,46 @@ class AVLTree: # 平衡树,无需旋转,直接返回 return node - def insert(self, val) -> AVLTreeNode: - """ - 插入结点 - Args: - val: 结点的值 + """ 插入结点 """ - Returns: - node: 插入结点后的根结点 - """ - self.root = self.insert_helper(self.root, val) + def insert(self, val) -> TreeNode: + self.root = self.__insert_helper(self.root, val) return self.root - def insert_helper( - self, node: typing.Optional[AVLTreeNode], val: int - ) -> AVLTreeNode: - """ - 递归插入结点(辅助函数) - Args: - node: 要插入的根结点 - val: 要插入的结点的值 + """ 递归插入结点(辅助函数)""" - Returns: 插入结点后的根结点 - - """ + def __insert_helper(self, node: typing.Optional[TreeNode], val: int) -> TreeNode: if node is None: - return AVLTreeNode(val) + return TreeNode(val) # 1. 查找插入位置,并插入结点 if val < node.val: - node.left = self.insert_helper(node.left, val) + node.left = self.__insert_helper(node.left, val) elif val > node.val: - node.right = self.insert_helper(node.right, val) + node.right = self.__insert_helper(node.right, val) else: # 重复结点不插入,直接返回 return node # 更新结点高度 self.__update_height(node) # 2. 执行旋转操作,使该子树重新恢复平衡 - return self.rotate(node) + return self.__rotate(node) + + """ 删除结点 """ def remove(self, val: int): - """ - 删除结点 - Args: - val: 要删除的结点的值 - - Returns: - - """ - root = self.remove_helper(self.root, val) + root = self.__remove_helper(self.root, val) return root - def remove_helper( - self, node: typing.Optional[AVLTreeNode], val: int - ) -> typing.Optional[AVLTreeNode]: - """ - 递归删除结点(辅助函数) - Args: - node: 删除的起始结点 - val: 要删除的结点的值 + """ 递归删除结点(辅助函数) """ - Returns: 删除目标结点后的起始结点 - - """ + def __remove_helper(self, node: typing.Optional[TreeNode], val: int) -> typing.Optional[TreeNode]: if node is None: return None # 1. 查找结点,并删除之 if val < node.val: - node.left = self.remove_helper(node.left, val) + node.left = self.__remove_helper(node.left, val) elif val > node.val: - node.right = self.remove_helper(node.right, val) + node.right = self.__remove_helper(node.right, val) else: if node.left is None or node.right is None: child = node.left or node.right @@ -210,17 +143,16 @@ class AVLTree: node = child else: # 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 temp = self.min_node(node.right) - node.right = self.remove_helper(node.right, temp.val) + node.right = self.__remove_helper(node.right, temp.val) node.val = temp.val # 更新结点高度 self.__update_height(node) # 2. 执行旋转操作,使该子树重新恢复平衡 - return self.rotate(node) + return self.__rotate(node) - def min_node( - self, node: typing.Optional[AVLTreeNode] - ) -> typing.Optional[AVLTreeNode]: - # 获取最小结点 + """ 获取最小结点 """ + + def min_node(self, node: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: if node is None: return None # 循环访问左子结点,直到叶结点时为最小结点,跳出 @@ -228,15 +160,22 @@ class AVLTree: node = node.left return node + """ 查找结点 """ + def search(self, val: int): cur = self.root + # 循环查找,越过叶结点后跳出 while cur is not None: + # 目标结点在 root 的右子树中 if cur.val < val: cur = cur.right + # 目标结点在 root 的左子树中 elif cur.val > val: cur = cur.left + # 找到目标结点,跳出循环 else: break + # 返回目标结点 return cur diff --git a/codes/python/chapter_tree/binary_search_tree.py b/codes/python/chapter_tree/binary_search_tree.py index 211e7c0df..2123252c6 100644 --- a/codes/python/chapter_tree/binary_search_tree.py +++ b/codes/python/chapter_tree/binary_search_tree.py @@ -1,43 +1,46 @@ """ File: binary_search_tree.py -Created Time: 2022-11-25 -Author: Krahets (krahets@163.com) +Created Time: 2022-12-20 +Author: a16su (lpluls001@gmail.com) """ import sys, os.path as osp +import typing sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * +""" 二叉搜索树 """ + + class BinarySearchTree: - """ - 二叉搜索树 - """ - - def __init__(self, nums) -> None: + def __init__(self, nums: typing.List[int]) -> None: nums.sort() - self.__root = self.buildTree(nums, 0, len(nums) - 1) + self.__root = self.build_tree(nums, 0, len(nums) - 1) - def buildTree(self, nums, start_index, end_index): + """ 构建二叉搜索树 """ + + def build_tree(self, nums: typing.List[int], start_index: int, end_index: int) -> typing.Optional[TreeNode]: if start_index > end_index: return None + + # 将数组中间结点作为根结点 mid = (start_index + end_index) // 2 root = TreeNode(nums[mid]) - root.left = self.buildTree( - nums=nums, start_index=start_index, end_index=mid - 1 - ) - root.right = self.buildTree(nums=nums, start_index=mid + 1, end_index=end_index) + # 递归建立左子树和右子树 + root.left = self.build_tree(nums=nums, start_index=start_index, end_index=mid - 1) + root.right = self.build_tree(nums=nums, start_index=mid + 1, end_index=end_index) return root - def get_root(self): + @property + def root(self) -> typing.Optional[TreeNode]: return self.__root - def search(self, num): - """ - 查找结点 - """ - cur = self.get_root() + """ 查找结点 """ + + def search(self, num: int) -> typing.Optional[TreeNode]: + cur = self.root # 循环查找,越过叶结点后跳出 while cur is not None: # 目标结点在 root 的右子树中 @@ -51,11 +54,10 @@ class BinarySearchTree: break return cur - def insert(self, num): - """ - 插入结点 - """ - root = self.get_root() + """ 插入结点 """ + + def insert(self, num: int) -> typing.Optional[TreeNode]: + root = self.root # 若树为空,直接提前返回 if root is None: return None @@ -83,11 +85,10 @@ class BinarySearchTree: pre.left = node return node - def remove(self, num): - """ - 删除结点 - """ - root = self.get_root() + """ 删除结点 """ + + def remove(self, num: int) -> typing.Optional[TreeNode]: + root = self.root # 若树为空,直接提前返回 if root is None: return None @@ -130,10 +131,9 @@ class BinarySearchTree: cur.val = tmp return cur - def min(self, root): - """ - 获取最小结点 - """ + """ 获取最小结点 """ + + def min(self, root: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: if root is None: return root @@ -148,7 +148,7 @@ if __name__ == "__main__": nums = list(range(1, 16)) bst = BinarySearchTree(nums=nums) print("\n初始化的二叉树为\n") - print_tree(bst.get_root()) + print_tree(bst.root) # 查找结点 node = bst.search(5) @@ -157,17 +157,17 @@ if __name__ == "__main__": # 插入结点 ndoe = bst.insert(16) print("\n插入结点 16 后,二叉树为\n") - print_tree(bst.get_root()) + print_tree(bst.root) # 删除结点 bst.remove(1) print("\n删除结点 1 后,二叉树为\n") - print_tree(bst.get_root()) + print_tree(bst.root) bst.remove(2) print("\n删除结点 2 后,二叉树为\n") - print_tree(bst.get_root()) + print_tree(bst.root) bst.remove(4) print("\n删除结点 4 后,二叉树为\n") - print_tree(bst.get_root()) + print_tree(bst.root) diff --git a/codes/python/chapter_tree/binary_tree.py b/codes/python/chapter_tree/binary_tree.py index 2a892fae8..b1d8a13e3 100644 --- a/codes/python/chapter_tree/binary_tree.py +++ b/codes/python/chapter_tree/binary_tree.py @@ -1,7 +1,7 @@ """ File: binary_tree.py -Created Time: 2022-11-25 -Author: Krahets (krahets@163.com) +Created Time: 2022-12-20 +Author: a16su (lpluls001@gmail.com) """ import sys, os.path as osp diff --git a/codes/python/chapter_tree/binary_tree_bfs.py b/codes/python/chapter_tree/binary_tree_bfs.py index 57d15f919..d7865b6f7 100644 --- a/codes/python/chapter_tree/binary_tree_bfs.py +++ b/codes/python/chapter_tree/binary_tree_bfs.py @@ -1,17 +1,22 @@ """ File: binary_tree_bfs.py -Created Time: 2022-11-25 -Author: Krahets (krahets@163.com) +Created Time: 2022-12-20 +Author: a16su (lpluls001@gmail.com) """ import sys, os.path as osp +import typing + sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * -def hierOrder(root): +""" 层序遍历 """ + + +def hier_order(root: TreeNode): # 初始化队列,加入根结点 - queue = collections.deque() + queue: typing.Deque[TreeNode] = collections.deque() queue.append(root) # 初始化一个列表,用于保存遍历序列 result = [] @@ -33,13 +38,11 @@ def hierOrder(root): if __name__ == "__main__": # 初始化二叉树 # 这里借助了一个从数组直接生成二叉树的函数 - root = list_to_tree( - arr=[1, 2, 3, 4, 5, 6, 7, None, None, None, None, None, None, None, None] - ) + root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7, None, None, None, None, None, None, None, None]) print("\n初始化二叉树\n") print_tree(root) # 层序遍历 - result = hierOrder(root) + result = hier_order(root) print("\n层序遍历的结点打印序列 = ", result) - assert result == [1, 2, 3, 4, 5, 6, 7] \ No newline at end of file + assert result == [1, 2, 3, 4, 5, 6, 7] diff --git a/codes/python/chapter_tree/binary_tree_dfs.py b/codes/python/chapter_tree/binary_tree_dfs.py index 6c5b92a3f..31b160552 100644 --- a/codes/python/chapter_tree/binary_tree_dfs.py +++ b/codes/python/chapter_tree/binary_tree_dfs.py @@ -1,10 +1,12 @@ """ File: binary_tree_dfs.py -Created Time: 2022-11-25 -Author: Krahets (krahets@163.com) +Created Time: 2022-12-20 +Author: a16su (lpluls001@gmail.com) """ import sys, os.path as osp +import typing + sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * @@ -12,42 +14,42 @@ from include import * result = [] -def preOrder(root): - """ - 前序遍历二叉树 - """ +""" 前序遍历二叉树 """ + + +def pre_order(root: typing.Optional[TreeNode]): if root is None: return # 访问优先级:根结点 -> 左子树 -> 右子树 result.append(root.val) - preOrder(root=root.left) - preOrder(root=root.right) + pre_order(root=root.left) + pre_order(root=root.right) -def inOrder(root): - """ - 中序遍历二叉树 - """ +""" 中序遍历二叉树 """ + + +def in_order(root: typing.Optional[TreeNode]): if root is None: return # 访问优先级:左子树 -> 根结点 -> 右子树 - inOrder(root=root.left) + in_order(root=root.left) result.append(root.val) - inOrder(root=root.right) + in_order(root=root.right) -def postOrder(root): - """ - 后序遍历二叉树 - """ +""" 后序遍历二叉树 """ + + +def post_order(root: typing.Optional[TreeNode]): if root is None: return # 访问优先级:左子树 -> 右子树 -> 根结点 - postOrder(root=root.left) - postOrder(root=root.right) + post_order(root=root.left) + post_order(root=root.right) result.append(root.val) @@ -55,26 +57,24 @@ def postOrder(root): if __name__ == "__main__": # 初始化二叉树 # 这里借助了一个从数组直接生成二叉树的函数 - root = list_to_tree( - arr=[1, 2, 3, 4, 5, 6, 7, None, None, None, None, None, None, None, None] - ) + root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7, None, None, None, None, None, None, None, None]) print("\n初始化二叉树\n") print_tree(root) # 前序遍历 - result = [] - preOrder(root) + result.clear() + pre_order(root) print("\n前序遍历的结点打印序列 = ", result) assert result == [1, 2, 4, 5, 3, 6, 7] # 中序遍历 - result = [] - inOrder(root) + result.clear() + in_order(root) print("\n中序遍历的结点打印序列 = ", result) assert result == [4, 2, 5, 1, 6, 3, 7] # 后序遍历 - result = [] - postOrder(root) + result.clear() + post_order(root) print("\n后序遍历的结点打印序列 = ", result) - assert result == [4, 5, 2, 6, 7, 3, 1] \ No newline at end of file + assert result == [4, 5, 2, 6, 7, 3, 1] diff --git a/codes/python/include/binary_tree.py b/codes/python/include/binary_tree.py index bfe5b5f27..fe96f5046 100644 --- a/codes/python/include/binary_tree.py +++ b/codes/python/include/binary_tree.py @@ -10,9 +10,19 @@ class TreeNode: """Definition for a binary tree node """ def __init__(self, val=None, left=None, right=None): - self.val = val - self.left = left - self.right = right + self.val = val # 结点值 + self.height = 0 # 结点高度, avl 树会用到 + self.left = left # 左子结点引用 + self.right = right # 右子结点引用 + + def __str__(self): # 直接print时会好看一点 + val = self.val + left_node_val = self.left.val if self.left else None + right_node_val = self.right.val if self.right else None + return "".format(val, left_node_val, right_node_val) + + __repr__ = __str__ + def list_to_tree(arr): """Generate a binary tree with a list diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index ef88975b2..5f2cfb185 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -48,24 +48,21 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Python" ```python title="avl_tree.py" - class AVLTreeNode: - def __init__( - self, - val=None, - height: int = 0, - left: typing.Optional["AVLTreeNode"] = None, - right: typing.Optional["AVLTreeNode"] = None - ): - self.val = val - self.height = height - self.left = left - self.right = right + """ AVL 树结点类 """ + class TreeNode: + def __init__(self, val=None, left=None, right=None): + self.val = val # 结点值 + self.height = 0 # 结点高度, avl 树会用到 + self.left = left # 左子结点引用 + self.right = right # 右子结点引用 - def __str__(self): + def __str__(self): # 直接print时会好看一点 val = self.val - left_val = self.left.val if self.left else None - right_val = self.right.val if self.right else None - return "".format(val, left_val, right_val) + left_node_val = self.left.val if self.left else None + right_node_val = self.right.val if self.right else None + return "".format(val, left_node_val, right_node_val) + + __repr__ = __str__ ``` === "Go" @@ -125,31 +122,17 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Python" ```python title="avl_tree.py" - def height(node: typing.Optional[AVLTreeNode]) -> int: - """ - 获取结点高度 - Args: - node:起始结点 - - Returns: 高度 or -1 - - """ + """ 获取结点高度 """ + def height(self, node: typing.Optional[TreeNode]) -> int: # 空结点高度为 -1 ,叶结点高度为 0 if node is not None: return node.height return -1 - def update_height(node: AVLTreeNode): - """ - 更新结点高度 - Args: - node: 要更新高度的结点 - - Returns: None - - """ + """ 更新结点高度 """ + def __update_height(self, node: TreeNode): # 结点高度等于最高子树高度 + 1 - node.height = max([height(node.left), height(node.right)]) + 1 + node.height = max([self.height(node.left), self.height(node.right)]) + 1 ``` === "Go" @@ -207,20 +190,13 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Python" ```python title="avl_tree.py" - def balance_factor(node: AVLTreeNode) -> int: - """ - 获取结点平衡因子 - Args: - node: 要获取平衡因子的结点 - - Returns: 平衡因子 - - """ + """ 获取平衡因子 """ + def balance_factor(self, node: TreeNode) -> int: # 空结点平衡因子为 0 if node is None: return 0 # 结点平衡因子 = 左子树高度 - 右子树高度 - return height(node.left) - height(node.right) + return self.height(node.left) - self.height(node.right) ``` === "Go" @@ -309,15 +285,16 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - def rightRotate(node: AVLTreeNode): + """ 右旋操作 """ + def __right_rotate(self, node: TreeNode) -> TreeNode: child = node.left grand_child = child.right # 以 child 为原点,将 node 向右旋转 child.right = node node.left = grand_child # 更新结点高度 - update_height(node) - update_height(child) + self.__update_height(node) + self.__update_height(child) # 返回旋转后子树的根节点 return child ``` @@ -387,15 +364,16 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - def leftRotate(node: AVLTreeNode): + """ 左旋操作 """ + def __left_rotate(self, node: TreeNode) -> TreeNode: child = node.right grand_child = child.left # 以 child 为原点,将 node 向左旋转 child.left = node node.right = grand_child # 更新结点高度 - update_height(node) - update_height(child) + self.__update_height(node) + self.__update_height(child) # 返回旋转后子树的根节点 return child ``` @@ -506,35 +484,28 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - def rotate(node: AVLTreeNode): - """ - 执行旋转操作,使该子树重新恢复平衡 - Args: - node: 要旋转的根结点 - - Returns: 旋转后的根结点 - - """ + """ 执行旋转操作,使该子树重新恢复平衡 """ + def __rotate(self, node: TreeNode) -> TreeNode: # 获取结点 node 的平衡因子 - factor = balance_factor(node) + balance_factor = self.balance_factor(node) # 左偏树 - if factor > 1: - if balance_factor(node.left) >= 0: + if balance_factor > 1: + if self.balance_factor(node.left) >= 0: # 右旋 - return right_rotate(node) + return self.__right_rotate(node) else: # 先左旋后右旋 - node.left = left_rotate(node.left) - return right_rotate(node) + node.left = self.__left_rotate(node.left) + return self.__right_rotate(node) # 右偏树 - elif factor < -1: - if balance_factor(node.right) <= 0: + elif balance_factor < -1: + if self.balance_factor(node.right) <= 0: # 左旋 - return left_rotate(node) + return self.__left_rotate(node) else: # 先右旋后左旋 - node.right = right_rotate(node.right) - return left_rotate(node) + node.right = self.__right_rotate(node.right) + return self.__left_rotate(node) # 平衡树,无需旋转,直接返回 return node ``` @@ -611,42 +582,27 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - def insert(val) -> AVLTreeNode: - """ - 插入结点 - Args: - val: 结点的值 + """ 插入结点 """ + def insert(self, val) -> TreeNode: + self.root = self.__insert_helper(self.root, val) + return self.root - Returns: - node: 插入结点后的根结点 - """ - root = insert_helper(root, val) - return root - - def insert_helper(node: typing.Optional[AVLTreeNode], val: int) -> AVLTreeNode: - """ - 递归插入结点(辅助函数) - Args: - node: 要插入的根结点 - val: 要插入的结点的值 - - Returns: 插入结点后的根结点 - - """ + """ 递归插入结点(辅助函数)""" + def __insert_helper(self, node: typing.Optional[TreeNode], val: int) -> TreeNode: if node is None: - return AVLTreeNode(val) + return TreeNode(val) # 1. 查找插入位置,并插入结点 if val < node.val: - node.left = insert_helper(node.left, val) + node.left = self.__insert_helper(node.left, val) elif val > node.val: - node.right = insert_helper(node.right, val) + node.right = self.__insert_helper(node.right, val) else: # 重复结点不插入,直接返回 return node # 更新结点高度 - update_height(node) + self.__update_height(node) # 2. 执行旋转操作,使该子树重新恢复平衡 - return rotate(node) + return self.__rotate(node) ``` === "Go" @@ -743,35 +699,20 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - def remove(val: int): - """ - 删除结点 - Args: - val: 要删除的结点的值 - - Returns: - - """ - root = remove_helper(root, val) + """ 删除结点 """ + def remove(self, val: int): + root = self.__remove_helper(self.root, val) return root - def remove_helper(node: typing.Optional[AVLTreeNode], val: int) -> typing.Optional[AVLTreeNode]: - """ - 递归删除结点(辅助函数) - Args: - node: 删除的起始结点 - val: 要删除的结点的值 - - Returns: 删除目标结点后的起始结点 - - """ + """ 递归删除结点(辅助函数) """ + def __remove_helper(self, node: typing.Optional[TreeNode], val: int) -> typing.Optional[TreeNode]: if node is None: return None # 1. 查找结点,并删除之 if val < node.val: - node.left = remove_helper(node.left, val) + node.left = self.__remove_helper(node.left, val) elif val > node.val: - node.right = remove_helper(node.right, val) + node.right = self.__remove_helper(node.right, val) else: if node.left is None or node.right is None: child = node.left or node.right @@ -781,18 +722,17 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 # 子结点数量 = 1 ,直接删除 node else: node = child - else: # 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 - temp = min_node(node.right) - node.right = remove_helper(node.right, temp.val) + else: # 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 + temp = self.min_node(node.right) + node.right = self.__remove_helper(node.right, temp.val) node.val = temp.val # 更新结点高度 - update_height(node) + self.__update_height(node) # 2. 执行旋转操作,使该子树重新恢复平衡 - return rotate(node) + return self.__rotate(node) - - def min_node(node: typing.Optional[AVLTreeNode]) -> typing.Optional[AVLTreeNode]: - # 获取最小结点 + """ 获取最小结点 """ + def min_node(self, node: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: if node is None: return None # 循环访问左子结点,直到叶结点时为最小结点,跳出 diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index a9328ddc6..08d16e4e4 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -82,11 +82,9 @@ comments: true === "Python" ```python title="binary_search_tree.py" - def search(self, num): - """ - 查找结点 - """ - cur = self.get_root() + """ 查找结点 """ + def search(self, num: int) -> typing.Optional[TreeNode]: + cur = self.root # 循环查找,越过叶结点后跳出 while cur is not None: # 目标结点在 root 的右子树中 @@ -99,7 +97,6 @@ comments: true else: break return cur - ``` === "Go" @@ -245,11 +242,9 @@ comments: true === "Python" ```python title="binary_search_tree.py" - def insert(self, num): - """ - 插入结点 - """ - root = self.get_root() + """ 插入结点 """ + def insert(self, num: int) -> typing.Optional[TreeNode]: + root = self.root # 若树为空,直接提前返回 if root is None: return None @@ -530,15 +525,13 @@ comments: true === "Python" ```python title="binary_search_tree.py" - def remove(self, num): - """ - 删除结点 - """ - root = self.get_root() + """ 删除结点 """ + def remove(self, num: int) -> typing.Optional[TreeNode]: + root = self.root # 若树为空,直接提前返回 if root is None: return None - + cur = root pre = None @@ -574,13 +567,11 @@ comments: true # 递归删除结点 nex self.remove(nex.val) # 将 nex 的值复制给 cur - cur.val = tmp + cur.val = tmp return cur - def min(self, root): - """ - 获取最小结点 - """ + """ 获取最小结点 """ + def min(self, root: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: if root is None: return root diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index ab67e6f80..d1e7774ad 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -33,12 +33,19 @@ comments: true === "Python" ```python title="" + """ 链表结点类 """ class TreeNode: - """ 链表结点类 """ def __init__(self, val=None, left=None, right=None): self.val = val # 结点值 self.left = left # 左子结点指针 self.right = right # 右子结点指针 + + def __str__(self): + val = self.val + left_node_val = self.left.val if self.left else None + right_node_val = self.right.val if self.right else None + return "".format(val, left_node_val, right_node_val) + ``` === "Go" @@ -423,7 +430,8 @@ comments: true === "Python" ```python title="binary_tree_bfs.py" - def hierOrder(root): + """ 层序遍历 """ + def hier_order(root): # 初始化队列,加入根结点 queue = collections.deque() queue.append(root) @@ -612,42 +620,36 @@ comments: true === "Python" ```python title="binary_tree_dfs.py" - def preOrder(root): - """ - 前序遍历二叉树 - """ + """ 前序遍历二叉树 """ + def pre_order(root: typing.Optional[TreeNode]): if root is None: return - + # 访问优先级:根结点 -> 左子树 -> 右子树 result.append(root.val) - preOrder(root=root.left) - preOrder(root=root.right) + pre_order(root=root.left) + pre_order(root=root.right) - def inOrder(root): - """ - 中序遍历二叉树 - """ + """ 中序遍历二叉树 """ + def in_order(root: typing.Optional[TreeNode]): if root is None: return # 访问优先级:左子树 -> 根结点 -> 右子树 - inOrder(root=root.left) + in_order(root=root.left) result.append(root.val) - inOrder(root=root.right) + in_order(root=root.right) - def postOrder(root): - """ - 后序遍历二叉树 - """ + """ 后序遍历二叉树 """ + def post_order(root: typing.Optional[TreeNode]): if root is None: return - + # 访问优先级:左子树 -> 右子树 -> 根结点 - postOrder(root=root.left) - postOrder(root=root.right) + post_order(root=root.left) + post_order(root=root.right) result.append(root.val) ``` From dbb25003ec9fec74e015a9019be0588df67523db Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Tue, 27 Dec 2022 19:24:43 +0800 Subject: [PATCH 020/158] Fine tune --- codes/python/chapter_tree/avl_tree.py | 18 +++--------------- .../python/chapter_tree/binary_search_tree.py | 8 +------- codes/python/chapter_tree/binary_tree.py | 3 +++ codes/python/chapter_tree/binary_tree_bfs.py | 2 -- codes/python/chapter_tree/binary_tree_dfs.py | 6 ------ 5 files changed, 7 insertions(+), 30 deletions(-) diff --git a/codes/python/chapter_tree/avl_tree.py b/codes/python/chapter_tree/avl_tree.py index 20cad94bc..d0ff48cba 100644 --- a/codes/python/chapter_tree/avl_tree.py +++ b/codes/python/chapter_tree/avl_tree.py @@ -16,7 +16,6 @@ class AVLTree: self.root = root """ 获取结点高度 """ - def height(self, node: typing.Optional[TreeNode]) -> int: # 空结点高度为 -1 ,叶结点高度为 0 if node is not None: @@ -24,13 +23,11 @@ class AVLTree: return -1 """ 更新结点高度 """ - def __update_height(self, node: TreeNode): # 结点高度等于最高子树高度 + 1 node.height = max([self.height(node.left), self.height(node.right)]) + 1 """ 获取平衡因子 """ - def balance_factor(self, node: TreeNode) -> int: # 空结点平衡因子为 0 if node is None: @@ -39,7 +36,6 @@ class AVLTree: return self.height(node.left) - self.height(node.right) """ 右旋操作 """ - def __right_rotate(self, node: TreeNode) -> TreeNode: child = node.left grand_child = child.right @@ -53,7 +49,6 @@ class AVLTree: return child """ 左旋操作 """ - def __left_rotate(self, node: TreeNode) -> TreeNode: child = node.right grand_child = child.left @@ -67,7 +62,6 @@ class AVLTree: return child """ 执行旋转操作,使该子树重新恢复平衡 """ - def __rotate(self, node: TreeNode) -> TreeNode: # 获取结点 node 的平衡因子 balance_factor = self.balance_factor(node) @@ -93,13 +87,11 @@ class AVLTree: return node """ 插入结点 """ - def insert(self, val) -> TreeNode: self.root = self.__insert_helper(self.root, val) return self.root """ 递归插入结点(辅助函数)""" - def __insert_helper(self, node: typing.Optional[TreeNode], val: int) -> TreeNode: if node is None: return TreeNode(val) @@ -117,13 +109,11 @@ class AVLTree: return self.__rotate(node) """ 删除结点 """ - def remove(self, val: int): root = self.__remove_helper(self.root, val) return root """ 递归删除结点(辅助函数) """ - def __remove_helper(self, node: typing.Optional[TreeNode], val: int) -> typing.Optional[TreeNode]: if node is None: return None @@ -142,7 +132,7 @@ class AVLTree: else: node = child else: # 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 - temp = self.min_node(node.right) + temp = self.__min_node(node.right) node.right = self.__remove_helper(node.right, temp.val) node.val = temp.val # 更新结点高度 @@ -151,8 +141,7 @@ class AVLTree: return self.__rotate(node) """ 获取最小结点 """ - - def min_node(self, node: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: + def __min_node(self, node: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: if node is None: return None # 循环访问左子结点,直到叶结点时为最小结点,跳出 @@ -161,7 +150,6 @@ class AVLTree: return node """ 查找结点 """ - def search(self, val: int): cur = self.root # 循环查找,越过叶结点后跳出 @@ -179,8 +167,8 @@ class AVLTree: return cur +""" Driver Code """ if __name__ == "__main__": - def test_insert(tree: AVLTree, val: int): tree.insert(val) print("\n插入结点 {} 后,AVL 树为".format(val)) diff --git a/codes/python/chapter_tree/binary_search_tree.py b/codes/python/chapter_tree/binary_search_tree.py index 2123252c6..23e341854 100644 --- a/codes/python/chapter_tree/binary_search_tree.py +++ b/codes/python/chapter_tree/binary_search_tree.py @@ -12,15 +12,12 @@ from include import * """ 二叉搜索树 """ - - class BinarySearchTree: def __init__(self, nums: typing.List[int]) -> None: nums.sort() self.__root = self.build_tree(nums, 0, len(nums) - 1) """ 构建二叉搜索树 """ - def build_tree(self, nums: typing.List[int], start_index: int, end_index: int) -> typing.Optional[TreeNode]: if start_index > end_index: return None @@ -38,7 +35,6 @@ class BinarySearchTree: return self.__root """ 查找结点 """ - def search(self, num: int) -> typing.Optional[TreeNode]: cur = self.root # 循环查找,越过叶结点后跳出 @@ -55,7 +51,6 @@ class BinarySearchTree: return cur """ 插入结点 """ - def insert(self, num: int) -> typing.Optional[TreeNode]: root = self.root # 若树为空,直接提前返回 @@ -86,7 +81,6 @@ class BinarySearchTree: return node """ 删除结点 """ - def remove(self, num: int) -> typing.Optional[TreeNode]: root = self.root # 若树为空,直接提前返回 @@ -132,7 +126,6 @@ class BinarySearchTree: return cur """ 获取最小结点 """ - def min(self, root: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: if root is None: return root @@ -143,6 +136,7 @@ class BinarySearchTree: return root +""" Driver Code """ if __name__ == "__main__": # 初始化二叉搜索树 nums = list(range(1, 16)) diff --git a/codes/python/chapter_tree/binary_tree.py b/codes/python/chapter_tree/binary_tree.py index b1d8a13e3..2b25e4724 100644 --- a/codes/python/chapter_tree/binary_tree.py +++ b/codes/python/chapter_tree/binary_tree.py @@ -23,6 +23,7 @@ if __name__ == "__main__": n1.right = n3 n2.left = n4 n2.right = n5 + print("\n初始化二叉树\n") print_tree(n1) # 插入与删除结点 @@ -31,8 +32,10 @@ if __name__ == "__main__": # 在 n1 -> n2 中间插入节点 P n1.left = P P.left = n2 + print("\n插入结点 P 后\n") print_tree(n1) # 删除结点 n1.left = n2 + print("\n删除结点 P 后\n"); print_tree(n1) diff --git a/codes/python/chapter_tree/binary_tree_bfs.py b/codes/python/chapter_tree/binary_tree_bfs.py index d7865b6f7..c6a73013b 100644 --- a/codes/python/chapter_tree/binary_tree_bfs.py +++ b/codes/python/chapter_tree/binary_tree_bfs.py @@ -12,8 +12,6 @@ from include import * """ 层序遍历 """ - - def hier_order(root: TreeNode): # 初始化队列,加入根结点 queue: typing.Deque[TreeNode] = collections.deque() diff --git a/codes/python/chapter_tree/binary_tree_dfs.py b/codes/python/chapter_tree/binary_tree_dfs.py index 31b160552..b5dc9c9f3 100644 --- a/codes/python/chapter_tree/binary_tree_dfs.py +++ b/codes/python/chapter_tree/binary_tree_dfs.py @@ -15,8 +15,6 @@ result = [] """ 前序遍历二叉树 """ - - def pre_order(root: typing.Optional[TreeNode]): if root is None: return @@ -28,8 +26,6 @@ def pre_order(root: typing.Optional[TreeNode]): """ 中序遍历二叉树 """ - - def in_order(root: typing.Optional[TreeNode]): if root is None: return @@ -41,8 +37,6 @@ def in_order(root: typing.Optional[TreeNode]): """ 后序遍历二叉树 """ - - def post_order(root: typing.Optional[TreeNode]): if root is None: return From 449258f0b06fcb666f73bf4d6488f0de9be46942 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Tue, 27 Dec 2022 19:33:58 +0800 Subject: [PATCH 021/158] Add the code to the docs. --- codes/cpp/chapter_tree/binary_tree_bfs.cpp | 8 +-- codes/python/chapter_tree/binary_tree.py | 7 ++- codes/python/chapter_tree/binary_tree_bfs.py | 24 ++++----- codes/python/chapter_tree/binary_tree_dfs.py | 38 ++++++-------- docs/chapter_tree/binary_tree.md | 6 +-- docs/chapter_tree/binary_tree_traversal.md | 52 +++++++++++++++++--- 6 files changed, 81 insertions(+), 54 deletions(-) diff --git a/codes/cpp/chapter_tree/binary_tree_bfs.cpp b/codes/cpp/chapter_tree/binary_tree_bfs.cpp index eeec2a18e..30c2d6007 100644 --- a/codes/cpp/chapter_tree/binary_tree_bfs.cpp +++ b/codes/cpp/chapter_tree/binary_tree_bfs.cpp @@ -15,12 +15,12 @@ vector hierOrder(TreeNode* root) { vector vec; while (!queue.empty()) { TreeNode* node = queue.front(); - queue.pop(); // 队列出队 - vec.push_back(node->val); // 保存结点 + queue.pop(); // 队列出队 + vec.push_back(node->val); // 保存结点 if (node->left != nullptr) - queue.push(node->left); // 左子结点入队 + queue.push(node->left); // 左子结点入队 if (node->right != nullptr) - queue.push(node->right); // 右子结点入队 + queue.push(node->right); // 右子结点入队 } return vec; } diff --git a/codes/python/chapter_tree/binary_tree.py b/codes/python/chapter_tree/binary_tree.py index 2b25e4724..d99026352 100644 --- a/codes/python/chapter_tree/binary_tree.py +++ b/codes/python/chapter_tree/binary_tree.py @@ -12,13 +12,14 @@ from include import * """ Driver Code """ if __name__ == "__main__": - # 初始化二叉树 + """ 初始化二叉树 """ # 初始化节点 n1 = TreeNode(val=1) n2 = TreeNode(val=2) n3 = TreeNode(val=3) n4 = TreeNode(val=4) n5 = TreeNode(val=5) + # 构建引用指向(即指针) n1.left = n2 n1.right = n3 n2.left = n4 @@ -26,15 +27,13 @@ if __name__ == "__main__": print("\n初始化二叉树\n") print_tree(n1) - # 插入与删除结点 + """ 插入与删除结点 """ P = TreeNode(0) - # 在 n1 -> n2 中间插入节点 P n1.left = P P.left = n2 print("\n插入结点 P 后\n") print_tree(n1) - # 删除结点 n1.left = n2 print("\n删除结点 P 后\n"); diff --git a/codes/python/chapter_tree/binary_tree_bfs.py b/codes/python/chapter_tree/binary_tree_bfs.py index c6a73013b..0320a08dd 100644 --- a/codes/python/chapter_tree/binary_tree_bfs.py +++ b/codes/python/chapter_tree/binary_tree_bfs.py @@ -14,22 +14,18 @@ from include import * """ 层序遍历 """ def hier_order(root: TreeNode): # 初始化队列,加入根结点 - queue: typing.Deque[TreeNode] = collections.deque() + queue = collections.deque() queue.append(root) # 初始化一个列表,用于保存遍历序列 - result = [] + res = [] while queue: - # 队列出队 - node = queue.popleft() - # 保存节点值 - result.append(node.val) + node = queue.popleft() # 队列出队 + res.append(node.val) # 保存节点值 if node.left is not None: - # 左子结点入队 - queue.append(node.left) + queue.append(node.left) # 左子结点入队 if node.right is not None: - # 右子结点入队 - queue.append(node.right) - return result + queue.append(node.right) # 右子结点入队 + return res """ Driver Code """ @@ -41,6 +37,6 @@ if __name__ == "__main__": print_tree(root) # 层序遍历 - result = hier_order(root) - print("\n层序遍历的结点打印序列 = ", result) - assert result == [1, 2, 3, 4, 5, 6, 7] + res = hier_order(root) + print("\n层序遍历的结点打印序列 = ", res) + assert res == [1, 2, 3, 4, 5, 6, 7] diff --git a/codes/python/chapter_tree/binary_tree_dfs.py b/codes/python/chapter_tree/binary_tree_dfs.py index b5dc9c9f3..11ee8339c 100644 --- a/codes/python/chapter_tree/binary_tree_dfs.py +++ b/codes/python/chapter_tree/binary_tree_dfs.py @@ -11,40 +11,34 @@ sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * -result = [] +res = [] - -""" 前序遍历二叉树 """ +""" 前序遍历 """ def pre_order(root: typing.Optional[TreeNode]): if root is None: return - # 访问优先级:根结点 -> 左子树 -> 右子树 - result.append(root.val) + res.append(root.val) pre_order(root=root.left) pre_order(root=root.right) - -""" 中序遍历二叉树 """ +""" 中序遍历 """ def in_order(root: typing.Optional[TreeNode]): if root is None: return - # 访问优先级:左子树 -> 根结点 -> 右子树 in_order(root=root.left) - result.append(root.val) + res.append(root.val) in_order(root=root.right) - -""" 后序遍历二叉树 """ +""" 后序遍历 """ def post_order(root: typing.Optional[TreeNode]): if root is None: return - # 访问优先级:左子树 -> 右子树 -> 根结点 post_order(root=root.left) post_order(root=root.right) - result.append(root.val) + res.append(root.val) """ Driver Code """ @@ -56,19 +50,19 @@ if __name__ == "__main__": print_tree(root) # 前序遍历 - result.clear() + res.clear() pre_order(root) - print("\n前序遍历的结点打印序列 = ", result) - assert result == [1, 2, 4, 5, 3, 6, 7] + print("\n前序遍历的结点打印序列 = ", res) + assert res == [1, 2, 4, 5, 3, 6, 7] # 中序遍历 - result.clear() + res.clear() in_order(root) - print("\n中序遍历的结点打印序列 = ", result) - assert result == [4, 2, 5, 1, 6, 3, 7] + print("\n中序遍历的结点打印序列 = ", res) + assert res == [4, 2, 5, 1, 6, 3, 7] # 后序遍历 - result.clear() + res.clear() post_order(root) - print("\n后序遍历的结点打印序列 = ", result) - assert result == [4, 5, 2, 6, 7, 3, 1] + print("\n后序遍历的结点打印序列 = ", res) + assert res == [4, 5, 2, 6, 7, 3, 1] diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 92539cc80..262b170be 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -182,7 +182,7 @@ comments: true === "Python" ```python title="binary_tree.py" - # 初始化二叉树 + """ 初始化二叉树 """ # 初始化节点 n1 = TreeNode(val=1) n2 = TreeNode(val=2) @@ -302,7 +302,7 @@ comments: true === "Python" ```python title="binary_tree.py" - # 插入与删除结点 + """ 插入与删除结点 """ p = TreeNode(0) # 在 n1 -> n2 中间插入结点 P n1.left = p @@ -461,7 +461,7 @@ comments: true === "Python" ```python title="" - “”“ 二叉树的数组表示 ”“” + """ 二叉树的数组表示 """ # 直接使用 None 来表示空位 tree = [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15] ``` diff --git a/docs/chapter_tree/binary_tree_traversal.md b/docs/chapter_tree/binary_tree_traversal.md index 971d24cc5..f46348412 100644 --- a/docs/chapter_tree/binary_tree_traversal.md +++ b/docs/chapter_tree/binary_tree_traversal.md @@ -51,12 +51,12 @@ comments: true vector vec; while (!queue.empty()) { TreeNode* node = queue.front(); - queue.pop(); // 队列出队 - vec.push_back(node->val); // 保存结点 + queue.pop(); // 队列出队 + vec.push_back(node->val); // 保存结点 if (node->left != nullptr) - queue.push(node->left); // 左子结点入队 + queue.push(node->left); // 左子结点入队 if (node->right != nullptr) - queue.push(node->right); // 右子结点入队 + queue.push(node->right); // 右子结点入队 } return vec; } @@ -65,7 +65,21 @@ comments: true === "Python" ```python title="binary_tree_bfs.py" - + """ 层序遍历 """ + def hier_order(root: TreeNode): + # 初始化队列,加入根结点 + queue = collections.deque() + queue.append(root) + # 初始化一个列表,用于保存遍历序列 + res = [] + while queue: + node = queue.popleft() # 队列出队 + res.append(node.val) # 保存节点值 + if node.left is not None: + queue.append(node.left) # 左子结点入队 + if node.right is not None: + queue.append(node.right) # 右子结点入队 + return res ``` === "Go" @@ -256,7 +270,32 @@ comments: true === "Python" ```python title="binary_tree_dfs.py" - + """ 前序遍历 """ + def pre_order(root: typing.Optional[TreeNode]): + if root is None: + return + # 访问优先级:根结点 -> 左子树 -> 右子树 + res.append(root.val) + pre_order(root=root.left) + pre_order(root=root.right) + + """ 中序遍历 """ + def in_order(root: typing.Optional[TreeNode]): + if root is None: + return + # 访问优先级:左子树 -> 根结点 -> 右子树 + in_order(root=root.left) + res.append(root.val) + in_order(root=root.right) + + """ 后序遍历 """ + def post_order(root: typing.Optional[TreeNode]): + if root is None: + return + # 访问优先级:左子树 -> 右子树 -> 根结点 + post_order(root=root.left) + post_order(root=root.right) + res.append(root.val) ``` === "Go" @@ -402,7 +441,6 @@ comments: true postOrder(root.right); list.Add(root.val); } - ``` !!! note From f9cc3a50cff45451eea9b973f4ffe06f232ce4a1 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Tue, 27 Dec 2022 19:38:14 +0800 Subject: [PATCH 022/158] Fine tune --- codes/python/include/binary_tree.py | 10 +++++----- docs/chapter_tree/binary_tree.md | 7 ------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/codes/python/include/binary_tree.py b/codes/python/include/binary_tree.py index 1e2eb0346..670fd18ce 100644 --- a/codes/python/include/binary_tree.py +++ b/codes/python/include/binary_tree.py @@ -9,13 +9,13 @@ import collections class TreeNode: """Definition for a binary tree node """ - def __init__(self, val=None, left=None, right=None): - self.val = val # 结点值 - self.height = 0 # 结点高度, avl 树会用到 - self.left = left # 左子结点引用 + def __init__(self, val=0, left=None, right=None): + self.val = val # 结点值 + self.height = 0 # 结点高度 + self.left = left # 左子结点引用 self.right = right # 右子结点引用 - def __str__(self): # 直接print时会好看一点 + def __str__(self): val = self.val left_node_val = self.left.val if self.left else None right_node_val = self.right.val if self.right else None diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 262b170be..4de1e4c1b 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -39,13 +39,6 @@ comments: true self.val = val # 结点值 self.left = left # 左子结点指针 self.right = right # 右子结点指针 - - def __str__(self): - val = self.val - left_node_val = self.left.val if self.left else None - right_node_val = self.right.val if self.right else None - return "".format(val, left_node_val, right_node_val) - ``` === "Go" From 466fdd494bab4ec95d2fd172e598d560d78adfed Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Tue, 27 Dec 2022 19:42:39 +0800 Subject: [PATCH 023/158] Update the chapter tree. --- docs/chapter_tree/avl_tree.md | 14 +++----------- docs/chapter_tree/binary_tree.md | 4 ++-- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index bc4493520..e9a656bed 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -51,18 +51,10 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit """ AVL 树结点类 """ class TreeNode: def __init__(self, val=None, left=None, right=None): - self.val = val # 结点值 - self.height = 0 # 结点高度, avl 树会用到 - self.left = left # 左子结点引用 + self.val = val # 结点值 + self.height = 0 # 结点高度 + self.left = left # 左子结点引用 self.right = right # 右子结点引用 - - def __str__(self): # 直接print时会好看一点 - val = self.val - left_node_val = self.left.val if self.left else None - right_node_val = self.right.val if self.right else None - return "".format(val, left_node_val, right_node_val) - - __repr__ = __str__ ``` === "Go" diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 4de1e4c1b..07e311551 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -44,13 +44,13 @@ comments: true === "Go" ```go title="" - """ 链表结点类 """ + // 链表结点类 type TreeNode struct { Val int Left *TreeNode Right *TreeNode } - """ 结点初始化方法 """ + // 结点初始化方法 func NewTreeNode(v int) *TreeNode { return &TreeNode{ Left: nil, From 34ad07bfed3b09accc30b5328a82e74865b5b7d5 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Tue, 27 Dec 2022 20:11:22 +0800 Subject: [PATCH 024/158] Fine tune --- codes/c/chapter_sorting/bubble_sort.c | 2 ++ codes/cpp/chapter_sorting/bubble_sort.cpp | 10 ++++------ docs/chapter_sorting/bubble_sort.md | 6 ++++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/codes/c/chapter_sorting/bubble_sort.c b/codes/c/chapter_sorting/bubble_sort.c index f59b81d00..6b67bd727 100644 --- a/codes/c/chapter_sorting/bubble_sort.c +++ b/codes/c/chapter_sorting/bubble_sort.c @@ -48,6 +48,8 @@ void bubble_sort_with_flag(int nums[], int size) } } + +/* Driver Code */ int main() { int nums[6] = {4, 1, 3, 1, 5, 2}; diff --git a/codes/cpp/chapter_sorting/bubble_sort.cpp b/codes/cpp/chapter_sorting/bubble_sort.cpp index 071827455..87ca305d1 100644 --- a/codes/cpp/chapter_sorting/bubble_sort.cpp +++ b/codes/cpp/chapter_sorting/bubble_sort.cpp @@ -14,9 +14,8 @@ void bubbleSort(vector& nums) { for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; + // 这里使用了 std::swap() 函数 + swap(nums[j], nums[j + 1]); } } } @@ -31,9 +30,8 @@ void bubbleSortWithFlag(vector& nums) { for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; + // 这里使用了 std::swap() 函数 + swap(nums[j], nums[j + 1]); flag = true; // 记录交换元素 } } diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index f71dce77d..336f3c2d5 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -85,7 +85,8 @@ comments: true for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] - std::swap(nums[j], nums[j+1]); + // 这里使用了 std::swap() 函数 + swap(nums[j], nums[j + 1]); } } } @@ -265,7 +266,8 @@ comments: true for (int j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { // 交换 nums[j] 与 nums[j + 1] - std::swap(nums[j], nums[j+1]); + // 这里使用了 std::swap() 函数 + swap(nums[j], nums[j + 1]); flag = true; // 记录交换元素 } } From f0c3bf57663c7cab0e6d4d6b847a67fc1a01d75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=9A=E5=9B=BD=E7=8E=AE?= Date: Wed, 28 Dec 2022 10:46:12 +0800 Subject: [PATCH 025/158] docs(array): reduce understanding cost MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 去除了并行测试; - 基于 Java 代码样例,统一了命名风格; - 基于 Go link 模块代码样例,统一了测试用例风格; - 我们将 Go 中的 Slice 切片看作 Array 数组。因为这样可以降低理解成本,利于我们将关注点放在数据结构与算法上。 --- .../go/chapter_array_and_linkedlist/array.go | 31 ++-- .../array_test.go | 162 ++++-------------- docs/chapter_array_and_linkedlist/array.md | 35 ++-- 3 files changed, 58 insertions(+), 170 deletions(-) diff --git a/codes/go/chapter_array_and_linkedlist/array.go b/codes/go/chapter_array_and_linkedlist/array.go index 26eef2a91..ccc5db19b 100644 --- a/codes/go/chapter_array_and_linkedlist/array.go +++ b/codes/go/chapter_array_and_linkedlist/array.go @@ -5,23 +5,24 @@ import ( "math/rand" ) -// ExpectSize 预期大小。 在 Go 中,声明数组长度必须是常量表达式, -// 所以这里我们约定,扩展后的长度为 6 -const ExpectSize = 6 +/** +我们将 Go 中的 Slice 切片看作 Array 数组,降低理解成本, +有利于我们将关注点放在数据结构与算法上。 +*/ /* 随机返回一个数组元素 */ -func randomAccess(nums [5]int) (ans int) { +func randomAccess(nums []int) (randomNum int) { // 在区间 [0, nums.length) 中随机抽取一个数字 randomIndex := rand.Intn(len(nums)) // 获取并返回随机元素 - ans = nums[randomIndex] + randomNum = nums[randomIndex] return } /* 扩展数组长度 */ -func extend(nums [5]int) [ExpectSize]int { +func extend(nums []int, enlarge int) []int { // 初始化一个扩展长度后的数组 - var res [ExpectSize]int + res := make([]int, len(nums)+enlarge) // 将原数组中的所有元素复制到新数组 for i := 0; i < len(nums); i++ { res[i] = nums[i] @@ -31,7 +32,7 @@ func extend(nums [5]int) [ExpectSize]int { } /* 在数组的索引 index 处插入元素 num */ -func insert(nums *[5]int, num int, index int) { +func insert(nums []int, num int, index int) { // 把索引 index 以及之后的所有元素向后移动一位 // 如果超出了数组长度,会被直接舍弃 for i := len(nums) - 1; i > index; i-- { @@ -42,11 +43,7 @@ func insert(nums *[5]int, num int, index int) { } /* 删除索引 index 处元素 */ -func remove(nums *[5]int, index int) { - // 越界检查 - if index >= len(nums) { - return - } +func remove(nums []int, index int) { // 把索引 index 之后的所有元素向前移动一位 for i := index; i < len(nums); i++ { if i+1 >= len(nums) { @@ -58,7 +55,7 @@ func remove(nums *[5]int, index int) { } /* 遍历数组 */ -func traverse(nums [5]int) { +func traverse(nums []int) { var count int // 通过索引遍历数组 for i := 0; i < len(nums); i++ { @@ -71,11 +68,11 @@ func traverse(nums [5]int) { } /* 在数组中查找指定元素 */ -func find(nums [5]int, target int) (ans int) { - ans = -1 +func find(nums []int, target int) (index int) { + index = -1 for i := 0; i < len(nums); i++ { if nums[i] == target { - ans = i + index = i break } } diff --git a/codes/go/chapter_array_and_linkedlist/array_test.go b/codes/go/chapter_array_and_linkedlist/array_test.go index 1f5510345..3f9882a9d 100644 --- a/codes/go/chapter_array_and_linkedlist/array_test.go +++ b/codes/go/chapter_array_and_linkedlist/array_test.go @@ -1,145 +1,43 @@ package chapter_array_and_linkedlist +/** +我们将 Go 中的 Slice 切片看作 Array 数组。因为这样可以 +降低理解成本,利于我们将关注点放在数据结构与算法上。 +*/ + import ( - "reflect" + "fmt" "testing" ) -func TestInitArray(t *testing.T) { - t.Parallel() - var arr [5]int - for _, v := range arr { - if v != 0 { - t.Fatalf("array init exception") - } - } -} +/* Driver Code */ +func TestArray(t *testing.T) { + /* 初始化数组 */ + var arr []int + fmt.Println("数组 arr = ", arr) + nums := []int{1, 3, 2, 5, 4} + fmt.Println("数组 nums = ", nums) -func TestRandomAccess(t *testing.T) { - t.Parallel() - min, max := 1, 5 - nums := [5]int{min, 2, 3, 4, max} - ans := randomAccess(nums) - if ans < min || ans > max { - t.Fatalf("Expected range is greater than min: %v and less than max: %v, got ans: %v", min, max, ans) - } -} + /* 随机访问 */ + randomNum := randomAccess(nums) + fmt.Println("在 nums 中获取随机元素 ", randomNum) -func TestExtend(t *testing.T) { - t.Parallel() - nums := [5]int{1, 2, 3, 4, 5} - newNums := extend(nums) - if len(newNums) != ExpectSize { - t.Fatalf("Expected len: %v, got: %v", ExpectSize, len(nums)) - } -} + /* 长度扩展 */ + nums = extend(nums, 3) + fmt.Println("将数组长度扩展至 8 ,得到 nums = ", nums) -func TestInsert(t *testing.T) { - t.Parallel() - nums := [5]int{1, 2, 3, 4, 5} - insert(&nums, 5, 0) - if nums[0] != 5 { - t.Fatalf("Expected index[0] val: 5, got: %v", nums[0]) - } -} + /* 插入元素 */ + insert(nums, 6, 3) + fmt.Println("在索引 3 处插入数字 6 ,得到 nums = ", nums) -func TestRemove(t *testing.T) { - t.Parallel() - type fields struct { - index int - after [5]int - before [5]int - } + /* 删除元素 */ + remove(nums, 2) + fmt.Println("删除索引 2 处的元素,得到 nums = ", nums) - tests := []struct { - name string - fields fields - }{ - { - name: "remove index[0]", - fields: struct { - index int - after [5]int - before [5]int - }{ - index: 0, - after: [5]int{2, 3, 4, 5, 0}, - before: [5]int{1, 2, 3, 4, 5}, - }, - }, - { - name: "remove end", - fields: struct { - index int - after [5]int - before [5]int - }{ - index: 4, - after: [5]int{1, 2, 3, 4, 0}, - before: [5]int{1, 2, 3, 4, 5}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - v := tt.fields - remove(&v.before, v.index) - if !reflect.DeepEqual(v.before, v.after) { - t.Errorf("remove(&v.before, v.index) = %v, want %v", v.before, v.after) - } - }) - } -} - -func TestTraverse(t *testing.T) { - t.Parallel() - nums := [5]int{1, 2, 3, 4, 5} + /* 遍历数组 */ traverse(nums) -} - -func TestFind(t *testing.T) { - t.Parallel() - type fields struct { - target int - nums [5]int - } - - tests := []struct { - name string - fields fields - want int - }{ - { - name: "element exists", - fields: struct { - target int - nums [5]int - }{ - target: 1, - nums: [5]int{1, 2, 3, 4, 5}, - }, - want: 0, - }, - { - name: "element does not exist", - fields: struct { - target int - nums [5]int - }{ - target: 6, - nums: [5]int{1, 2, 3, 4, 5}, - }, - want: -1, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - v := tt.fields - if got := find(v.nums, v.target); got != tt.want { - t.Errorf("find(v.nums, v.target) = %v, want %v", got, tt.want) - } - }) - } + + /* 查找元素 */ + index := find(nums, 3) + fmt.Println("在 nums 中查找元素 3 ,得到索引 = ", index) } diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 52d23024f..643b16a23 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -44,8 +44,8 @@ comments: true ```go title="array.go" /* 初始化数组 */ - var arr [5]int // {0, 0, 0, 0, 0} - nums := [5]int{1, 3, 2, 5, 4} + var arr []int + nums := []int{1, 3, 2, 5, 4} ``` === "JavaScript" @@ -136,11 +136,11 @@ elementAddr = firtstElementAddr + elementLength * elementIndex ```go title="array.go" /* 随机返回一个数组元素 */ - func randomAccess(nums [5]int) (ans int) { + func randomAccess(nums [5]int) (randomNum int) { // 在区间 [0, nums.length) 中随机抽取一个数字 randomIndex := rand.Intn(len(nums)) // 获取并返回随机元素 - ans = nums[randomIndex] + randomNum = nums[randomIndex] return } ``` @@ -245,13 +245,10 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - // 在 Go 中,声明数组长度必须是常量表达式, - // 所以这里我们约定,扩展后的长度为 6 - const expectSize = 6 - // 扩展数组长度 - func extend(nums [5]int) [expectSize]int { + /* 扩展数组长度 */ + func extend(nums []int, enlarge int) []int { // 初始化一个扩展长度后的数组 - var res [expectSize]int + res := make([]int, len(nums)+enlarge) // 将原数组中的所有元素复制到新数组 for i := 0; i < len(nums); i++ { res[i] = nums[i] @@ -393,7 +390,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex ```go title="array.go" /* 在数组的索引 index 处插入元素 num */ - func insert(nums [5]int, num int, index int) { + func insert(nums []int, num int, index int) { // 把索引 index 以及之后的所有元素向后移动一位 // 如果超出了数组长度,会被直接舍弃 for i := len(nums) - 1; i > index; i-- { @@ -404,11 +401,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex } /* 删除索引 index 处元素 */ - func remove(nums [5]int, index int) { - // 越界检查 - if index >= len(nums) { - return - } + func remove(nums []int, index int) { // 把索引 index 之后的所有元素向前移动一位 for i := index; i < len(nums); i++ { if i+1 >= len(nums) { @@ -547,7 +540,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex ```go title="array.go" /* 遍历数组 */ - func traverse(nums [5]int) { + func traverse(nums []int) { var count int // 通过索引遍历数组 for i := 0; i < len(nums); i++ { @@ -555,7 +548,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex } // 直接遍历数组 for index, val := range nums { - fmt.Printf("index:%d value:%d\n", index, val) + fmt.Printf("index:%v value:%v\n", index, val) } } ``` @@ -663,11 +656,11 @@ elementAddr = firtstElementAddr + elementLength * elementIndex ```go title="array.go" /* 在数组中查找指定元素 */ - func find(nums [5]int, target int) (ans int){ - ans = -1 + func find(nums []int, target int) (index int) { + index = -1 for i := 0; i < len(nums); i++ { if nums[i] == target { - ans = i + index = i break } } From bd5cfb1117e52e8b81d1a29de454041df7388c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=9A=E5=9B=BD=E7=8E=AE?= Date: Wed, 28 Dec 2022 12:29:29 +0800 Subject: [PATCH 026/158] docs(binary_tree): fix comment style and punctuation --- docs/chapter_tree/binary_tree.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 07e311551..0c858fd3a 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -44,13 +44,13 @@ comments: true === "Go" ```go title="" - // 链表结点类 + /* 链表结点类 */ type TreeNode struct { Val int Left *TreeNode Right *TreeNode } - // 结点初始化方法 + /* 结点初始化方法 */ func NewTreeNode(v int) *TreeNode { return &TreeNode{ Left: nil, @@ -121,7 +121,7 @@ comments: true - 「根结点 Root Node」:二叉树最顶层的结点,其没有父结点; - 「叶结点 Leaf Node」:没有子结点的结点,其两个指针都指向 $\text{null}$ ; - 结点所处「层 Level」:从顶置底依次增加,根结点所处层为 1 ; -- 结点「度 Degree」:结点的子结点数量,二叉树中度的范围是 0, 1, 2 ; +- 结点「度 Degree」:结点的子结点数量。二叉树中,度的范围是 0, 1, 2 ; - 「边 Edge」:连接两个结点的边,即结点指针; - 二叉树「高度」:二叉树中根结点到最远叶结点走过边的数量; - 结点「深度 Depth」 :根结点到该结点走过边的数量; From af5497e70bae154f295ba1d6ff7c02af34b765c9 Mon Sep 17 00:00:00 2001 From: XC-Zero <55583209+XC-Zero@users.noreply.github.com> Date: Wed, 28 Dec 2022 14:11:36 +0800 Subject: [PATCH 027/158] Update data_and_memory.md add code for go --- docs/chapter_data_structure/data_and_memory.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/chapter_data_structure/data_and_memory.md b/docs/chapter_data_structure/data_and_memory.md index 78fcac857..b4c8fbe56 100644 --- a/docs/chapter_data_structure/data_and_memory.md +++ b/docs/chapter_data_structure/data_and_memory.md @@ -74,7 +74,11 @@ comments: true === "Go" ```go title="" - + var numbers =[5]int{} + var decimals =[5]float64{} + // go 里没有char ,但有 rune/byte 代替 char + var characters =[5]byte{} + var booleans =[5]bool{} ``` === "JavaScript" From a4161b5fa2747be17be932d1b5d9227b08a8fad9 Mon Sep 17 00:00:00 2001 From: XC-Zero <55583209+XC-Zero@users.noreply.github.com> Date: Wed, 28 Dec 2022 14:27:28 +0800 Subject: [PATCH 028/158] Update data_and_memory.md update tab to space --- docs/chapter_data_structure/data_and_memory.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/chapter_data_structure/data_and_memory.md b/docs/chapter_data_structure/data_and_memory.md index b4c8fbe56..0a4787845 100644 --- a/docs/chapter_data_structure/data_and_memory.md +++ b/docs/chapter_data_structure/data_and_memory.md @@ -74,11 +74,11 @@ comments: true === "Go" ```go title="" - var numbers =[5]int{} - var decimals =[5]float64{} - // go 里没有char ,但有 rune/byte 代替 char - var characters =[5]byte{} - var booleans =[5]bool{} + var numbers = [5]int{} + var decimals = [5]float64{} + // go 里没有char ,但有 rune/byte 用以代替 char + var characters = [5]byte{} + var booleans = [5]bool{} ``` === "JavaScript" From debcc984e1cc339f92c036b12cadbea7c65822ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=9A=E5=9B=BD=E7=8E=AE?= Date: Wed, 28 Dec 2022 17:07:59 +0800 Subject: [PATCH 029/158] docs(binary_search_tree): variable name error Combining Context, Compare 'cur.val' and 'num', not 'cur.val' and 'val`. --- docs/chapter_tree/binary_search_tree.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index b341e3dc4..629c051f1 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -17,9 +17,9 @@ comments: true 给定目标结点值 `num` ,可以根据二叉搜索树的性质来查找。我们声明一个结点 `cur` ,从二叉树的根结点 `root` 出发,循环比较结点值 `cur.val` 和 `num` 之间的大小关系 -- 若 `cur.val < val` ,说明目标结点在 `cur` 的右子树中,因此执行 `cur = cur.right` ; -- 若 `cur.val > val` ,说明目标结点在 `cur` 的左子树中,因此执行 `cur = cur.left` ; -- 若 `cur.val = val` ,说明找到目标结点,跳出循环并返回该结点即可; +- 若 `cur.val < num` ,说明目标结点在 `cur` 的右子树中,因此执行 `cur = cur.right` ; +- 若 `cur.val > num` ,说明目标结点在 `cur` 的左子树中,因此执行 `cur = cur.left` ; +- 若 `cur.val = num` ,说明找到目标结点,跳出循环并返回该结点即可; === "Step 1" From 678e1a025b4a1050c0897372d5ddc0c6d4e9d332 Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Wed, 28 Dec 2022 20:04:39 +0800 Subject: [PATCH 030/158] add zig language support --- .../time_complexity.zig | 179 ++++++++++++++++++ .../worst_best_time_complexity.zig | 42 ++++ 2 files changed, 221 insertions(+) create mode 100644 codes/zig/chapter_computational_complexity/time_complexity.zig create mode 100644 codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig diff --git a/codes/zig/chapter_computational_complexity/time_complexity.zig b/codes/zig/chapter_computational_complexity/time_complexity.zig new file mode 100644 index 000000000..dab17034a --- /dev/null +++ b/codes/zig/chapter_computational_complexity/time_complexity.zig @@ -0,0 +1,179 @@ +// File: time_complexity.zig +// Created Time: 2022-12-28 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); + +// 常数阶 +fn constant(n: i32) i32 { + _ = n; + var count: i32 = 0; + const size: i32 = 100_000; + var i: i32 = 0; + while(i 0) : (i -= 1) { + var j: usize = 0; + // 内循环:冒泡操作 + while (j < i) : (j += 1) { + // 交换 nums[j] 与 nums[j + 1] + var tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + return count; +} + +// 指数阶(循环实现) +fn exponential(n: i32) i32{ + var count: i32 = 0; + var bas: i32 = 1; + var i: i32 = 0; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + while (i < n) : (i += 1) { + var j: i32 = 0; + while (j < bas) : (j += 1) { + count += 1; + } + bas *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} + +// 指数阶(递归实现) +fn expRecur(n: i32) i32{ + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} + +// 对数阶(循环实现) +fn logarithmic(n: f32) i32 +{ + var count: i32 = 0; + var n_var = n; + while (n_var > 1) + { + n_var = n_var / 2; + count +=1; + } + return count; +} + +// 对数阶(递归实现) +fn logRecur(n: f32) i32 +{ + if (n <= 1) return 0; + return logRecur(n / 2) + 1; +} + +// 线性对数阶 +fn linearLogRecur(n: f32) i32 +{ + if (n <= 1) return 1; + var count: i32 = linearLogRecur(n / 2) + + linearLogRecur(n / 2); + var i: f32 = 0; + while (i < n) : (i += 1) { + count += 1; + } + return count; +} + +// 阶乘阶(递归实现) +fn factorialRecur(n: i32) i32 { + if (n == 0) return 1; + var count: i32 = 0; + var i: i32 = 0; + // 从 1 个分裂出 n 个 + while (i < n) : (i += 1) { + count += factorialRecur(n - 1); + } + return count; +} + +// Driver Code +pub fn main() void { + // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 + const n: i32 = 8; + std.debug.print("输入数据大小 n = {}\n", .{n}); + + var count = constant(n); + std.debug.print("常数阶的计算操作数量 = {}\n", .{count}); + + count = linear(n); + std.debug.print("线性阶的计算操作数量 = {}\n", .{count}); + var nums = [_]i32{0}**n; + count = arrayTraversal(&nums); + std.debug.print("线性阶(遍历数组)的计算操作数量 = {}\n", .{count}); + + count = quadratic(n); + std.debug.print("平方阶的计算操作数量 = {}\n", .{count}); + for (nums) |*num, i| { + num.* = n - @intCast(i32, i); // [n,n-1,...,2,1] + } + count = bubbleSort(&nums); + std.debug.print("平方阶(冒泡排序)的计算操作数量 = {}\n", .{count}); + + count = exponential(n); + std.debug.print("指数阶(循环实现)的计算操作数量 = {}\n", .{count}); + count = expRecur(n); + std.debug.print("指数阶(递归实现)的计算操作数量 = {}\n", .{count}); + + count = logarithmic(@as(f32, n)); + std.debug.print("对数阶(循环实现)的计算操作数量 = {}\n", .{count}); + count = logRecur(@as(f32, n)); + std.debug.print("对数阶(递归实现)的计算操作数量 = {}\n", .{count}); + + count = linearLogRecur(@as(f32, n)); + std.debug.print("线性对数阶(递归实现)的计算操作数量 = {}\n", .{count}); + + count = factorialRecur(n); + std.debug.print("阶乘阶(递归实现)的计算操作数量 = {}\n", .{count}); +} + diff --git a/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig b/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig new file mode 100644 index 000000000..dacf45952 --- /dev/null +++ b/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig @@ -0,0 +1,42 @@ +// File: worst_best_time_complexity.zig +// Created Time: 2022-12-28 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); + +// 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 +pub fn randomNumbers(comptime n: usize) [n]i32 { + var nums: [n]i32 = undefined; + // 生成数组 nums = { 1, 2, 3, ..., n } + for (nums) |*num, i| { + num.* = @intCast(i32, i) + 1; + } + // 随机打乱数组元素 + const rand = std.crypto.random; + rand.shuffle(i32, &nums); + return nums; +} + +// 查找数组 nums 中数字 1 所在索引 +pub fn findOne(nums: []i32) i32 { + for (nums) |num, i| { + if (num == 1) return @intCast(i32, i); + } + return -1; +} + +// Driver Code +pub fn main() !void { + var i: i32 = 0; + while (i < 10) : (i += 1) { + const n: usize = 100; + var nums = randomNumbers(n); + var index = findOne(&nums); + std.debug.print("\n数组 [ 1, 2, ..., n ] 被打乱后 = ", .{}); + for (nums) |num, j| { + std.debug.print("{}{s}", .{num, if (j == nums.len-1) "" else "," }); + } + std.debug.print("\n数字 1 的索引为 {}\n", .{index}); + } +} + From 78c2b94422818e7c0bcc2dcedec12ce8306a2b49 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 29 Dec 2022 00:49:50 +0800 Subject: [PATCH 031/158] Update data_and_memory.md --- docs/chapter_data_structure/data_and_memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_data_structure/data_and_memory.md b/docs/chapter_data_structure/data_and_memory.md index 0a4787845..3e1e2167c 100644 --- a/docs/chapter_data_structure/data_and_memory.md +++ b/docs/chapter_data_structure/data_and_memory.md @@ -74,9 +74,9 @@ comments: true === "Go" ```go title="" + // 使用多种「基本数据类型」来初始化「数组」 var numbers = [5]int{} var decimals = [5]float64{} - // go 里没有char ,但有 rune/byte 用以代替 char var characters = [5]byte{} var booleans = [5]bool{} ``` From 4fb267918b6047b946f4eaebb193d44929e4419a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=9A=E5=9B=BD=E7=8E=AE?= Date: Thu, 29 Dec 2022 10:06:11 +0800 Subject: [PATCH 032/158] docs(array): add file author, created time --- codes/go/chapter_array_and_linkedlist/array.go | 4 ++++ codes/go/chapter_array_and_linkedlist/array_test.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/codes/go/chapter_array_and_linkedlist/array.go b/codes/go/chapter_array_and_linkedlist/array.go index ccc5db19b..26c056763 100644 --- a/codes/go/chapter_array_and_linkedlist/array.go +++ b/codes/go/chapter_array_and_linkedlist/array.go @@ -1,3 +1,7 @@ +// File: array.go +// Created Time: 2022-12-29 +// Author: GuoWei (gongguowei01@gmail.com) + package chapter_array_and_linkedlist import ( diff --git a/codes/go/chapter_array_and_linkedlist/array_test.go b/codes/go/chapter_array_and_linkedlist/array_test.go index 3f9882a9d..384cc34d9 100644 --- a/codes/go/chapter_array_and_linkedlist/array_test.go +++ b/codes/go/chapter_array_and_linkedlist/array_test.go @@ -1,3 +1,7 @@ +// File: array_test.go +// Created Time: 2022-12-29 +// Author: GuoWei (gongguowei01@gmail.com) + package chapter_array_and_linkedlist /** From c0e8b75bfd323b9dd304301b28969dc7d9d6cb75 Mon Sep 17 00:00:00 2001 From: Cathay Date: Thu, 29 Dec 2022 13:26:09 +0800 Subject: [PATCH 033/158] feat(array): add the Go code to array docs (Chapter of Array and LinkedList) --- .../go/chapter_array_and_linkedlist/array.go | 78 +++++++++++++++++++ .../array_test.go | 28 +++++++ docs/chapter_array_and_linkedlist/array.md | 63 +++++++++++++-- 3 files changed, 163 insertions(+), 6 deletions(-) create mode 100644 codes/go/chapter_array_and_linkedlist/array.go create mode 100644 codes/go/chapter_array_and_linkedlist/array_test.go diff --git a/codes/go/chapter_array_and_linkedlist/array.go b/codes/go/chapter_array_and_linkedlist/array.go new file mode 100644 index 000000000..416957ff3 --- /dev/null +++ b/codes/go/chapter_array_and_linkedlist/array.go @@ -0,0 +1,78 @@ +// File: array.go +// Created Time: 2022-12-29 +// Author: cathay (cathaycchen@gmail.com) + +package chapter_array_and_linkedlist + +import ( + "fmt" + "math/rand" +) + +/* 初始化数组 */ +var arr = [5]int{} +var nums = []int{1, 3, 2, 5, 4} + +/* 随机返回一个数组元素 */ +func randomAccess(nums []int) int { + randomIndex := rand.Intn(len(nums)) + randomNum := nums[randomIndex] + return randomNum +} + +/* 扩展数组长度 */ +func extend(nums []int, enlarge int) []int { + // 初始化一个扩展长度后的数组 + res := make([]int, len(nums)+enlarge) + // 将原数组中的所有元素复制到新数组 + for i, num := range nums { + res[i] = num + } + // 返回扩展后的新数组 + return res +} + +/* 在数组的索引 index 处插入元素 num */ +func insert(nums []int, size int, num int, index int) { + // 把索引 index 以及之后的所有元素向后移动一位 + for i := size - 1; i > index; i-- { + nums[i] = nums[i-1] + } + // 将 num 赋给 index 处元素 + nums[index] = num +} + +/* 删除索引 index 处元素 */ +func remove(nums []int, size int, index int) { + // 把索引 index 之后的所有元素向前移动一位 + for i := index; i < size-1; i++ { + nums[i] = nums[i+1] + } +} + +/* 遍历数组 */ +func traverse(nums []int) { + count := 0 + // 通过索引遍历数组 + for i := 0; i < len(nums); i++ { + count++ + } + fmt.Println(count) + + count = 0 + // 直接遍历数组 + for range nums { + count++ + } + fmt.Println(count) +} + +/* 在数组中查找指定元素,返回第一个索引位置,未查找到则返回 -1 */ +func find(nums []int, target int) int { + for i := 0; i < len(nums); i++ { + if nums[i] == target { + return i + } + } + return -1 +} diff --git a/codes/go/chapter_array_and_linkedlist/array_test.go b/codes/go/chapter_array_and_linkedlist/array_test.go new file mode 100644 index 000000000..37f75a55d --- /dev/null +++ b/codes/go/chapter_array_and_linkedlist/array_test.go @@ -0,0 +1,28 @@ +// File: array_test.go +// Created Time: 2022-12-29 +// Author: cathay (cathaycchen@gmail.com) + +package chapter_array_and_linkedlist + +import ( + "fmt" + "testing" +) + +func TestArray(t *testing.T) { + nums := make([]int, 5) + fmt.Println("randomAccess:", randomAccess(nums)) + + fmt.Println("extend:", extend(nums, 5)) + + insert(nums, 5, 2, 2) + fmt.Println("after insert:", nums) + + remove(nums, 5, 2) + fmt.Println("after remove:", nums) + + fmt.Println("traverse nums:") + traverse(nums) + + fmt.Println("find value 2 key:", find(nums, 2)) +} diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 70e30129a..bae5c2772 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -43,7 +43,8 @@ comments: true === "Go" ```go title="array.go" - + var arr = [5]int{} + var nums = [5]int{1, 3, 2, 5, 4} ``` === "JavaScript" @@ -136,7 +137,12 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - + /* 随机返回一个数组元素 */ + func randomAccess(nums []int) int { + randomIndex := rand.Intn(len(nums)) + randomNum := nums[randomIndex] + return randomNum + } ``` === "JavaScript" @@ -239,7 +245,17 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - + /* 扩展数组长度 */ + func extend(nums []int, enlarge int) []int { + // 初始化一个扩展长度后的数组 + res := make([]int, len(nums)+enlarge) + // 将原数组中的所有元素复制到新数组 + for i, num := range nums { + res[i] = num + } + // 返回扩展后的新数组 + return res + } ``` === "JavaScript" @@ -373,7 +389,23 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - + /* 在数组的索引 index 处插入元素 num */ + func insert(nums []int, size int, num int, index int) { + // 把索引 index 以及之后的所有元素向后移动一位 + for i := size - 1; i > index; i-- { + nums[i] = nums[i-1] + } + // 将 num 赋给 index 处元素 + nums[index] = num + } + + /* 删除索引 index 处元素 */ + func remove(nums []int, size int, index int) { + // 把索引 index 之后的所有元素向前移动一位 + for i := index; i < size-1; i++ { + nums[i] = nums[i+1] + } + } ``` === "JavaScript" @@ -502,7 +534,18 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - + /* 遍历数组 */ + func traverse(nums []int) { + count := 0 + // 通过索引遍历数组 + for i := 0; i < len(nums); i++ { + count++ + } + // 直接遍历数组 + for range nums { + count++ + } + } ``` === "JavaScript" @@ -607,7 +650,15 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Go" ```go title="array.go" - + /* 在数组中查找指定元素,返回第一个索引位置,未查找到则返回 -1 */ + func find(nums []int, target int) int { + for i := 0; i < len(nums); i++ { + if nums[i] == target { + return i + } + } + return -1 + } ``` === "JavaScript" From a5e923a387f8380ede28b0b7a4bf0983e02b6705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=9A=E5=9B=BD=E7=8E=AE?= Date: Thu, 29 Dec 2022 14:44:15 +0800 Subject: [PATCH 034/158] test(binary_search_tree): update test param use param value 7, not 5, function test param value with param value in example picture as same. --- codes/go/chapter_tree/binary_search_tree_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codes/go/chapter_tree/binary_search_tree_test.go b/codes/go/chapter_tree/binary_search_tree_test.go index 0d0369a78..d98e5d661 100644 --- a/codes/go/chapter_tree/binary_search_tree_test.go +++ b/codes/go/chapter_tree/binary_search_tree_test.go @@ -23,7 +23,7 @@ func TestBinarySearchTree(t *testing.T) { fmt.Println("二叉树的最小结点为:", node.Val) // 查找结点 - node = bst.Search(5) + node = bst.Search(7) fmt.Println("查找到的结点对象为", node, ",结点值 =", node.Val) // 插入结点 From 1062fe64a398aacb8c4e00e17c03e57289dd9ae9 Mon Sep 17 00:00:00 2001 From: Cathay Date: Thu, 29 Dec 2022 17:43:16 +0800 Subject: [PATCH 035/158] feat(linked_list): add the Go code to linked_list docs (Chapter of Array and LinkedList) --- .../linked_list.go | 105 ++++++++++++++++++ .../linked_list_test.go | 47 ++++++++ .../linked_list.md | 82 +++++++++++++- 3 files changed, 230 insertions(+), 4 deletions(-) create mode 100644 codes/go/chapter_array_and_linkedlist/linked_list.go create mode 100644 codes/go/chapter_array_and_linkedlist/linked_list_test.go diff --git a/codes/go/chapter_array_and_linkedlist/linked_list.go b/codes/go/chapter_array_and_linkedlist/linked_list.go new file mode 100644 index 000000000..3129a0eaf --- /dev/null +++ b/codes/go/chapter_array_and_linkedlist/linked_list.go @@ -0,0 +1,105 @@ +// File: linked_list.go +// Created Time: 2022-12-29 +// Author: cathay (cathaycchen@gmail.com) + +package chapter_array_and_linkedlist + +import ( + "fmt" + "strconv" + "strings" +) + +/* 链表结点结构体 */ +type ListNode struct { + Val int // 结点值 + Next *ListNode // 指向下一结点的指针(引用) +} + +// NewListNode 构造函数,创建一个新的链表 +func NewListNode(val int) *ListNode { + return &ListNode{ + Val: val, + Next: nil, + } +} + +// PrintLinkedList Print a linked list +func PrintLinkedList(node *ListNode) { + if node == nil { + return + } + var builder strings.Builder + for node.Next != nil { + builder.WriteString(strconv.Itoa(node.Val) + " -> ") + node = node.Next + } + builder.WriteString(strconv.Itoa(node.Val)) + fmt.Println(builder.String()) +} + +/* 在链表的结点 n0 之后插入结点 P */ +func insertNode(n0 *ListNode, P *ListNode) { + n1 := n0.Next + n0.Next = P + P.Next = n1 +} + +/* 删除链表的结点 n0 之后的首个结点 */ +func removeNode(n0 *ListNode) { + if n0.Next == nil { + return + } + // n0 -> P -> n1 + P := n0.Next + n1 := P.Next + n0.Next = n1 +} + +/* 访问链表中索引为 index 的结点 */ +func access(head *ListNode, index int) *ListNode { + for i := 0; i < index; i++ { + head = head.Next + if head == nil { + return nil + } + } + return head +} + +/* 在链表中查找值为 target 的首个结点 */ +func findNode(head *ListNode, target int) int { + index := 0 + for head != nil { + if head.Val == target { + return index + } + head = head.Next + index++ + } + return -1 +} + +/* 双向链表结点结构体 */ +type DoublyListNode struct { + Val int // 结点值 + Next *DoublyListNode // 指向后继结点的指针(引用) + Prev *DoublyListNode // 指向前驱结点的指针(引用) +} + +// NewDoublyListNode 初始化 +func NewDoublyListNode(val int, nodes ...*DoublyListNode) *DoublyListNode { + var next, prev *DoublyListNode + length := len(nodes) + if length > 0 { + next = nodes[0] + } + if length > 1 { + prev = nodes[1] + } + return &DoublyListNode{ + Val: val, + Next: next, + Prev: prev, + } +} diff --git a/codes/go/chapter_array_and_linkedlist/linked_list_test.go b/codes/go/chapter_array_and_linkedlist/linked_list_test.go new file mode 100644 index 000000000..5deabff88 --- /dev/null +++ b/codes/go/chapter_array_and_linkedlist/linked_list_test.go @@ -0,0 +1,47 @@ +// File: linked_list_test.go +// Created Time: 2022-12-29 +// Author: cathay (cathaycchen@gmail.com) + +package chapter_array_and_linkedlist + +import ( + "fmt" + "testing" +) + +func TestLikedList(t *testing.T) { + /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ + // 初始化各个结点 + n0 := NewListNode(1) + n1 := NewListNode(3) + n2 := NewListNode(2) + n3 := NewListNode(5) + n4 := NewListNode(4) + + // 构建引用指向 + n0.Next = n1 + n1.Next = n2 + n2.Next = n3 + n3.Next = n4 + + fmt.Println("链表结构:") + PrintLinkedList(n0) + + P := NewListNode(6) + + /* 在链表的结点 n0 之后插入结点 P */ + insertNode(n0, P) + fmt.Println("在链表的结点 n0 之后插入结点 P 后,链表结构:") + PrintLinkedList(n0) + + /* 删除链表的结点 n0 之后的首个结点 */ + removeNode(n0) + fmt.Println("删除链表的结点 n0 之后的首个结点后,链表结构:") + PrintLinkedList(n0) + + /* 访问链表中索引为 index 的结点 */ + fmt.Println("访问链表中索引为 2 的结点:", access(n0, 2)) + + /* 在链表中查找值为 target 的首个结点 */ + fmt.Println("在链表中查找 5 的首个节点索引值:", findNode(n0, 5)) +} diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index 6ad1d5658..83c8db64e 100644 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -51,7 +51,19 @@ comments: true === "Go" ```go title="" - + /* 链表结点结构体 */ + type ListNode struct { + Val int // 结点值 + Next *ListNode // 指向下一结点的指针(引用) + } + + // NewListNode 构造函数,创建一个新的链表 + func NewListNode(val int) *ListNode { + return &ListNode{ + Val: val, + Next: nil, + } + } ``` === "JavaScript" @@ -162,7 +174,19 @@ comments: true === "Go" ```go title="" + /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ + // 初始化各个结点 + n0 := NewListNode(1) + n1 := NewListNode(3) + n2 := NewListNode(2) + n3 := NewListNode(5) + n4 := NewListNode(4) + // 构建引用指向 + n0.Next = n1 + n1.Next = n2 + n2.Next = n3 + n3.Next = n4 ``` === "JavaScript" @@ -294,7 +318,23 @@ comments: true === "Go" ```go title="" + /* 在链表的结点 n0 之后插入结点 P */ + func insert(n0 *ListNode, P *ListNode) { + n1 := n0.Next + n0.Next = P + P.Next = n1 + } + /* 删除链表的结点 n0 之后的首个结点 */ + func removeNode(n0 *ListNode) { + if n0.Next == nil { + return + } + // n0 -> P -> n1 + P := n0.Next + n1 := P.Next + n0.Next = n1 + } ``` === "JavaScript" @@ -415,7 +455,16 @@ comments: true === "Go" ```go title="" - + /* 访问链表中索引为 index 的结点 */ + func access(head *ListNode, index int) *ListNode { + for i := 0; i < index; i++ { + head = head.Next + if head == nil { + return nil + } + } + return head + } ``` === "JavaScript" @@ -524,7 +573,18 @@ comments: true === "Go" ```go title="" - + /* 在链表中查找值为 target 的首个结点 */ + func find(head *ListNode, target int) int { + index := 0 + for head != nil { + if head.Val == target { + return index + } + head = head.Next + index++ + } + return -1 + } ``` === "JavaScript" @@ -631,7 +691,21 @@ comments: true === "Go" ```go title="" - + /* 双向链表结点结构体 */ + type DoublyListNode struct { + Val int // 结点值 + Next *DoublyListNode // 指向后继结点的指针(引用) + Prev *DoublyListNode // 指向前驱结点的指针(引用) + } + + // NewDoublyListNode 初始化 + func NewDoublyListNode(val int) *DoublyListNode { + return &DoublyListNode{ + Val: val, + Next: nil, + Prev: nil, + } + } ``` === "JavaScript" From cd9f4fc35d48cbb0547622d40fcef2d01e814d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9B=BD=E5=A4=AA?= Date: Thu, 29 Dec 2022 21:49:46 +0800 Subject: [PATCH 036/158] fix(array and linkedlist): fix that the printing in the test function is the same as that in other languages --- .../go/chapter_array_and_linkedlist/array.go | 12 ++-- .../array_test.go | 33 +++++++--- .../linked_list.go | 64 ++----------------- .../linked_list_test.go | 42 ++++++------ 4 files changed, 54 insertions(+), 97 deletions(-) diff --git a/codes/go/chapter_array_and_linkedlist/array.go b/codes/go/chapter_array_and_linkedlist/array.go index 416957ff3..1a02bea47 100644 --- a/codes/go/chapter_array_and_linkedlist/array.go +++ b/codes/go/chapter_array_and_linkedlist/array.go @@ -9,10 +9,6 @@ import ( "math/rand" ) -/* 初始化数组 */ -var arr = [5]int{} -var nums = []int{1, 3, 2, 5, 4} - /* 随机返回一个数组元素 */ func randomAccess(nums []int) int { randomIndex := rand.Intn(len(nums)) @@ -33,9 +29,9 @@ func extend(nums []int, enlarge int) []int { } /* 在数组的索引 index 处插入元素 num */ -func insert(nums []int, size int, num int, index int) { +func insert(nums []int, num int, index int) { // 把索引 index 以及之后的所有元素向后移动一位 - for i := size - 1; i > index; i-- { + for i := len(nums) - 1; i > index; i-- { nums[i] = nums[i-1] } // 将 num 赋给 index 处元素 @@ -43,9 +39,9 @@ func insert(nums []int, size int, num int, index int) { } /* 删除索引 index 处元素 */ -func remove(nums []int, size int, index int) { +func remove(nums []int, index int) { // 把索引 index 之后的所有元素向前移动一位 - for i := index; i < size-1; i++ { + for i := index; i < len(nums)-1; i++ { nums[i] = nums[i+1] } } diff --git a/codes/go/chapter_array_and_linkedlist/array_test.go b/codes/go/chapter_array_and_linkedlist/array_test.go index 37f75a55d..4938ac509 100644 --- a/codes/go/chapter_array_and_linkedlist/array_test.go +++ b/codes/go/chapter_array_and_linkedlist/array_test.go @@ -9,20 +9,35 @@ import ( "testing" ) +/* Driver Code */ func TestArray(t *testing.T) { - nums := make([]int, 5) - fmt.Println("randomAccess:", randomAccess(nums)) + /* 初始化数组 */ + var arr = [5]int{} + fmt.Println("数组 arr =", arr) - fmt.Println("extend:", extend(nums, 5)) + var nums = []int{1, 3, 2, 5, 4} + fmt.Println("数组 nums =", nums) - insert(nums, 5, 2, 2) - fmt.Println("after insert:", nums) + /* 随机访问 */ + randomNum := randomAccess(nums) + fmt.Println("在 nums 中获取随机元素", randomNum) - remove(nums, 5, 2) - fmt.Println("after remove:", nums) + /* 长度扩展 */ + nums = extend(nums, 3) + fmt.Println("将数组长度扩展至 8 ,得到 nums =", nums) - fmt.Println("traverse nums:") + /* 插入元素 */ + insert(nums, 6, 3) + fmt.Println("在索引 3 处插入数字 6 ,得到 nums =", nums) + + /* 删除元素 */ + remove(nums, 2) + fmt.Println("删除索引 2 处的元素,得到 nums = ", nums) + + /* 遍历数组 */ traverse(nums) - fmt.Println("find value 2 key:", find(nums, 2)) + /* 查找元素 */ + index := find(nums, 3) + fmt.Println("在 nums 中查找元素 3 ,得到索引 =", index) } diff --git a/codes/go/chapter_array_and_linkedlist/linked_list.go b/codes/go/chapter_array_and_linkedlist/linked_list.go index 3129a0eaf..e7fdb35ca 100644 --- a/codes/go/chapter_array_and_linkedlist/linked_list.go +++ b/codes/go/chapter_array_and_linkedlist/linked_list.go @@ -5,48 +5,18 @@ package chapter_array_and_linkedlist import ( - "fmt" - "strconv" - "strings" + "github.com/krahets/hello-algo/pkg" ) -/* 链表结点结构体 */ -type ListNode struct { - Val int // 结点值 - Next *ListNode // 指向下一结点的指针(引用) -} - -// NewListNode 构造函数,创建一个新的链表 -func NewListNode(val int) *ListNode { - return &ListNode{ - Val: val, - Next: nil, - } -} - -// PrintLinkedList Print a linked list -func PrintLinkedList(node *ListNode) { - if node == nil { - return - } - var builder strings.Builder - for node.Next != nil { - builder.WriteString(strconv.Itoa(node.Val) + " -> ") - node = node.Next - } - builder.WriteString(strconv.Itoa(node.Val)) - fmt.Println(builder.String()) -} - /* 在链表的结点 n0 之后插入结点 P */ -func insertNode(n0 *ListNode, P *ListNode) { +func insertNode(n0 *pkg.ListNode, P *pkg.ListNode) { n1 := n0.Next n0.Next = P P.Next = n1 } /* 删除链表的结点 n0 之后的首个结点 */ -func removeNode(n0 *ListNode) { +func removeNode(n0 *pkg.ListNode) { if n0.Next == nil { return } @@ -57,7 +27,7 @@ func removeNode(n0 *ListNode) { } /* 访问链表中索引为 index 的结点 */ -func access(head *ListNode, index int) *ListNode { +func access(head *pkg.ListNode, index int) *pkg.ListNode { for i := 0; i < index; i++ { head = head.Next if head == nil { @@ -68,7 +38,7 @@ func access(head *ListNode, index int) *ListNode { } /* 在链表中查找值为 target 的首个结点 */ -func findNode(head *ListNode, target int) int { +func findNode(head *pkg.ListNode, target int) int { index := 0 for head != nil { if head.Val == target { @@ -79,27 +49,3 @@ func findNode(head *ListNode, target int) int { } return -1 } - -/* 双向链表结点结构体 */ -type DoublyListNode struct { - Val int // 结点值 - Next *DoublyListNode // 指向后继结点的指针(引用) - Prev *DoublyListNode // 指向前驱结点的指针(引用) -} - -// NewDoublyListNode 初始化 -func NewDoublyListNode(val int, nodes ...*DoublyListNode) *DoublyListNode { - var next, prev *DoublyListNode - length := len(nodes) - if length > 0 { - next = nodes[0] - } - if length > 1 { - prev = nodes[1] - } - return &DoublyListNode{ - Val: val, - Next: next, - Prev: prev, - } -} diff --git a/codes/go/chapter_array_and_linkedlist/linked_list_test.go b/codes/go/chapter_array_and_linkedlist/linked_list_test.go index 5deabff88..18061efff 100644 --- a/codes/go/chapter_array_and_linkedlist/linked_list_test.go +++ b/codes/go/chapter_array_and_linkedlist/linked_list_test.go @@ -6,42 +6,42 @@ package chapter_array_and_linkedlist import ( "fmt" + "github.com/krahets/hello-algo/pkg" "testing" ) func TestLikedList(t *testing.T) { /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ // 初始化各个结点 - n0 := NewListNode(1) - n1 := NewListNode(3) - n2 := NewListNode(2) - n3 := NewListNode(5) - n4 := NewListNode(4) + n0 := pkg.NewListNode(1) + n1 := pkg.NewListNode(3) + n2 := pkg.NewListNode(2) + n3 := pkg.NewListNode(5) + n4 := pkg.NewListNode(4) // 构建引用指向 n0.Next = n1 n1.Next = n2 n2.Next = n3 n3.Next = n4 + fmt.Println("初始化的链表为") + pkg.PrintLinkedList(n0) - fmt.Println("链表结构:") - PrintLinkedList(n0) + /* 插入结点 */ + insertNode(n0, pkg.NewListNode(0)) + fmt.Println("插入结点后的链表为") + pkg.PrintLinkedList(n0) - P := NewListNode(6) - - /* 在链表的结点 n0 之后插入结点 P */ - insertNode(n0, P) - fmt.Println("在链表的结点 n0 之后插入结点 P 后,链表结构:") - PrintLinkedList(n0) - - /* 删除链表的结点 n0 之后的首个结点 */ + /* 删除结点 */ removeNode(n0) - fmt.Println("删除链表的结点 n0 之后的首个结点后,链表结构:") - PrintLinkedList(n0) + fmt.Println("删除结点后的链表为") + pkg.PrintLinkedList(n0) - /* 访问链表中索引为 index 的结点 */ - fmt.Println("访问链表中索引为 2 的结点:", access(n0, 2)) + /* 访问结点 */ + node := access(n0, 3) + fmt.Println("链表中索引 3 处的结点的值 =", node) - /* 在链表中查找值为 target 的首个结点 */ - fmt.Println("在链表中查找 5 的首个节点索引值:", findNode(n0, 5)) + /* 查找结点 */ + index := findNode(n0, 2) + fmt.Println("链表中值为 2 的结点的索引 =", index) } From 4ca09c1015ccd8af6f0592728facaccb3c83b0dc Mon Sep 17 00:00:00 2001 From: GN-Yu <58758623+GN-Yu@users.noreply.github.com> Date: Thu, 29 Dec 2022 17:50:02 -0500 Subject: [PATCH 037/158] Update merge_sort.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改了代码注释使其表述更严谨,如C++中: for (int k = left; k <= right; k++) { // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) nums[k] = tmp[j++]; // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ else if (j > rightEnd || tmp[i] <= tmp[j]) nums[k] = tmp[i++]; // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ else nums[k] = tmp[j++]; } --- docs/chapter_sorting/merge_sort.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index 9ee984271..48a5046c7 100644 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -81,10 +81,10 @@ comments: true // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) nums[k] = tmp[j++]; - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ else if (j > rightEnd || tmp[i] <= tmp[j]) nums[k] = tmp[i++]; - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ else nums[k] = tmp[j++]; } @@ -125,10 +125,10 @@ comments: true // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) nums[k] = tmp[j++]; - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ else if (j > rightEnd || tmp[i] <= tmp[j]) nums[k] = tmp[i++]; - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ else nums[k] = tmp[j++]; } @@ -170,11 +170,11 @@ comments: true if i > left_end: nums[k] = tmp[j] j += 1 - # 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + # 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ elif j > right_end or tmp[i] <= tmp[j]: nums[k] = tmp[i] i += 1 - # 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + # 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ else: nums[k] = tmp[j] j += 1 @@ -218,11 +218,11 @@ comments: true if i > left_end { nums[k] = tmp[j] j++ - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ } else if j > right_end || tmp[i] <= tmp[j] { nums[k] = tmp[i] i++ - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ } else { nums[k] = tmp[j] j++ @@ -267,10 +267,10 @@ comments: true // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) { nums[k] = tmp[j++]; - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ } else if (j > rightEnd || tmp[i] <= tmp[j]) { nums[k] = tmp[i++]; - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ } else { nums[k] = tmp[j++]; } @@ -312,10 +312,10 @@ comments: true // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) { nums[k] = tmp[j++]; - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ } else if (j > rightEnd || tmp[i] <= tmp[j]) { nums[k] = tmp[i++]; - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ } else { nums[k] = tmp[j++]; } @@ -365,10 +365,10 @@ comments: true // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) nums[k] = tmp[j++]; - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ else if (j > rightEnd || tmp[i] <= tmp[j]) nums[k] = tmp[i++]; - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ else nums[k] = tmp[j++]; } From 0dda12e0ab44c0e330f43533e6c13751f2e812b5 Mon Sep 17 00:00:00 2001 From: Listening <120311070@qq.com> Date: Fri, 30 Dec 2022 09:26:26 +0800 Subject: [PATCH 038/158] add insertion sort content --- codes/c/chapter_sorting/insertion_sort.c | 41 ++++++++++++++++++++++++ docs/chapter_sorting/insertion_sort.md | 19 ++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 codes/c/chapter_sorting/insertion_sort.c diff --git a/codes/c/chapter_sorting/insertion_sort.c b/codes/c/chapter_sorting/insertion_sort.c new file mode 100644 index 000000000..ae1f6e7de --- /dev/null +++ b/codes/c/chapter_sorting/insertion_sort.c @@ -0,0 +1,41 @@ +/* + * File: insertion_sort.c + * Created Time: 2022-12-29 + * Author: Listening (https://github.com/L-Super) + */ + +#include "../include/include.h" + +/* 插入排序 */ +void insertionSort(int nums[], int size) +{ + // 外循环:base = nums[1], nums[2], ..., nums[n-1] + for (int i = 1; i < size; i++) + { + int base = nums[i], j = i - 1; + // 内循环:将 base 插入到左边的正确位置 + while (j >= 0 && nums[j] > base) + { + // 1. 将 nums[j] 向右移动一位 + nums[j + 1] = nums[j]; + j--; + } + // 2. 将 base 赋值到正确位置 + nums[j + 1] = base; + } +} + +/* Driver Code */ +int main() +{ + int nums[] = {4, 1, 3, 1, 5, 2}; + insertionSort(nums, 6); + printf("插入排序完成后 nums = \n"); + for (int i = 0; i < 6; i++) + { + printf("%d ", nums[i]); + } + printf("\n"); + + return 0; +} diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index 1614b5c35..faaf38358 100644 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -135,7 +135,24 @@ comments: true === "C" ```c title="insertion_sort.c" - + /* 插入排序 */ + void insertionSort(int nums[], int size) + { + // 外循环:base = nums[1], nums[2], ..., nums[n-1] + for (int i = 1; i < size; i++) + { + int base = nums[i], j = i - 1; + // 内循环:将 base 插入到左边的正确位置 + while (j >= 0 && nums[j] > base) + { + // 1. 将 nums[j] 向右移动一位 + nums[j + 1] = nums[j]; + j--; + } + // 2. 将 base 赋值到正确位置 + nums[j + 1] = base; + } + } ``` === "C#" From d8bf0b02d163bde9b3ae90640a63fd0d66b8a1dd Mon Sep 17 00:00:00 2001 From: moonache <476681765@qq.com> Date: Fri, 30 Dec 2022 14:28:39 +0800 Subject: [PATCH 039/158] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20C#=20=E5=86=85?= =?UTF-8?q?=E7=BD=AE=E7=9A=84=E5=8F=8C=E5=90=91=E9=98=9F=E5=88=97=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RT --- docs/chapter_stack_and_queue/deque.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/chapter_stack_and_queue/deque.md b/docs/chapter_stack_and_queue/deque.md index ca55f8223..41d9f4498 100644 --- a/docs/chapter_stack_and_queue/deque.md +++ b/docs/chapter_stack_and_queue/deque.md @@ -167,5 +167,28 @@ comments: true === "C#" ```csharp title="deque.cs" + /* 初始化双向队列 */ + /* LinkedList 是 C# 中使用双向链表实现的双向队列 */ + LinkedList deque = new LinkedList(); + /* 元素入队 */ + deque.AddLast(2); // 添加至队尾 + deque.AddLast(5); + deque.AddLast(4); + deque.AddFirst(3); // 添加至队首 + deque.AddFirst(1); + + /* 访问元素 */ + int peekFirst = deque.First.Value; // 队首元素 + int peekLast = deque.Last.Value; // 队尾元素 + + /* 元素出队 */ + deque.RemoveFirst(); // 队首元素出队 + deque.RemoveLast(); // 队尾元素出队 + + /* 获取双向队列的长度 */ + int size = deque.Count; + + /* 判断双向队列是否为空 */ + bool isEmpty = deque.Count == 0; ``` From 0cf37e3f8eaeadb4c9c054828359e582e743d068 Mon Sep 17 00:00:00 2001 From: moonache <476681765@qq.com> Date: Fri, 30 Dec 2022 14:35:54 +0800 Subject: [PATCH 040/158] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20deque.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 以 C# 内置的双向队列 LinkedList 为基础,编写了 C# 版本的 deque --- codes/csharp/chapter_stack_and_queue/deque.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 codes/csharp/chapter_stack_and_queue/deque.cs diff --git a/codes/csharp/chapter_stack_and_queue/deque.cs b/codes/csharp/chapter_stack_and_queue/deque.cs new file mode 100644 index 000000000..b1360e283 --- /dev/null +++ b/codes/csharp/chapter_stack_and_queue/deque.cs @@ -0,0 +1,48 @@ +/** + * File: deque.cs + * Created Time: 2022-12-30 + * Author: moonache (microin1301@outlook.com) + */ + +using NUnit.Framework; + +namespace hello_algo.chapter_stack_and_queue +{ + public class deque + { + [Test] + public void Test() + { + /* 初始化双向队列 */ + LinkedList deque = new LinkedList(); + + /* 元素入队 */ + deque.AddLast(2); // 添加至队尾 + deque.AddLast(5); + deque.AddLast(4); + deque.AddFirst(3); // 添加至队首 + deque.AddFirst(1); + Console.WriteLine("双向队列 deque = " + String.Join(",", deque.ToArray())); + + /* 访问元素 */ + int peekFirst = deque.First.Value; // 队首元素 + Console.WriteLine("队首元素 peekFirst = " + peekFirst); + int peekLast = deque.Last.Value; // 队尾元素 + Console.WriteLine("队尾元素 peekLast = " + peekLast); + + /* 元素出队 */ + deque.RemoveFirst(); // 队首元素出队 + Console.WriteLine("队首元素出队后 deque = " + String.Join(",", deque.ToArray())); + deque.RemoveLast(); // 队尾元素出队 + Console.WriteLine("队尾元素出队后 deque = " + String.Join(",", deque.ToArray())); + + /* 获取双向队列的长度 */ + int size = deque.Count; + Console.WriteLine("双向队列长度 size = " + size); + + /* 判断双向队列是否为空 */ + bool isEmpty = deque.Count == 0; + Console.WriteLine("双向队列是否为空 = " + isEmpty); + } + } +} From 2465db1eff7d1f7298cf9ca610d2d8722428407f Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 30 Dec 2022 15:57:15 +0800 Subject: [PATCH 041/158] Update space_complexity.md Fix the Go code. --- docs/chapter_computational_complexity/space_complexity.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index df5d98f34..e96c97209 100644 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -219,11 +219,11 @@ comments: true ```go title="" func algorithm(n int) { - a := 0 // O(1) - b := make([]int, 10000) // O(1) + a := 0 // O(1) + b := make([]int, 10000) // O(1) var nums []int if n > 10 { - nums = make([]int, 10000) // O(n) + nums := make([]int, n) // O(n) } fmt.Println(a, b, nums) } From c67363a78e84148c1ae44b258d6ae3aa4bc4086a Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 30 Dec 2022 16:10:22 +0800 Subject: [PATCH 042/158] Update deque.cs --- codes/csharp/chapter_stack_and_queue/deque.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/codes/csharp/chapter_stack_and_queue/deque.cs b/codes/csharp/chapter_stack_and_queue/deque.cs index b1360e283..d9f7a5098 100644 --- a/codes/csharp/chapter_stack_and_queue/deque.cs +++ b/codes/csharp/chapter_stack_and_queue/deque.cs @@ -14,6 +14,7 @@ namespace hello_algo.chapter_stack_and_queue public void Test() { /* 初始化双向队列 */ + // 在 C# 中,将链表 LinkedList 看作双向队列来使用 LinkedList deque = new LinkedList(); /* 元素入队 */ From 56b6bf10f84d420fa70e74d995337ced04295110 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 30 Dec 2022 16:11:47 +0800 Subject: [PATCH 043/158] Update deque.md --- docs/chapter_stack_and_queue/deque.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_stack_and_queue/deque.md b/docs/chapter_stack_and_queue/deque.md index 41d9f4498..72106360f 100644 --- a/docs/chapter_stack_and_queue/deque.md +++ b/docs/chapter_stack_and_queue/deque.md @@ -168,7 +168,7 @@ comments: true ```csharp title="deque.cs" /* 初始化双向队列 */ - /* LinkedList 是 C# 中使用双向链表实现的双向队列 */ + // 在 C# 中,将链表 LinkedList 看作双向队列来使用 LinkedList deque = new LinkedList(); /* 元素入队 */ From ae78126d8055edba6df8899601830b881d00b781 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 30 Dec 2022 16:44:09 +0800 Subject: [PATCH 044/158] Update array.go --- codes/go/chapter_array_and_linkedlist/array.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/codes/go/chapter_array_and_linkedlist/array.go b/codes/go/chapter_array_and_linkedlist/array.go index 26c056763..eb21143fe 100644 --- a/codes/go/chapter_array_and_linkedlist/array.go +++ b/codes/go/chapter_array_and_linkedlist/array.go @@ -49,11 +49,7 @@ func insert(nums []int, num int, index int) { /* 删除索引 index 处元素 */ func remove(nums []int, index int) { // 把索引 index 之后的所有元素向前移动一位 - for i := index; i < len(nums); i++ { - if i+1 >= len(nums) { - nums[len(nums)-1] = 0 - break - } + for i := index; i < len(nums) - 1; i++ { nums[i] = nums[i+1] } } From e5497496f9d69ead975cb48078145d7d3c2bc956 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 30 Dec 2022 16:45:40 +0800 Subject: [PATCH 045/158] Update array.md --- docs/chapter_array_and_linkedlist/array.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 643b16a23..095960563 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -403,11 +403,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex /* 删除索引 index 处元素 */ func remove(nums []int, index int) { // 把索引 index 之后的所有元素向前移动一位 - for i := index; i < len(nums); i++ { - if i+1 >= len(nums) { - nums[len(nums)-1] = 0 - break - } + for i := index; i < len(nums) - 1; i++ { nums[i] = nums[i+1] } } @@ -721,4 +717,4 @@ elementAddr = firtstElementAddr + elementLength * elementIndex **二分查找。** 例如前文查字典的例子,我们可以将字典中的所有字按照拼音顺序存储在数组中,然后使用与日常查纸质字典相同的“翻开中间,排除一半”的方式,来实现一个查电子字典的算法。 -**深度学习。** 神经网络中大量使用了向量、矩阵、张量之间的线性代数运算,这些数据都是以数组的形式构建的。数组是神经网络编程中最常使用的数据结构。 \ No newline at end of file +**深度学习。** 神经网络中大量使用了向量、矩阵、张量之间的线性代数运算,这些数据都是以数组的形式构建的。数组是神经网络编程中最常使用的数据结构。 From 507f07ac4b8dace4219793ad22debb6e079b6384 Mon Sep 17 00:00:00 2001 From: Zhizhen He Date: Fri, 30 Dec 2022 18:37:12 +0800 Subject: [PATCH 046/158] fix typo --- .../space_complexity.md | 2 +- .../time_complexity.md | 2 +- .../classification_logic_structure.png | Bin .../classification_phisical_structure.png | Bin ...uture.md => classification_of_data_structure.md} | 4 ++-- mkdocs.yml | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename docs/chapter_data_structure/{classification_of_data_strcuture.assets => classification_of_data_structure.assets}/classification_logic_structure.png (100%) rename docs/chapter_data_structure/{classification_of_data_strcuture.assets => classification_of_data_structure.assets}/classification_phisical_structure.png (100%) rename docs/chapter_data_structure/{classification_of_data_strcuture.md => classification_of_data_structure.md} (93%) diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index e96c97209..4c0d2d1bf 100644 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -838,7 +838,7 @@ $$ ``` -在以下递归函数中,同时存在 $n$ 个未返回的 `algorihtm()` ,并且每个函数中都初始化了一个数组,长度分别为 $n, n-1, n-2, ..., 2, 1$ ,平均长度为 $\frac{n}{2}$ ,因此总体使用 $O(n^2)$ 空间。 +在以下递归函数中,同时存在 $n$ 个未返回的 `algorithm()` ,并且每个函数中都初始化了一个数组,长度分别为 $n, n-1, n-2, ..., 2, 1$ ,平均长度为 $\frac{n}{2}$ ,因此总体使用 $O(n^2)$ 空间。 === "Java" diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index aafb0d893..539c9b5cb 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -1442,7 +1442,7 @@ $$ ### 对数阶 $O(\log n)$ -对数阶与指数阶正好相反,后者反映“每轮增加到两倍的情况”,而前者反映“每轮缩减到一半的情况”。对数阶仅次于常数阶,时间增长的很慢,是理想的时间复杂度。 +对数阶与指数阶正好相反,后者反映“每轮增加到两倍的情况”,而前者反映“每轮缩减到一半的情况”。对数阶仅次于常数阶,时间增长得很慢,是理想的时间复杂度。 对数阶常出现于「二分查找」和「分治算法」中,体现“一分为多”、“化繁为简”的算法思想。 diff --git a/docs/chapter_data_structure/classification_of_data_strcuture.assets/classification_logic_structure.png b/docs/chapter_data_structure/classification_of_data_structure.assets/classification_logic_structure.png similarity index 100% rename from docs/chapter_data_structure/classification_of_data_strcuture.assets/classification_logic_structure.png rename to docs/chapter_data_structure/classification_of_data_structure.assets/classification_logic_structure.png diff --git a/docs/chapter_data_structure/classification_of_data_strcuture.assets/classification_phisical_structure.png b/docs/chapter_data_structure/classification_of_data_structure.assets/classification_phisical_structure.png similarity index 100% rename from docs/chapter_data_structure/classification_of_data_strcuture.assets/classification_phisical_structure.png rename to docs/chapter_data_structure/classification_of_data_structure.assets/classification_phisical_structure.png diff --git a/docs/chapter_data_structure/classification_of_data_strcuture.md b/docs/chapter_data_structure/classification_of_data_structure.md similarity index 93% rename from docs/chapter_data_structure/classification_of_data_strcuture.md rename to docs/chapter_data_structure/classification_of_data_structure.md index b41266bb8..d4397c5da 100644 --- a/docs/chapter_data_structure/classification_of_data_strcuture.md +++ b/docs/chapter_data_structure/classification_of_data_structure.md @@ -15,7 +15,7 @@ comments: true - **线性数据结构:** 数组、链表、栈、队列、哈希表; - **非线性数据结构:** 树、图、堆、哈希表; -![classification_logic_structure](classification_of_data_strcuture.assets/classification_logic_structure.png) +![classification_logic_structure](classification_of_data_structure.assets/classification_logic_structure.png)

Fig. 线性与非线性数据结构

@@ -27,7 +27,7 @@ comments: true **「物理结构」反映了数据在计算机内存中的存储方式。** 从本质上看,分别是 **数组的连续空间存储** 和 **链表的离散空间存储** 。物理结构从底层上决定了数据的访问、更新、增删等操作方法,在时间效率和空间效率方面呈现出此消彼长的特性。 -![classification_phisical_structure](classification_of_data_strcuture.assets/classification_phisical_structure.png) +![classification_phisical_structure](classification_of_data_structure.assets/classification_phisical_structure.png)

Fig. 连续空间存储与离散空间存储

diff --git a/mkdocs.yml b/mkdocs.yml index 4334382f4..4aca74c24 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -139,7 +139,7 @@ nav: - 小结: chapter_computational_complexity/summary.md - 数据结构简介: - 数据与内存: chapter_data_structure/data_and_memory.md - - 数据结构分类: chapter_data_structure/classification_of_data_strcuture.md + - 数据结构分类: chapter_data_structure/classification_of_data_structure.md - 小结: chapter_data_structure/summary.md - 数组与链表: - 数组(Array): chapter_array_and_linkedlist/array.md From a03353f8e2bbde2583bbfe65f8e0f9c1c04ec4ee Mon Sep 17 00:00:00 2001 From: GN-Yu <58758623+GN-Yu@users.noreply.github.com> Date: Fri, 30 Dec 2022 13:20:25 -0500 Subject: [PATCH 047/158] Update merge_sort.cpp --- codes/cpp/chapter_sorting/merge_sort.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codes/cpp/chapter_sorting/merge_sort.cpp b/codes/cpp/chapter_sorting/merge_sort.cpp index 7c9a41c8e..b406529a3 100644 --- a/codes/cpp/chapter_sorting/merge_sort.cpp +++ b/codes/cpp/chapter_sorting/merge_sort.cpp @@ -25,10 +25,10 @@ void merge(vector& nums, int left, int mid, int right) { // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) nums[k] = tmp[j++]; - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ else if (j > rightEnd || tmp[i] <= tmp[j]) nums[k] = tmp[i++]; - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ else nums[k] = tmp[j++]; } From 1b71e74baac5f79073a7179d509b78a42962a59a Mon Sep 17 00:00:00 2001 From: GN-Yu <58758623+GN-Yu@users.noreply.github.com> Date: Fri, 30 Dec 2022 13:21:03 -0500 Subject: [PATCH 048/158] Update merge_sort.cs --- codes/csharp/chapter_sorting/merge_sort.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codes/csharp/chapter_sorting/merge_sort.cs b/codes/csharp/chapter_sorting/merge_sort.cs index 04ac8acdd..e66ae5e23 100644 --- a/codes/csharp/chapter_sorting/merge_sort.cs +++ b/codes/csharp/chapter_sorting/merge_sort.cs @@ -31,10 +31,10 @@ namespace hello_algo.chapter_sorting // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) nums[k] = tmp[j++]; - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ else if (j > rightEnd || tmp[i] <= tmp[j]) nums[k] = tmp[i++]; - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ else nums[k] = tmp[j++]; } From 5d45f7116716be00be72ae3a611b1a0597319254 Mon Sep 17 00:00:00 2001 From: GN-Yu <58758623+GN-Yu@users.noreply.github.com> Date: Fri, 30 Dec 2022 13:21:40 -0500 Subject: [PATCH 049/158] Update merge_sort.go --- codes/go/chapter_sorting/merge_sort/merge_sort.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codes/go/chapter_sorting/merge_sort/merge_sort.go b/codes/go/chapter_sorting/merge_sort/merge_sort.go index 830e2dfa4..f2910600a 100644 --- a/codes/go/chapter_sorting/merge_sort/merge_sort.go +++ b/codes/go/chapter_sorting/merge_sort/merge_sort.go @@ -25,11 +25,11 @@ func merge(nums []int, left, mid, right int) { if i > left_end { nums[k] = tmp[j] j++ - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ } else if j > right_end || tmp[i] <= tmp[j] { nums[k] = tmp[i] i++ - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ } else { nums[k] = tmp[j] j++ From 6ba808ed3631c01e320ebe24e4fbe78c43f58eaf Mon Sep 17 00:00:00 2001 From: GN-Yu <58758623+GN-Yu@users.noreply.github.com> Date: Fri, 30 Dec 2022 13:22:07 -0500 Subject: [PATCH 050/158] Update merge_sort.java --- codes/java/chapter_sorting/merge_sort.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codes/java/chapter_sorting/merge_sort.java b/codes/java/chapter_sorting/merge_sort.java index 498a6fd81..fe9f183a5 100644 --- a/codes/java/chapter_sorting/merge_sort.java +++ b/codes/java/chapter_sorting/merge_sort.java @@ -28,10 +28,10 @@ public class merge_sort { // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) nums[k] = tmp[j++]; - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ else if (j > rightEnd || tmp[i] <= tmp[j]) nums[k] = tmp[i++]; - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ else nums[k] = tmp[j++]; } From 96355afb1ce236fb19dfda07a96ebe865a766734 Mon Sep 17 00:00:00 2001 From: GN-Yu <58758623+GN-Yu@users.noreply.github.com> Date: Fri, 30 Dec 2022 13:22:31 -0500 Subject: [PATCH 051/158] Update merge_sort.js --- codes/javascript/chapter_sorting/merge_sort.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codes/javascript/chapter_sorting/merge_sort.js b/codes/javascript/chapter_sorting/merge_sort.js index 31860ce1d..b00c17bcd 100644 --- a/codes/javascript/chapter_sorting/merge_sort.js +++ b/codes/javascript/chapter_sorting/merge_sort.js @@ -23,10 +23,10 @@ function merge(nums, left, mid, right) { // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) { nums[k] = tmp[j++]; - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ } else if (j > rightEnd || tmp[i] <= tmp[j]) { nums[k] = tmp[i++]; - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ } else { nums[k] = tmp[j++]; } From 506bc009c7c9d51f97fbfb617aa8d303093f2d08 Mon Sep 17 00:00:00 2001 From: GN-Yu <58758623+GN-Yu@users.noreply.github.com> Date: Fri, 30 Dec 2022 13:22:59 -0500 Subject: [PATCH 052/158] Update merge_sort.py --- codes/python/chapter_sorting/merge_sort.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codes/python/chapter_sorting/merge_sort.py b/codes/python/chapter_sorting/merge_sort.py index f3fb538ae..5fa7d83a9 100644 --- a/codes/python/chapter_sorting/merge_sort.py +++ b/codes/python/chapter_sorting/merge_sort.py @@ -28,11 +28,11 @@ def merge(nums, left, mid, right): if i > left_end: nums[k] = tmp[j] j += 1 - # 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + # 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ elif j > right_end or tmp[i] <= tmp[j]: nums[k] = tmp[i] i += 1 - # 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + # 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ else: nums[k] = tmp[j] j += 1 From 327b566ff936db1114be40a928cc1e6b11632c05 Mon Sep 17 00:00:00 2001 From: GN-Yu <58758623+GN-Yu@users.noreply.github.com> Date: Fri, 30 Dec 2022 13:23:26 -0500 Subject: [PATCH 053/158] Update merge_sort.ts --- codes/typescript/chapter_sorting/merge_sort.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codes/typescript/chapter_sorting/merge_sort.ts b/codes/typescript/chapter_sorting/merge_sort.ts index 44fb4b8d0..a4cb5e1cb 100644 --- a/codes/typescript/chapter_sorting/merge_sort.ts +++ b/codes/typescript/chapter_sorting/merge_sort.ts @@ -23,10 +23,10 @@ function merge(nums: number[], left: number, mid: number, right: number): void { // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ if (i > leftEnd) { nums[k] = tmp[j++]; - // 否则,若“右子数组已全部合并完”或“左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ } else if (j > rightEnd || tmp[i] <= tmp[j]) { nums[k] = tmp[i++]; - // 否则,若“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ } else { nums[k] = tmp[j++]; } From c94101a365a7b22f9457c9d18dccd29290adbc1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Wang?= <52880665+RiverTwilight@users.noreply.github.com> Date: Sun, 1 Jan 2023 19:29:45 +0800 Subject: [PATCH 054/158] docs: added Typescript and Javascript examples Not sure whether these formats meet the requirement. If everything is okay I will continue to transcribe more:-) --- .../time_complexity.md | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 539c9b5cb..75f2dd31e 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -79,13 +79,27 @@ $$ === "JavaScript" ```js title="" - + function algorithm(n) { + var a = 2; // 1 ns + a = a + 1; // 1 ns + a = a * 2; // 10 ns + for(var i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ + console.log(0); // 5 ns + } + } ``` === "TypeScript" ```typescript title="" - + function algorithm(n: number): void { + var a: number = 2; // 1 ns + a = a + 1; // 1 ns + a = a * 2; // 10 ns + for(var i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ + console.log(0); // 5 ns + } + } ``` === "C" @@ -220,13 +234,44 @@ $$ === "JavaScript" ```js title="" + // 算法 A 时间复杂度:常数阶 + function algorithm_A(n) { + console.log(0); + } + // 算法 B 时间复杂度:线性阶 + function algorithm_B(n) { + for (var i = 0; i < n; i++) { + console.log(0); + } + } + // 算法 C 时间复杂度:常数阶 + function algorithm_C(n) { + for (var i = 0; i < 1000000; i++) { + console.log(0); + } + } ``` === "TypeScript" ```typescript title="" - + // 算法 A 时间复杂度:常数阶 + function algorithm_A(n: number): void { + console.log(0); + } + // 算法 B 时间复杂度:线性阶 + function algorithm_B(n: number): void { + for (var i = 0; i < n; i++) { + console.log(0); + } + } + // 算法 C 时间复杂度:常数阶 + function algorithm_C(n: number): void { + for (var i = 0; i < 1000000; i++) { + console.log(0); + } + } ``` === "C" From 57bd711779ab91eda6705180b15a12ce634cc90f Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Sun, 1 Jan 2023 21:29:45 +0800 Subject: [PATCH 055/158] feat: add Swift codes for space complexity article --- .../space_complexity.swift | 172 ++++++++++++++++++ .../space_complexity.md | 152 ++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100644 codes/swift/chapter_computational_complexity/space_complexity.swift diff --git a/codes/swift/chapter_computational_complexity/space_complexity.swift b/codes/swift/chapter_computational_complexity/space_complexity.swift new file mode 100644 index 000000000..7326c82b4 --- /dev/null +++ b/codes/swift/chapter_computational_complexity/space_complexity.swift @@ -0,0 +1,172 @@ +/* + * File: space_complexity.swift + * Created Time: 2023-01-01 + * Author: nuomi1 (nuomi1@qq.com) + */ + +class ListNode { + var val: Int + var next: ListNode? + + init(x: Int) { + val = x + } +} + +class TreeNode { + var val: Int // 结点值 + var height: Int // 结点高度 + var left: TreeNode? // 左子结点引用 + var right: TreeNode? // 右子结点引用 + + init(x: Int) { + val = x + height = 0 + } +} + +enum PrintUtil { + private class Trunk { + var prev: Trunk? + var str: String + + init(prev: Trunk?, str: String) { + self.prev = prev + self.str = str + } + } + + static func printTree(root: TreeNode?) { + printTree(root: root, prev: nil, isLeft: false) + } + + private static func printTree(root: TreeNode?, prev: Trunk?, isLeft: Bool) { + if root == nil { + return + } + + var prevStr = " " + let trunk = Trunk(prev: prev, str: prevStr) + + printTree(root: root?.right, prev: trunk, isLeft: true) + + if prev == nil { + trunk.str = "———" + } else if isLeft { + trunk.str = "/———" + prevStr = " |" + } else { + trunk.str = "\\———" + prev?.str = prevStr + } + + showTrunks(p: trunk) + print(" \(root!.val)") + + if prev != nil { + prev?.str = prevStr + } + trunk.str = " |" + + printTree(root: root?.left, prev: trunk, isLeft: false) + } + + private static func showTrunks(p: Trunk?) { + if p == nil { + return + } + + showTrunks(p: p?.prev) + print(p!.str, terminator: "") + } +} + +// 函数 +@discardableResult +func function() -> Int { + // do something + return 0 +} + +// 常数阶 +func constant(n: Int) { + // 常量、变量、对象占用 O(1) 空间 + let a = 0 + var b = 0 + let nums = Array(repeating: 0, count: 10000) + let node = ListNode(x: 0) + // 循环中的变量占用 O(1) 空间 + for _ in 0 ..< n { + let c = 0 + } + // 循环中的函数占用 O(1) 空间 + for _ in 0 ..< n { + function() + } +} + +// 线性阶 +func linear(n: Int) { + // 长度为 n 的数组占用 O(n) 空间 + let nums = Array(repeating: 0, count: n) + // 长度为 n 的列表占用 O(n) 空间 + let nodes = (0 ..< n).map { ListNode(x: $0) } + // 长度为 n 的哈希表占用 O(n) 空间 + let map = Dictionary(uniqueKeysWithValues: (0 ..< n).map { ($0, "\($0)") }) +} + +// 线性阶(递归实现) +func linearRecur(n: Int) { + print("递归 n = \(n)") + if n == 1 { + return + } + linearRecur(n: n - 1) +} + +// 平方阶 +func quadratic(n: Int) { + // 二维列表占用 O(n^2) 空间 + let numList = Array(repeating: Array(repeating: 0, count: n), count: n) +} + +// 平方阶(递归实现) +@discardableResult +func quadraticRecur(n: Int) -> Int { + if n <= 0 { + return 0 + } + // 数组 nums 长度为 n, n-1, ..., 2, 1 + let nums = Array(repeating: 0, count: n) + print("递归 n = \(n) 中的 nums 长度 = \(nums.count)") + return quadraticRecur(n: n - 1) +} + +// 指数阶(建立满二叉树) +func buildTree(n: Int) -> TreeNode? { + if n == 0 { + return nil + } + let root = TreeNode(x: 0) + root.left = buildTree(n: n - 1) + root.right = buildTree(n: n - 1) + return root +} + +// Driver Code +func main() { + let n = 5 + // 常数阶 + constant(n: n) + // 线性阶 + linear(n: n) + linearRecur(n: n) + // 平方阶 + quadratic(n: n) + quadraticRecur(n: n) + // 指数阶 + let root = buildTree(n: n) + PrintUtil.printTree(root: root) +} + +main() diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index 4c0d2d1bf..1d561792b 100644 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -174,6 +174,34 @@ comments: true } ``` +=== "Swift" + + ```swift title="" + // 类 + class Node { + var val: Int + var next: Node? + + init(x: Int) { + val = x + } + } + + // 函数 + func function() -> Int { + // do something... + return 0 + } + + func algorithm(n: Int) -> Int { // 输入数据 + let a = 0 // 暂存数据(常量) + var b = 0 // 暂存数据(变量) + let node = Node(x: 0) // 暂存数据(对象) + let c = function() // 栈帧空间(调用函数) + return a + b + c // 输出数据 + } + ``` + ## 推算方法 空间复杂度的推算方法和时间复杂度总体类似,只是从统计“计算操作数量”变为统计“使用空间大小”。与时间复杂度不同的是,**我们一般只关注「最差空间复杂度」**。这是因为内存空间是一个硬性要求,我们必须保证在所有输入数据下都有足够的内存空间预留。 @@ -261,6 +289,18 @@ comments: true } ``` +=== "Swift" + + ```swift title="" + func algorithm(n: Int) { + let a = 0 // O(1) + let b = Array(repeating: 0, count: 10000) // O(1) + if n > 10 { + let nums = Array(repeating: 0, count: n) // O(n) + } + } + ``` + **在递归函数中,需要注意统计栈帧空间。** 例如函数 `loop()`,在循环中调用了 $n$ 次 `function()` ,每轮中的 `function()` 都返回并释放了栈帧空间,因此空间复杂度仍为 $O(1)$ 。而递归函数 `recur()` 在运行中会同时存在 $n$ 个未返回的 `recur()` ,从而使用 $O(n)$ 的栈帧空间。 === "Java" @@ -387,6 +427,31 @@ comments: true } ``` +=== "Swift" + + ```swift title="" + @discardableResult + func function() -> Int { + // do something + return 0 + } + + // 循环 O(1) + func loop(n: Int) { + for _ in 0 ..< n { + function() + } + } + + // 递归 O(n) + func recur(n: Int) { + if n == 1 { + return + } + recur(n: n - 1) + } + ``` + ## 常见类型 设输入数据大小为 $n$ ,常见的空间复杂度类型有(从低到高排列) @@ -536,6 +601,27 @@ $$ } ``` +=== "Swift" + + ```swift title="space_complexity.swift" + // 常数阶 + func constant(n: Int) { + // 常量、变量、对象占用 O(1) 空间 + let a = 0 + var b = 0 + let nums = Array(repeating: 0, count: 10000) + let node = ListNode(x: 0) + // 循环中的变量占用 O(1) 空间 + for _ in 0 ..< n { + let c = 0 + } + // 循环中的函数占用 O(1) 空间 + for _ in 0 ..< n { + function() + } + } + ``` + ### 线性阶 $O(n)$ 线性阶常见于元素数量与 $n$ 成正比的数组、链表、栈、队列等。 @@ -654,6 +740,20 @@ $$ } ``` +=== "Swift" + + ```swift title="space_complexity.swift" + // 线性阶 + func linear(n: Int) { + // 长度为 n 的数组占用 O(n) 空间 + let nums = Array(repeating: 0, count: n) + // 长度为 n 的列表占用 O(n) 空间 + let nodes = (0 ..< n).map { ListNode(x: $0) } + // 长度为 n 的哈希表占用 O(n) 空间 + let map = Dictionary(uniqueKeysWithValues: (0 ..< n).map { ($0, "\($0)") }) + } + ``` + 以下递归函数会同时存在 $n$ 个未返回的 `algorithm()` 函数,使用 $O(n)$ 大小的栈帧空间。 === "Java" @@ -731,6 +831,19 @@ $$ } ``` +=== "Swift" + + ```swift title="space_complexity.swift" + // 线性阶(递归实现) + func linearRecur(n: Int) { + print("递归 n = \(n)") + if n == 1 { + return + } + linearRecur(n: n - 1) + } + ``` + ![space_complexity_recursive_linear](space_complexity.assets/space_complexity_recursive_linear.png)

Fig. 递归函数产生的线性阶空间复杂度

@@ -838,6 +951,16 @@ $$ ``` +=== "Swift" + + ```swift title="space_complexity.swift" + // 平方阶 + func quadratic(n: Int) { + // 二维列表占用 O(n^2) 空间 + let numList = Array(repeating: Array(repeating: 0, count: n), count: n) + } + ``` + 在以下递归函数中,同时存在 $n$ 个未返回的 `algorithm()` ,并且每个函数中都初始化了一个数组,长度分别为 $n, n-1, n-2, ..., 2, 1$ ,平均长度为 $\frac{n}{2}$ ,因此总体使用 $O(n^2)$ 空间。 === "Java" @@ -921,6 +1044,20 @@ $$ ``` +=== "Swift" + + ```swift title="space_complexity.swift" + // 平方阶(递归实现) + func quadraticRecur(n: Int) -> Int { + if n <= 0 { + return 0 + } + // 数组 nums 长度为 n, n-1, ..., 2, 1 + let nums = Array(repeating: 0, count: n) + return quadraticRecur(n: n - 1) + } + ``` + ![space_complexity_recursive_quadratic](space_complexity.assets/space_complexity_recursive_quadratic.png)

Fig. 递归函数产生的平方阶空间复杂度

@@ -1014,6 +1151,21 @@ $$ } ``` +=== "Swift" + + ```swift title="space_complexity.swift" + // 指数阶(建立满二叉树) + func buildTree(n: Int) -> TreeNode? { + if n == 0 { + return nil + } + let root = TreeNode(x: 0) + root.left = buildTree(n: n - 1) + root.right = buildTree(n: n - 1) + return root + } + ``` + ![space_complexity_exponential](space_complexity.assets/space_complexity_exponential.png)

Fig. 满二叉树下的指数阶空间复杂度

From 679d5314d9df578d0555fba67b28bb3983f3c999 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 2 Jan 2023 18:41:21 +0800 Subject: [PATCH 056/158] Update linked_list.md --- .../linked_list.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index 6627fb930..a55d68028 100644 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -174,19 +174,19 @@ comments: true === "Go" ```go title="" - /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ - // 初始化各个结点 - n0 := NewListNode(1) - n1 := NewListNode(3) - n2 := NewListNode(2) - n3 := NewListNode(5) - n4 := NewListNode(4) - - // 构建引用指向 - n0.Next = n1 - n1.Next = n2 - n2.Next = n3 - n3.Next = n4 + /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ + // 初始化各个结点 + n0 := NewListNode(1) + n1 := NewListNode(3) + n2 := NewListNode(2) + n3 := NewListNode(5) + n4 := NewListNode(4) + + // 构建引用指向 + n0.Next = n1 + n1.Next = n2 + n2.Next = n3 + n3.Next = n4 ``` === "JavaScript" From bacf8553c5527d9f14c4093aa789c8f6c900ebdc Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 2 Jan 2023 18:42:58 +0800 Subject: [PATCH 057/158] Update linked_list.go --- codes/go/chapter_array_and_linkedlist/linked_list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codes/go/chapter_array_and_linkedlist/linked_list.go b/codes/go/chapter_array_and_linkedlist/linked_list.go index e7fdb35ca..106e49f4c 100644 --- a/codes/go/chapter_array_and_linkedlist/linked_list.go +++ b/codes/go/chapter_array_and_linkedlist/linked_list.go @@ -5,7 +5,7 @@ package chapter_array_and_linkedlist import ( - "github.com/krahets/hello-algo/pkg" + . "github.com/krahets/hello-algo/pkg" ) /* 在链表的结点 n0 之后插入结点 P */ From 3f00aa39fb69198901728cceec66fcfe69e57850 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 18:47:36 +0800 Subject: [PATCH 058/158] code: added code for time complexity chapter --- .../time_complexity.js | 21 +++++++++++++ .../time_complexity.ts | 22 ++++++++++++++ .../time_complexity.md | 30 +++++++++---------- 3 files changed, 58 insertions(+), 15 deletions(-) create mode 100644 codes/javascript/chapter_computational_complexity/time_complexity.js create mode 100644 codes/typescript/chapter_computational_complexity/time_complexity.ts diff --git a/codes/javascript/chapter_computational_complexity/time_complexity.js b/codes/javascript/chapter_computational_complexity/time_complexity.js new file mode 100644 index 000000000..1ce3ffbf0 --- /dev/null +++ b/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -0,0 +1,21 @@ +/** + * File: time_complexity.js + * Created Time: 2023-01-02 + * Author: RiverTwilight (contact@rene.wang) + */ + +function algorithm_A(n) { + console.log(0); +} +// 算法 B 时间复杂度:线性阶 +function algorithm_B(n) { + for (var i = 0; i < n; i++) { + console.log(0); + } +} +// 算法 C 时间复杂度:常数阶 +function algorithm_C(n) { + for (var i = 0; i < 1000000; i++) { + console.log(0); + } +} diff --git a/codes/typescript/chapter_computational_complexity/time_complexity.ts b/codes/typescript/chapter_computational_complexity/time_complexity.ts new file mode 100644 index 000000000..cc183265b --- /dev/null +++ b/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -0,0 +1,22 @@ +/** + * File: time_complexity.ts + * Created Time: 2023-01-02 + * Author: RiverTwilight (contact@rene.wang) + */ + +// 算法 A 时间复杂度:常数阶 +function algorithm_A(n: number): void { + console.log(0); +} +// 算法 B 时间复杂度:线性阶 +function algorithm_B(n: number): void { + for (var i = 0; i < n; i++) { + console.log(0); + } +} +// 算法 C 时间复杂度:常数阶 +function algorithm_C(n: number): void { + for (var i = 0; i < 1000000; i++) { + console.log(0); + } +} diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 75f2dd31e..dd82b6a2a 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -148,9 +148,9 @@ $$ “时间增长趋势”这个概念比较抽象,我们借助一个例子来理解。设输入数据大小为 $n$ ,给定三个算法 `A` , `B` , `C` 。 -- 算法 `A` 只有 $1$ 个打印操作,算法运行时间不随着 $n$ 增大而增长。我们称此算法的时间复杂度为「常数阶」。 -- 算法 `B` 中的打印操作需要循环 $n$ 次,算法运行时间随着 $n$ 增大成线性增长。此算法的时间复杂度被称为「线性阶」。 -- 算法 `C` 中的打印操作需要循环 $1000000$ 次,但运行时间仍与输入数据大小 $n$ 无关。因此 `C` 的时间复杂度和 `A` 相同,仍为「常数阶」。 +- 算法 `A` 只有 $1$ 个打印操作,算法运行时间不随着 $n$ 增大而增长。我们称此算法的时间复杂度为「常数阶」。 +- 算法 `B` 中的打印操作需要循环 $n$ 次,算法运行时间随着 $n$ 增大成线性增长。此算法的时间复杂度被称为「线性阶」。 +- 算法 `C` 中的打印操作需要循环 $1000000$ 次,但运行时间仍与输入数据大小 $n$ 无关。因此 `C` 的时间复杂度和 `A` 相同,仍为「常数阶」。 === "Java" @@ -233,7 +233,7 @@ $$ === "JavaScript" - ```js title="" + ```js title="time_complexity.js" // 算法 A 时间复杂度:常数阶 function algorithm_A(n) { console.log(0); @@ -255,7 +255,7 @@ $$ === "TypeScript" - ```typescript title="" + ```typescript title="time_complexity.ts" // 算法 A 时间复杂度:常数阶 function algorithm_A(n: number): void { console.log(0); @@ -343,7 +343,7 @@ $$ ## 函数渐近上界 -设算法「计算操作数量」为 $T(n)$ ,其是一个关于输入数据大小 $n$ 的函数。例如,以下算法的操作数量为 +设算法「计算操作数量」为 $T(n)$ ,其是一个关于输入数据大小 $n$ 的函数。例如,以下算法的操作数量为 $$ T(n) = 3 + 2n @@ -399,7 +399,7 @@ $$ // 循环 n 次 for i := 0; i < n; i++ { // +1 fmt.Println(a) // +1 - } + } } ``` @@ -640,7 +640,7 @@ $$
-| 操作数量 $T(n)$ | 时间复杂度 $O(f(n))$ | +| 操作数量 $T(n)$ | 时间复杂度 $O(f(n))$ | | ---------------------- | -------------------- | | $100000$ | $O(1)$ | | $3n + 2$ | $O(n)$ | @@ -1696,7 +1696,7 @@ $$ /* 线性对数阶 */ int linearLogRecur(float n) { if (n <= 1) return 1; - int count = linearLogRecur(n / 2) + + int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); for (int i = 0; i < n; i++) { count++; @@ -1711,7 +1711,7 @@ $$ /* 线性对数阶 */ int linearLogRecur(float n) { if (n <= 1) return 1; - int count = linearLogRecur(n / 2) + + int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); for (int i = 0; i < n; i++) { count++; @@ -1935,8 +1935,8 @@ $$ **某些算法的时间复杂度不是恒定的,而是与输入数据的分布有关。** 举一个例子,输入一个长度为 $n$ 数组 `nums` ,其中 `nums` 由从 $1$ 至 $n$ 的数字组成,但元素顺序是随机打乱的;算法的任务是返回元素 $1$ 的索引。我们可以得出以下结论: -- 当 `nums = [?, ?, ..., 1]`,即当末尾元素是 $1$ 时,则需完整遍历数组,此时达到 **最差时间复杂度 $O(n)$** ; -- 当 `nums = [1, ?, ?, ...]` ,即当首个数字为 $1$ 时,无论数组多长都不需要继续遍历,此时达到 **最佳时间复杂度 $\Omega(1)$** ; +- 当 `nums = [?, ?, ..., 1]`,即当末尾元素是 $1$ 时,则需完整遍历数组,此时达到 **最差时间复杂度 $O(n)$** ; +- 当 `nums = [1, ?, ?, ...]` ,即当首个数字为 $1$ 时,无论数组多长都不需要继续遍历,此时达到 **最佳时间复杂度 $\Omega(1)$** ; 「函数渐近上界」使用大 $O$ 记号表示,代表「最差时间复杂度」。与之对应,「函数渐近下界」用 $\Omega$ 记号(Omega Notation)来表示,代表「最佳时间复杂度」。 @@ -1960,7 +1960,7 @@ $$ } return res; } - + /* 查找数组 nums 中数字 1 所在索引 */ int findOne(int[] nums) { for (int i = 0; i < nums.length; i++) { @@ -1969,7 +1969,7 @@ $$ } return -1; } - + /* Driver Code */ public void main(String[] args) { for (int i = 0; i < 10; i++) { @@ -2029,7 +2029,7 @@ $$ ```python title="worst_best_time_complexity.py" """ 生成一个数组,元素为: 1, 2, ..., n ,顺序被打乱 """ def random_numbers(n): - # 生成数组 nums =: 1, 2, 3, ..., n + # 生成数组 nums =: 1, 2, 3, ..., n nums = [i for i in range(1, n + 1)] # 随机打乱数组元素 random.shuffle(nums) From 2bd24e61a8ddb17da40b46341b3aa2ae2b52f799 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 18:57:26 +0800 Subject: [PATCH 059/158] code: added doc code --- .../time_complexity.js | 16 ------ .../time_complexity.ts | 17 ------ .../time_complexity.md | 52 ++++++++++++++++++- 3 files changed, 50 insertions(+), 35 deletions(-) diff --git a/codes/javascript/chapter_computational_complexity/time_complexity.js b/codes/javascript/chapter_computational_complexity/time_complexity.js index 1ce3ffbf0..9c877602f 100644 --- a/codes/javascript/chapter_computational_complexity/time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -3,19 +3,3 @@ * Created Time: 2023-01-02 * Author: RiverTwilight (contact@rene.wang) */ - -function algorithm_A(n) { - console.log(0); -} -// 算法 B 时间复杂度:线性阶 -function algorithm_B(n) { - for (var i = 0; i < n; i++) { - console.log(0); - } -} -// 算法 C 时间复杂度:常数阶 -function algorithm_C(n) { - for (var i = 0; i < 1000000; i++) { - console.log(0); - } -} diff --git a/codes/typescript/chapter_computational_complexity/time_complexity.ts b/codes/typescript/chapter_computational_complexity/time_complexity.ts index cc183265b..6f0c9737c 100644 --- a/codes/typescript/chapter_computational_complexity/time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -3,20 +3,3 @@ * Created Time: 2023-01-02 * Author: RiverTwilight (contact@rene.wang) */ - -// 算法 A 时间复杂度:常数阶 -function algorithm_A(n: number): void { - console.log(0); -} -// 算法 B 时间复杂度:线性阶 -function algorithm_B(n: number): void { - for (var i = 0; i < n; i++) { - console.log(0); - } -} -// 算法 C 时间复杂度:常数阶 -function algorithm_C(n: number): void { - for (var i = 0; i < 1000000; i++) { - console.log(0); - } -} diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index dd82b6a2a..8c2edb08a 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -233,7 +233,7 @@ $$ === "JavaScript" - ```js title="time_complexity.js" + ```js title="" // 算法 A 时间复杂度:常数阶 function algorithm_A(n) { console.log(0); @@ -255,7 +255,7 @@ $$ === "TypeScript" - ```typescript title="time_complexity.ts" + ```typescript title="" // 算法 A 时间复杂度:常数阶 function algorithm_A(n: number): void { console.log(0); @@ -406,12 +406,32 @@ $$ === "JavaScript" ```js title="" + function algorithm(n){ + var a = 1; // +1 + a += 1; // +1 + a *= 2; // +1 + // 循环 n 次 + for(var i = 0; i < n; i++){ // +1 + console.log(0) // +1 + } + + } ``` === "TypeScript" ```typescript title="" + function algorithm(n: number): void{ + var a: number = 1; // +1 + a += 1; // +1 + a *= 2; // +1 + // 循环 n 次 + for(var i = 0; i < n; i++){ // +1 + console.log(0) // +1 + } + + } ``` @@ -575,12 +595,40 @@ $$ === "JavaScript" ```js title="" + function algorithm(n) { + var a = 1; // +0(技巧 1) + a = a + n; // +0(技巧 1) + // +n(技巧 2) + for (var i = 0; i < 5 * n + 1; i++) { + console.log(0); + } + // +n*n(技巧 3) + for (var i = 0; i < 2 * n; i++) { + for (var j = 0; j < n + 1; j++) { + console.log(0); + } + } + } ``` === "TypeScript" ```typescript title="" + function algorithm(n: number): void { + var a: number = 1; // +0(技巧 1) + a = a + n; // +0(技巧 1) + // +n(技巧 2) + for (var i = 0; i < 5 * n + 1; i++) { + console.log(0); + } + // +n*n(技巧 3) + for (var i = 0; i < 2 * n; i++) { + for (var j = 0; j < n + 1; j++) { + console.log(0); + } + } + } ``` From 1f1c58519deb942c51ff53e086344060c8783380 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 2 Jan 2023 18:59:35 +0800 Subject: [PATCH 060/158] Update the array and linked list (Go code). --- .../go/chapter_array_and_linkedlist/array.go | 6 +++--- .../linked_list.go | 8 +++---- .../linked_list_test.go | 21 ++++++++++--------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/codes/go/chapter_array_and_linkedlist/array.go b/codes/go/chapter_array_and_linkedlist/array.go index 1f1a78be1..578bcb349 100644 --- a/codes/go/chapter_array_and_linkedlist/array.go +++ b/codes/go/chapter_array_and_linkedlist/array.go @@ -5,7 +5,6 @@ package chapter_array_and_linkedlist import ( - "fmt" "math/rand" ) @@ -60,9 +59,10 @@ func traverse(nums []int) { for i := 0; i < len(nums); i++ { count++ } + count = 0 // 直接遍历数组 - for index, val := range nums { - fmt.Printf("index:%v value:%v\n", index, val) + for range nums { + count++ } } diff --git a/codes/go/chapter_array_and_linkedlist/linked_list.go b/codes/go/chapter_array_and_linkedlist/linked_list.go index 106e49f4c..fa4538ab4 100644 --- a/codes/go/chapter_array_and_linkedlist/linked_list.go +++ b/codes/go/chapter_array_and_linkedlist/linked_list.go @@ -9,14 +9,14 @@ import ( ) /* 在链表的结点 n0 之后插入结点 P */ -func insertNode(n0 *pkg.ListNode, P *pkg.ListNode) { +func insertNode(n0 *ListNode, P *ListNode) { n1 := n0.Next n0.Next = P P.Next = n1 } /* 删除链表的结点 n0 之后的首个结点 */ -func removeNode(n0 *pkg.ListNode) { +func removeNode(n0 *ListNode) { if n0.Next == nil { return } @@ -27,7 +27,7 @@ func removeNode(n0 *pkg.ListNode) { } /* 访问链表中索引为 index 的结点 */ -func access(head *pkg.ListNode, index int) *pkg.ListNode { +func access(head *ListNode, index int) *ListNode { for i := 0; i < index; i++ { head = head.Next if head == nil { @@ -38,7 +38,7 @@ func access(head *pkg.ListNode, index int) *pkg.ListNode { } /* 在链表中查找值为 target 的首个结点 */ -func findNode(head *pkg.ListNode, target int) int { +func findNode(head *ListNode, target int) int { index := 0 for head != nil { if head.Val == target { diff --git a/codes/go/chapter_array_and_linkedlist/linked_list_test.go b/codes/go/chapter_array_and_linkedlist/linked_list_test.go index 18061efff..466bde4f5 100644 --- a/codes/go/chapter_array_and_linkedlist/linked_list_test.go +++ b/codes/go/chapter_array_and_linkedlist/linked_list_test.go @@ -6,18 +6,19 @@ package chapter_array_and_linkedlist import ( "fmt" - "github.com/krahets/hello-algo/pkg" "testing" + + . "github.com/krahets/hello-algo/pkg" ) func TestLikedList(t *testing.T) { /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ // 初始化各个结点 - n0 := pkg.NewListNode(1) - n1 := pkg.NewListNode(3) - n2 := pkg.NewListNode(2) - n3 := pkg.NewListNode(5) - n4 := pkg.NewListNode(4) + n0 := NewListNode(1) + n1 := NewListNode(3) + n2 := NewListNode(2) + n3 := NewListNode(5) + n4 := NewListNode(4) // 构建引用指向 n0.Next = n1 @@ -25,17 +26,17 @@ func TestLikedList(t *testing.T) { n2.Next = n3 n3.Next = n4 fmt.Println("初始化的链表为") - pkg.PrintLinkedList(n0) + PrintLinkedList(n0) /* 插入结点 */ - insertNode(n0, pkg.NewListNode(0)) + insertNode(n0, NewListNode(0)) fmt.Println("插入结点后的链表为") - pkg.PrintLinkedList(n0) + PrintLinkedList(n0) /* 删除结点 */ removeNode(n0) fmt.Println("删除结点后的链表为") - pkg.PrintLinkedList(n0) + PrintLinkedList(n0) /* 访问结点 */ node := access(n0, 3) From 5cb62fd45899b8ddd74e1c76cf419356c67449b8 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 2 Jan 2023 19:03:36 +0800 Subject: [PATCH 061/158] Update the array (Go code). --- codes/go/chapter_array_and_linkedlist/array.go | 4 ++-- docs/chapter_array_and_linkedlist/array.md | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/codes/go/chapter_array_and_linkedlist/array.go b/codes/go/chapter_array_and_linkedlist/array.go index 578bcb349..5740811d8 100644 --- a/codes/go/chapter_array_and_linkedlist/array.go +++ b/codes/go/chapter_array_and_linkedlist/array.go @@ -27,8 +27,8 @@ func extend(nums []int, enlarge int) []int { // 初始化一个扩展长度后的数组 res := make([]int, len(nums)+enlarge) // 将原数组中的所有元素复制到新数组 - for i := 0; i < len(nums); i++ { - res[i] = nums[i] + for i, num := range nums { + res[i] = num } // 返回扩展后的新数组 return res diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 5168a5d5d..7f853a8f5 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -136,7 +136,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex ```go title="array.go" /* 随机返回一个数组元素 */ - func randomAccess(nums [5]int) (randomNum int) { + func randomAccess(nums []int) (randomNum int) { // 在区间 [0, nums.length) 中随机抽取一个数字 randomIndex := rand.Intn(len(nums)) // 获取并返回随机元素 @@ -392,7 +392,6 @@ elementAddr = firtstElementAddr + elementLength * elementIndex /* 在数组的索引 index 处插入元素 num */ func insert(nums []int, num int, index int) { // 把索引 index 以及之后的所有元素向后移动一位 - // 如果超出了数组长度,会被直接舍弃 for i := len(nums) - 1; i > index; i-- { nums[i] = nums[i-1] } @@ -403,7 +402,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex /* 删除索引 index 处元素 */ func remove(nums []int, index int) { // 把索引 index 之后的所有元素向前移动一位 - for i := index; i < len(nums) - 1; i++ { + for i := index; i < len(nums)-1; i++ { nums[i] = nums[i+1] } } From 6b02449f2275b5be5df66379b17f29f99c32db68 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 2 Jan 2023 19:09:46 +0800 Subject: [PATCH 062/158] Update the chapter sorting (Go code). --- codes/go/chapter_sorting/{bubble_sort => }/bubble_sort.go | 2 +- codes/go/chapter_sorting/{bubble_sort => }/bubble_sort_test.go | 2 +- .../go/chapter_sorting/{insertion_sort => }/insertion_sort.go | 2 +- .../{insertion_sort => }/insertion_sort_test.go | 2 +- codes/go/chapter_sorting/{merge_sort => }/merge_sort.go | 2 +- codes/go/chapter_sorting/{merge_sort => }/merge_sort_test.go | 3 ++- codes/go/chapter_sorting/{quick_sort => }/quick_sort.go | 2 +- codes/go/chapter_sorting/{quick_sort => }/quick_sort_test.go | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) rename codes/go/chapter_sorting/{bubble_sort => }/bubble_sort.go (97%) rename codes/go/chapter_sorting/{bubble_sort => }/bubble_sort_test.go (94%) rename codes/go/chapter_sorting/{insertion_sort => }/insertion_sort.go (95%) rename codes/go/chapter_sorting/{insertion_sort => }/insertion_sort_test.go (92%) rename codes/go/chapter_sorting/{merge_sort => }/merge_sort.go (98%) rename codes/go/chapter_sorting/{merge_sort => }/merge_sort_test.go (92%) rename codes/go/chapter_sorting/{quick_sort => }/quick_sort.go (99%) rename codes/go/chapter_sorting/{quick_sort => }/quick_sort_test.go (97%) diff --git a/codes/go/chapter_sorting/bubble_sort/bubble_sort.go b/codes/go/chapter_sorting/bubble_sort.go similarity index 97% rename from codes/go/chapter_sorting/bubble_sort/bubble_sort.go rename to codes/go/chapter_sorting/bubble_sort.go index a51d19925..ff1ff9ee5 100644 --- a/codes/go/chapter_sorting/bubble_sort/bubble_sort.go +++ b/codes/go/chapter_sorting/bubble_sort.go @@ -2,7 +2,7 @@ // Created Time: 2022-12-06 // Author: Slone123c (274325721@qq.com) -package bubble_sort +package chapter_sorting /* 冒泡排序 */ func bubbleSort(nums []int) { diff --git a/codes/go/chapter_sorting/bubble_sort/bubble_sort_test.go b/codes/go/chapter_sorting/bubble_sort_test.go similarity index 94% rename from codes/go/chapter_sorting/bubble_sort/bubble_sort_test.go rename to codes/go/chapter_sorting/bubble_sort_test.go index bab85139e..53a7d055a 100644 --- a/codes/go/chapter_sorting/bubble_sort/bubble_sort_test.go +++ b/codes/go/chapter_sorting/bubble_sort_test.go @@ -2,7 +2,7 @@ // Created Time: 2022-12-06 // Author: Slone123c (274325721@qq.com) -package bubble_sort +package chapter_sorting import ( "fmt" diff --git a/codes/go/chapter_sorting/insertion_sort/insertion_sort.go b/codes/go/chapter_sorting/insertion_sort.go similarity index 95% rename from codes/go/chapter_sorting/insertion_sort/insertion_sort.go rename to codes/go/chapter_sorting/insertion_sort.go index 2be78bcf6..24101e969 100644 --- a/codes/go/chapter_sorting/insertion_sort/insertion_sort.go +++ b/codes/go/chapter_sorting/insertion_sort.go @@ -2,7 +2,7 @@ // Created Time: 2022-12-12 // Author: msk397 (machangxinq@gmail.com) -package insertion_sort +package chapter_sorting func insertionSort(nums []int) { // 外循环:待排序元素数量为 n-1, n-2, ..., 1 diff --git a/codes/go/chapter_sorting/insertion_sort/insertion_sort_test.go b/codes/go/chapter_sorting/insertion_sort_test.go similarity index 92% rename from codes/go/chapter_sorting/insertion_sort/insertion_sort_test.go rename to codes/go/chapter_sorting/insertion_sort_test.go index 07a96b8f4..9802b406b 100644 --- a/codes/go/chapter_sorting/insertion_sort/insertion_sort_test.go +++ b/codes/go/chapter_sorting/insertion_sort_test.go @@ -2,7 +2,7 @@ // Created Time: 2022-12-12 // Author: msk397 (machangxinq@gmail.com) -package insertion_sort +package chapter_sorting import ( "fmt" diff --git a/codes/go/chapter_sorting/merge_sort/merge_sort.go b/codes/go/chapter_sorting/merge_sort.go similarity index 98% rename from codes/go/chapter_sorting/merge_sort/merge_sort.go rename to codes/go/chapter_sorting/merge_sort.go index f2910600a..1b3a3932d 100644 --- a/codes/go/chapter_sorting/merge_sort/merge_sort.go +++ b/codes/go/chapter_sorting/merge_sort.go @@ -2,7 +2,7 @@ // Created Time: 2022-12-13 // Author: msk397 (machangxinq@gmail.com) -package merge_sort +package chapter_sorting // 合并左子数组和右子数组 // 左子数组区间 [left, mid] diff --git a/codes/go/chapter_sorting/merge_sort/merge_sort_test.go b/codes/go/chapter_sorting/merge_sort_test.go similarity index 92% rename from codes/go/chapter_sorting/merge_sort/merge_sort_test.go rename to codes/go/chapter_sorting/merge_sort_test.go index 04abd443b..3ece175a1 100644 --- a/codes/go/chapter_sorting/merge_sort/merge_sort_test.go +++ b/codes/go/chapter_sorting/merge_sort_test.go @@ -1,8 +1,9 @@ -package merge_sort // File: merge_sort_test.go // Created Time: 2022-12-13 // Author: msk397 (machangxinq@gmail.com) +package chapter_sorting + import ( "fmt" "testing" diff --git a/codes/go/chapter_sorting/quick_sort/quick_sort.go b/codes/go/chapter_sorting/quick_sort.go similarity index 99% rename from codes/go/chapter_sorting/quick_sort/quick_sort.go rename to codes/go/chapter_sorting/quick_sort.go index d42eb0abd..88aa14264 100644 --- a/codes/go/chapter_sorting/quick_sort/quick_sort.go +++ b/codes/go/chapter_sorting/quick_sort.go @@ -2,7 +2,7 @@ // Created Time: 2022-12-12 // Author: msk397 (machangxinq@gmail.com) -package quick_sort +package chapter_sorting // 快速排序 type QuickSort struct{} diff --git a/codes/go/chapter_sorting/quick_sort/quick_sort_test.go b/codes/go/chapter_sorting/quick_sort_test.go similarity index 97% rename from codes/go/chapter_sorting/quick_sort/quick_sort_test.go rename to codes/go/chapter_sorting/quick_sort_test.go index 0a0a6d44a..86ae0115a 100644 --- a/codes/go/chapter_sorting/quick_sort/quick_sort_test.go +++ b/codes/go/chapter_sorting/quick_sort_test.go @@ -2,7 +2,7 @@ // Created Time: 2022-12-12 // Author: msk397 (machangxinq@gmail.com) -package quick_sort +package chapter_sorting import ( "fmt" From 8c736252abd973e881091facff78872eef85bbb3 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 19:10:48 +0800 Subject: [PATCH 063/158] code: transcribe time_complexity.js --- .../time_complexity.js | 153 ++++++++++++++++++ .../time_complexity.ts | 2 +- 2 files changed, 154 insertions(+), 1 deletion(-) diff --git a/codes/javascript/chapter_computational_complexity/time_complexity.js b/codes/javascript/chapter_computational_complexity/time_complexity.js index 9c877602f..803b15c85 100644 --- a/codes/javascript/chapter_computational_complexity/time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -3,3 +3,156 @@ * Created Time: 2023-01-02 * Author: RiverTwilight (contact@rene.wang) */ + +class time_complexity { + /* 常数阶 */ + constant(n) { + var count = 0; + const size = 100000; + for (var i = 0; i < size; i++) count++; + return count; + } + + /* 线性阶 */ + linear(n) { + var count = 0; + for (var i = 0; i < n; i++) count++; + return count; + } + + /* 线性阶(遍历数组) */ + arrayTraversal(nums) { + var count = 0; + // 循环次数与数组长度成正比 + nums.forEach(() => { + count++; + }); + } + + /* 平方阶 */ + quadratic(n) { + var count = 0; + // 循环次数与数组长度成平方关系 + for (var i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; + } + + /* 平方阶(冒泡排序) */ + bubbleSort(nums) { + var count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (var i = nums.length - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; + } + + /* 指数阶(循环实现) */ + exponential(n) { + var count = 0, + base = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (var i = 0; i < n; i++) { + for (let j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; + } + + /* 指数阶(递归实现) */ + expRecur(n) { + if (n == 1) return 1; + return this.expRecur(n - 1) + this.expRecur(n - 1) + 1; + } + + /* 对数阶(循环实现) */ + logarithmic(n) { + var count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } + + /* 对数阶(递归实现) */ + logRecur(n) { + if (n <= 1) return 0; + return this.logRecur(n / 2) + 1; + } + + /* 线性对数阶 */ + linearLogRecur(n) { + if (n <= 1) return 1; + var count = this.linearLogRecur(n / 2) + this.linearLogRecur(n / 2); + for (var i = 0; i < n; i++) { + count++; + } + return count; + } + + /* 阶乘阶(递归实现) */ + factorialRecur(n) { + if (n == 0) return 1; + var count = 0; + // 从 1 个分裂出 n 个 + for (var i = 0; i < n; i++) { + count += this.factorialRecur(n - 1); + } + return count; + } +} + +(function main() { + var test = new time_complexity(); + + var n = 8; + console.log("输入数据大小 n = " + n); + + var count = test.constant(n); + console.log("常数阶的计算操作数量 = " + count); + + count = test.linear(n); + console.log("线性阶的计算操作数量 = " + count); + count = test.arrayTraversal(new Array(n)); + console.log("线性阶(遍历数组)的计算操作数量 = " + count); + + count = test.quadratic(n); + console.log("平方阶的计算操作数量 = " + count); + var nums = new Array(n); + for (var i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] + count = test.bubbleSort(nums); + console.log("平方阶(冒泡排序)的计算操作数量 = " + count); + + count = test.exponential(n); + console.log("指数阶(循环实现)的计算操作数量 = " + count); + count = test.expRecur(n); + console.log("指数阶(递归实现)的计算操作数量 = " + count); + + count = test.logarithmic(n); + console.log("对数阶(循环实现)的计算操作数量 = " + count); + count = test.logRecur(n); + console.log("对数阶(递归实现)的计算操作数量 = " + count); + + count = test.linearLogRecur(n); + console.log("线性对数阶(递归实现)的计算操作数量 = " + count); + + count = test.factorialRecur(n); + console.log("阶乘阶(递归实现)的计算操作数量 = " + count); +})(); diff --git a/codes/typescript/chapter_computational_complexity/time_complexity.ts b/codes/typescript/chapter_computational_complexity/time_complexity.ts index 6f0c9737c..9c877602f 100644 --- a/codes/typescript/chapter_computational_complexity/time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -1,5 +1,5 @@ /** - * File: time_complexity.ts + * File: time_complexity.js * Created Time: 2023-01-02 * Author: RiverTwilight (contact@rene.wang) */ From f83dcce3467f31823b471e13c7a93b0125efb814 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 19:13:36 +0800 Subject: [PATCH 064/158] fix: doesnt return anything --- .../chapter_computational_complexity/time_complexity.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/codes/javascript/chapter_computational_complexity/time_complexity.js b/codes/javascript/chapter_computational_complexity/time_complexity.js index 803b15c85..9fafb9656 100644 --- a/codes/javascript/chapter_computational_complexity/time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -24,9 +24,10 @@ class time_complexity { arrayTraversal(nums) { var count = 0; // 循环次数与数组长度成正比 - nums.forEach(() => { + for (var i = 0; i < nums.length; i++) { count++; - }); + } + return count; } /* 平方阶 */ From 410c5d6b6270dd58fac638194e56f879fec512eb Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 2 Jan 2023 19:53:55 +0800 Subject: [PATCH 065/158] Free memory after removing a node from a LinkedList or TreeNode. --- .../linked_list.cpp | 2 + .../linkedlist_queue.cpp | 3 + .../linkedlist_stack.cpp | 3 + codes/cpp/chapter_tree/binary_search_tree.cpp | 8 ++- codes/cpp/chapter_tree/binary_tree.cpp | 1 + codes/csharp/chapter_tree/avl_tree.cs | 6 +- .../csharp/chapter_tree/binary_search_tree.cs | 6 +- codes/go/chapter_tree/binary_search_tree.go | 21 ++----- .../chapter_tree/binary_search_tree_test.go | 17 +++--- codes/java/chapter_tree/avl_tree.java | 6 +- .../java/chapter_tree/binary_search_tree.java | 6 +- .../chapter_tree/binary_search_tree.js | 6 +- codes/python/chapter_tree/avl_tree.py | 6 +- .../python/chapter_tree/binary_search_tree.py | 7 +-- .../chapter_tree/binary_search_tree.ts | 6 +- .../linked_list.md | 2 + docs/chapter_stack_and_queue/queue.md | 3 + docs/chapter_stack_and_queue/stack.md | 3 + docs/chapter_tree/avl_tree.md | 37 +----------- docs/chapter_tree/binary_search_tree.md | 60 +++---------------- 20 files changed, 69 insertions(+), 140 deletions(-) diff --git a/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp b/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp index 46116d291..4cf049204 100644 --- a/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp +++ b/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp @@ -21,6 +21,8 @@ void remove(ListNode* n0) { ListNode* P = n0->next; ListNode* n1 = P->next; n0->next = n1; + // 释放内存 + delete P; } /* 访问链表中索引为 index 的结点 */ diff --git a/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp b/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp index 17625322d..dcaac225f 100644 --- a/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp @@ -50,7 +50,10 @@ public: int poll() { int num = peek(); // 删除头结点 + ListNode *tmp = front; front = front->next; + // 释放内存 + delete tmp; queSize--; return num; } diff --git a/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp b/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp index 1bb7c2a3c..312b75135 100644 --- a/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp +++ b/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp @@ -39,7 +39,10 @@ public: /* 出栈 */ int pop() { int num = top(); + ListNode *tmp = stackTop; stackTop = stackTop->next; + // 释放内存 + delete tmp; stkSize--; return num; } diff --git a/codes/cpp/chapter_tree/binary_search_tree.cpp b/codes/cpp/chapter_tree/binary_search_tree.cpp index 246bdbaa1..c7f8734da 100644 --- a/codes/cpp/chapter_tree/binary_search_tree.cpp +++ b/codes/cpp/chapter_tree/binary_search_tree.cpp @@ -96,11 +96,13 @@ public: // 删除结点 cur if (pre->left == cur) pre->left = child; else pre->right = child; + // 释放内存 + delete cur; } // 子结点数量 = 2 else { // 获取中序遍历中 cur 的下一个结点 - TreeNode* nex = min(cur->right); + TreeNode* nex = getInOrderNext(cur->right); int tmp = nex->val; // 递归删除结点 nex remove(nex->val); @@ -110,8 +112,8 @@ public: return cur; } - /* 获取最小结点 */ - TreeNode* min(TreeNode* root) { + /* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ + TreeNode* getInOrderNext(TreeNode* root) { if (root == nullptr) return root; // 循环访问左子结点,直到叶结点时为最小结点,跳出 while (root->left != nullptr) { diff --git a/codes/cpp/chapter_tree/binary_tree.cpp b/codes/cpp/chapter_tree/binary_tree.cpp index 3258d9cfc..fb00431eb 100644 --- a/codes/cpp/chapter_tree/binary_tree.cpp +++ b/codes/cpp/chapter_tree/binary_tree.cpp @@ -33,6 +33,7 @@ int main() { PrintUtil::printTree(n1); // 删除结点 P n1->left = n2; + delete P; // 释放内存 cout << endl << "删除结点 P 后\n" << endl; PrintUtil::printTree(n1); diff --git a/codes/csharp/chapter_tree/avl_tree.cs b/codes/csharp/chapter_tree/avl_tree.cs index 99ec17f63..6bcb559b7 100644 --- a/codes/csharp/chapter_tree/avl_tree.cs +++ b/codes/csharp/chapter_tree/avl_tree.cs @@ -162,7 +162,7 @@ namespace hello_algo.chapter_tree else { // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 - TreeNode? temp = minNode(node.right); + TreeNode? temp = getInOrderNext(node.right); node.right = removeHelper(node.right, temp.val); node.val = temp.val; } @@ -174,8 +174,8 @@ namespace hello_algo.chapter_tree return node; } - /* 获取最小结点 */ - private TreeNode? minNode(TreeNode? node) + /* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ + private TreeNode? getInOrderNext(TreeNode? node) { if (node == null) return node; // 循环访问左子结点,直到叶结点时为最小结点,跳出 diff --git a/codes/csharp/chapter_tree/binary_search_tree.cs b/codes/csharp/chapter_tree/binary_search_tree.cs index 8224317df..5164cf780 100644 --- a/codes/csharp/chapter_tree/binary_search_tree.cs +++ b/codes/csharp/chapter_tree/binary_search_tree.cs @@ -125,7 +125,7 @@ namespace hello_algo.chapter_tree else { // 获取中序遍历中 cur 的下一个结点 - TreeNode? nex = min(cur.right); + TreeNode? nex = getInOrderNext(cur.right); if (nex != null) { int tmp = nex.val; @@ -138,8 +138,8 @@ namespace hello_algo.chapter_tree return cur; } - /* 获取最小结点 */ - private TreeNode? min(TreeNode? root) + /* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ + private TreeNode? getInOrderNext(TreeNode? root) { if (root == null) return root; // 循环访问左子结点,直到叶结点时为最小结点,跳出 diff --git a/codes/go/chapter_tree/binary_search_tree.go b/codes/go/chapter_tree/binary_search_tree.go index 2af2e7be0..c8fc5d62c 100644 --- a/codes/go/chapter_tree/binary_search_tree.go +++ b/codes/go/chapter_tree/binary_search_tree.go @@ -23,13 +23,13 @@ func NewBinarySearchTree(nums []int) *BinarySearchTree { } } -// GetRoot Get the root node of binary search tree +/* 获取根结点 */ func (bst *BinarySearchTree) GetRoot() *TreeNode { return bst.root } -// GetMin Get node with the min value -func (bst *BinarySearchTree) GetMin(node *TreeNode) *TreeNode { +/* 获取中序遍历的下一个结点 */ +func (bst *BinarySearchTree) GetInOrderNext(node *TreeNode) *TreeNode { if node == nil { return node } @@ -40,19 +40,6 @@ func (bst *BinarySearchTree) GetMin(node *TreeNode) *TreeNode { return node } -// GetInorderNext Get node inorder next -func (bst *BinarySearchTree) GetInorderNext(node *TreeNode) *TreeNode { - if node == nil || node.Right == nil { - return node - } - node = node.Right - // 循环访问左子结点,直到叶结点时为最小结点,跳出 - for node.Left != nil { - node = node.Left - } - return node -} - /* 查找结点 */ func (bst *BinarySearchTree) Search(num int) *TreeNode { node := bst.root @@ -149,7 +136,7 @@ func (bst *BinarySearchTree) Remove(num int) *TreeNode { // 子结点数为 2 } else { // 获取中序遍历中待删除结点 cur 的下一个结点 - next := bst.GetInorderNext(cur) + next := bst.GetInOrderNext(cur) temp := next.Val // 递归删除结点 next bst.Remove(next.Val) diff --git a/codes/go/chapter_tree/binary_search_tree_test.go b/codes/go/chapter_tree/binary_search_tree_test.go index 0d0369a78..cdf7d8d48 100644 --- a/codes/go/chapter_tree/binary_search_tree_test.go +++ b/codes/go/chapter_tree/binary_search_tree_test.go @@ -12,33 +12,30 @@ import ( func TestBinarySearchTree(t *testing.T) { nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} bst := NewBinarySearchTree(nums) - fmt.Println("初始化的二叉树为:") + fmt.Println("\n初始化的二叉树为:") bst.Print() // 获取根结点 node := bst.GetRoot() - fmt.Println("二叉树的根结点为:", node.Val) - // 获取最小的结点 - node = bst.GetMin(bst.GetRoot()) - fmt.Println("二叉树的最小结点为:", node.Val) + fmt.Println("\n二叉树的根结点为:", node.Val) // 查找结点 node = bst.Search(5) - fmt.Println("查找到的结点对象为", node, ",结点值 =", node.Val) + fmt.Println("\n查找到的结点对象为", node, ",结点值 =", node.Val) // 插入结点 node = bst.Insert(16) - fmt.Println("插入结点后 16 的二叉树为:") + fmt.Println("\n插入结点后 16 的二叉树为:") bst.Print() // 删除结点 bst.Remove(1) - fmt.Println("删除结点 1 后的二叉树为:") + fmt.Println("\n删除结点 1 后的二叉树为:") bst.Print() bst.Remove(2) - fmt.Println("删除结点 2 后的二叉树为:") + fmt.Println("\n删除结点 2 后的二叉树为:") bst.Print() bst.Remove(4) - fmt.Println("删除结点 4 后的二叉树为:") + fmt.Println("\n删除结点 4 后的二叉树为:") bst.Print() } diff --git a/codes/java/chapter_tree/avl_tree.java b/codes/java/chapter_tree/avl_tree.java index 48be762e9..2f731b7e4 100644 --- a/codes/java/chapter_tree/avl_tree.java +++ b/codes/java/chapter_tree/avl_tree.java @@ -138,7 +138,7 @@ class AVLTree { node = child; } else { // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 - TreeNode temp = minNode(node.right); + TreeNode temp = getInOrderNext(node.right); node.right = removeHelper(node.right, temp.val); node.val = temp.val; } @@ -150,8 +150,8 @@ class AVLTree { return node; } - /* 获取最小结点 */ - private TreeNode minNode(TreeNode node) { + /* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ + private TreeNode getInOrderNext(TreeNode node) { if (node == null) return node; // 循环访问左子结点,直到叶结点时为最小结点,跳出 while (node.left != null) { diff --git a/codes/java/chapter_tree/binary_search_tree.java b/codes/java/chapter_tree/binary_search_tree.java index 0770c567d..4a7c53dba 100644 --- a/codes/java/chapter_tree/binary_search_tree.java +++ b/codes/java/chapter_tree/binary_search_tree.java @@ -101,7 +101,7 @@ class BinarySearchTree { // 子结点数量 = 2 else { // 获取中序遍历中 cur 的下一个结点 - TreeNode nex = min(cur.right); + TreeNode nex = getInOrderNext(cur.right); int tmp = nex.val; // 递归删除结点 nex remove(nex.val); @@ -111,8 +111,8 @@ class BinarySearchTree { return cur; } - /* 获取最小结点 */ - public TreeNode min(TreeNode root) { + /* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ + public TreeNode getInOrderNext(TreeNode root) { if (root == null) return root; // 循环访问左子结点,直到叶结点时为最小结点,跳出 while (root.left != null) { diff --git a/codes/javascript/chapter_tree/binary_search_tree.js b/codes/javascript/chapter_tree/binary_search_tree.js index 1e0d92738..c00d8bea8 100644 --- a/codes/javascript/chapter_tree/binary_search_tree.js +++ b/codes/javascript/chapter_tree/binary_search_tree.js @@ -98,7 +98,7 @@ function remove(num) { // 子结点数量 = 2 else { // 获取中序遍历中 cur 的下一个结点 - let nex = min(cur.right); + let nex = getInOrderNext(cur.right); let tmp = nex.val; // 递归删除结点 nex remove(nex.val); @@ -108,8 +108,8 @@ function remove(num) { return cur; } -/* 获取最小结点 */ -function min(root) { +/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ +function getInOrderNext(root) { if (root === null) return root; // 循环访问左子结点,直到叶结点时为最小结点,跳出 while (root.left !== null) { diff --git a/codes/python/chapter_tree/avl_tree.py b/codes/python/chapter_tree/avl_tree.py index d0ff48cba..04218e46a 100644 --- a/codes/python/chapter_tree/avl_tree.py +++ b/codes/python/chapter_tree/avl_tree.py @@ -132,7 +132,7 @@ class AVLTree: else: node = child else: # 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 - temp = self.__min_node(node.right) + temp = self.__get_inorder_next(node.right) node.right = self.__remove_helper(node.right, temp.val) node.val = temp.val # 更新结点高度 @@ -140,8 +140,8 @@ class AVLTree: # 2. 执行旋转操作,使该子树重新恢复平衡 return self.__rotate(node) - """ 获取最小结点 """ - def __min_node(self, node: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: + """ 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) """ + def __get_inorder_next(self, node: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: if node is None: return None # 循环访问左子结点,直到叶结点时为最小结点,跳出 diff --git a/codes/python/chapter_tree/binary_search_tree.py b/codes/python/chapter_tree/binary_search_tree.py index 23e341854..db52d57bd 100644 --- a/codes/python/chapter_tree/binary_search_tree.py +++ b/codes/python/chapter_tree/binary_search_tree.py @@ -117,7 +117,7 @@ class BinarySearchTree: # 子结点数量 = 2 else: # 获取中序遍历中 cur 的下一个结点 - nex = self.min(cur.right) + nex = self.get_inorder_next(cur.right) tmp = nex.val # 递归删除结点 nex self.remove(nex.val) @@ -125,11 +125,10 @@ class BinarySearchTree: cur.val = tmp return cur - """ 获取最小结点 """ - def min(self, root: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: + """ 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) """ + def get_inorder_next(self, root: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: if root is None: return root - # 循环访问左子结点,直到叶结点时为最小结点,跳出 while root.left is not None: root = root.left diff --git a/codes/typescript/chapter_tree/binary_search_tree.ts b/codes/typescript/chapter_tree/binary_search_tree.ts index 6dad83421..a99e6ba26 100644 --- a/codes/typescript/chapter_tree/binary_search_tree.ts +++ b/codes/typescript/chapter_tree/binary_search_tree.ts @@ -120,7 +120,7 @@ function remove(num: number): TreeNode | null { // 子结点数量 = 2 else { // 获取中序遍历中 cur 的下一个结点 - let next = min(cur.right); + let next = getInOrderNext(cur.right); let tmp = next!.val; // 递归删除结点 nex remove(next!.val); @@ -130,8 +130,8 @@ function remove(num: number): TreeNode | null { return cur; } -/* 获取最小结点 */ -function min(root: TreeNode | null): TreeNode | null { +/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ +function getInOrderNext(root: TreeNode | null): TreeNode | null { if (root === null) { return null; } diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index a55d68028..5b6888c06 100644 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -293,6 +293,8 @@ comments: true ListNode* P = n0->next; ListNode* n1 = P->next; n0->next = n1; + // 释放内存 + delete P; } ``` diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index 3c5c0528d..cc815b3ab 100644 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -331,7 +331,10 @@ comments: true int poll() { int num = peek(); // 删除头结点 + ListNode *tmp = front; front = front->next; + // 释放内存 + delete tmp; queSize--; return num; } diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index b0d577bce..7e35fddbd 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -311,7 +311,10 @@ comments: true /* 出栈 */ int pop() { int num = top(); + ListNode *tmp = stackTop; stackTop = stackTop->next; + // 释放内存 + delete tmp; stkSize--; return num; } diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index e9a656bed..45308fa6f 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -772,7 +772,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 node = child; } else { // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 - TreeNode temp = minNode(node.right); + TreeNode temp = getInOrderNext(node.right); node.right = removeHelper(node.right, temp.val); node.val = temp.val; } @@ -783,16 +783,6 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 // 返回子树的根节点 return node; } - - /* 获取最小结点 */ - TreeNode minNode(TreeNode node) { - if (node == null) return node; - // 循环访问左子结点,直到叶结点时为最小结点,跳出 - while (node.left != null) { - node = node.left; - } - return node; - } ``` === "C++" @@ -828,22 +818,13 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 else: node = child else: # 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 - temp = self.min_node(node.right) + temp = self.__get_inorder_next(node.right) node.right = self.__remove_helper(node.right, temp.val) node.val = temp.val # 更新结点高度 self.__update_height(node) # 2. 执行旋转操作,使该子树重新恢复平衡 return self.__rotate(node) - - """ 获取最小结点 """ - def min_node(self, node: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: - if node is None: - return None - # 循环访问左子结点,直到叶结点时为最小结点,跳出 - while node.left is not None: - node = node.left - return node ``` === "Go" @@ -904,7 +885,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 else { // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 - TreeNode? temp = minNode(node.right); + TreeNode? temp = getInOrderNext(node.right); node.right = removeHelper(node.right, temp.val); node.val = temp.val; } @@ -915,18 +896,6 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 // 返回子树的根节点 return node; } - - /* 获取最小结点 */ - private TreeNode? minNode(TreeNode? node) - { - if (node == null) return node; - // 循环访问左子结点,直到叶结点时为最小结点,跳出 - while (node.left != null) - { - node = node.left; - } - return node; - } ``` ### 查找结点 diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index 629c051f1..cc7c2f51c 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -445,19 +445,15 @@ comments: true 3. 使用 `nex` 替换待删除结点; === "Step 1" - ![bst_remove_case3_1](binary_search_tree.assets/bst_remove_case3_1.png) === "Step 2" - ![bst_remove_case3_2](binary_search_tree.assets/bst_remove_case3_2.png) === "Step 3" - ![bst_remove_case3_3](binary_search_tree.assets/bst_remove_case3_3.png) === "Step 4" - ![bst_remove_case3_4](binary_search_tree.assets/bst_remove_case3_4.png) 删除结点操作也使用 $O(\log n)$ 时间,其中查找待删除结点 $O(\log n)$ ,获取中序遍历后继结点 $O(\log n)$ 。 @@ -489,11 +485,13 @@ comments: true // 删除结点 cur if (pre.left == cur) pre.left = child; else pre.right = child; + // 释放内存 + delete cur; } // 子结点数量 = 2 else { // 获取中序遍历中 cur 的下一个结点 - TreeNode nex = min(cur.right); + TreeNode nex = getInOrderNext(cur.right); int tmp = nex.val; // 递归删除结点 nex remove(nex.val); @@ -502,15 +500,6 @@ comments: true } return cur; } - /* 获取最小结点 */ - TreeNode min(TreeNode root) { - if (root == null) return root; - // 循环访问左子结点,直到叶结点时为最小结点,跳出 - while (root.left != null) { - root = root.left; - } - return root; - } ``` === "C++" @@ -544,7 +533,7 @@ comments: true // 子结点数量 = 2 else { // 获取中序遍历中 cur 的下一个结点 - TreeNode* nex = min(cur->right); + TreeNode* nex = getInOrderNext(cur->right); int tmp = nex->val; // 递归删除结点 nex remove(nex->val); @@ -553,15 +542,6 @@ comments: true } return cur; } - /* 获取最小结点 */ - TreeNode* min(TreeNode* root) { - if (root == nullptr) return root; - // 循环访问左子结点,直到叶结点时为最小结点,跳出 - while (root->left != nullptr) { - root = root->left; - } - return root; - } ``` === "Python" @@ -604,23 +584,13 @@ comments: true # 子结点数量 = 2 else: # 获取中序遍历中 cur 的下一个结点 - nex = self.min(cur.right) + nex = self.get_inorder_next(cur.right) tmp = nex.val # 递归删除结点 nex self.remove(nex.val) # 将 nex 的值复制给 cur cur.val = tmp return cur - - """ 获取最小结点 """ - def min(self, root: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: - if root is None: - return root - - # 循环访问左子结点,直到叶结点时为最小结点,跳出 - while root.left is not None: - root = root.left - return root ``` === "Go" @@ -671,7 +641,7 @@ comments: true // 子结点数为 2 } else { // 获取中序遍历中待删除结点 cur 的下一个结点 - next := bst.GetInorderNext(cur) + next := bst.GetInOrderNext(cur) temp := next.Val // 递归删除结点 next bst.Remove(next.Val) @@ -713,7 +683,7 @@ comments: true // 子结点数量 = 2 else { // 获取中序遍历中 cur 的下一个结点 - let nex = min(cur.right); + let nex = getInOrderNext(cur.right); let tmp = nex.val; // 递归删除结点 nex remove(nex.val); @@ -766,7 +736,7 @@ comments: true // 子结点数量 = 2 else { // 获取中序遍历中 cur 的下一个结点 - let next = min(cur.right); + let next = getInOrderNext(cur.right); let tmp = next!.val; // 递归删除结点 nex remove(next!.val); @@ -824,7 +794,7 @@ comments: true else { // 获取中序遍历中 cur 的下一个结点 - TreeNode? nex = min(cur.right); + TreeNode? nex = getInOrderNext(cur.right); if (nex != null) { int tmp = nex.val; @@ -836,18 +806,6 @@ comments: true } return cur; } - - /* 获取最小结点 */ - TreeNode? min(TreeNode? root) - { - if (root == null) return root; - // 循环访问左子结点,直到叶结点时为最小结点,跳出 - while (root.left != null) - { - root = root.left; - } - return root; - } ``` ## 二叉搜索树的优势 From 6e2412f89704665a9e365f9222cf07ee8db422f1 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 20:40:01 +0800 Subject: [PATCH 066/158] lint: code lint --- docs/chapter_computational_complexity/time_complexity.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 8c2edb08a..2ba703457 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -412,7 +412,7 @@ $$ a *= 2; // +1 // 循环 n 次 for(var i = 0; i < n; i++){ // +1 - console.log(0) // +1 + console.log(0); // +1 } } @@ -428,7 +428,7 @@ $$ a *= 2; // +1 // 循环 n 次 for(var i = 0; i < n; i++){ // +1 - console.log(0) // +1 + console.log(0); // +1 } } From db2a91bd93f3803ae34f218736414876e36a27f8 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 20:49:35 +0800 Subject: [PATCH 067/158] lint: remove class and main --- .../time_complexity.js | 280 +++++++++--------- 1 file changed, 137 insertions(+), 143 deletions(-) diff --git a/codes/javascript/chapter_computational_complexity/time_complexity.js b/codes/javascript/chapter_computational_complexity/time_complexity.js index 9fafb9656..b1a9eb7c5 100644 --- a/codes/javascript/chapter_computational_complexity/time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -4,156 +4,150 @@ * Author: RiverTwilight (contact@rene.wang) */ -class time_complexity { - /* 常数阶 */ - constant(n) { - var count = 0; - const size = 100000; - for (var i = 0; i < size; i++) count++; - return count; - } - - /* 线性阶 */ - linear(n) { - var count = 0; - for (var i = 0; i < n; i++) count++; - return count; - } - - /* 线性阶(遍历数组) */ - arrayTraversal(nums) { - var count = 0; - // 循环次数与数组长度成正比 - for (var i = 0; i < nums.length; i++) { - count++; - } - return count; - } - - /* 平方阶 */ - quadratic(n) { - var count = 0; - // 循环次数与数组长度成平方关系 - for (var i = 0; i < n; i++) { - for (let j = 0; j < n; j++) { - count++; - } - } - return count; - } - - /* 平方阶(冒泡排序) */ - bubbleSort(nums) { - var count = 0; // 计数器 - // 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for (var i = nums.length - 1; i > 0; i--) { - // 内循环:冒泡操作 - for (let j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - return count; - } - - /* 指数阶(循环实现) */ - exponential(n) { - var count = 0, - base = 1; - // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (var i = 0; i < n; i++) { - for (let j = 0; j < base; j++) { - count++; - } - base *= 2; - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count; - } - - /* 指数阶(递归实现) */ - expRecur(n) { - if (n == 1) return 1; - return this.expRecur(n - 1) + this.expRecur(n - 1) + 1; - } - - /* 对数阶(循环实现) */ - logarithmic(n) { - var count = 0; - while (n > 1) { - n = n / 2; - count++; - } - return count; - } - - /* 对数阶(递归实现) */ - logRecur(n) { - if (n <= 1) return 0; - return this.logRecur(n / 2) + 1; - } - - /* 线性对数阶 */ - linearLogRecur(n) { - if (n <= 1) return 1; - var count = this.linearLogRecur(n / 2) + this.linearLogRecur(n / 2); - for (var i = 0; i < n; i++) { - count++; - } - return count; - } - - /* 阶乘阶(递归实现) */ - factorialRecur(n) { - if (n == 0) return 1; - var count = 0; - // 从 1 个分裂出 n 个 - for (var i = 0; i < n; i++) { - count += this.factorialRecur(n - 1); - } - return count; - } +/* 常数阶 */ +function constant(n) { + var count = 0; + const size = 100000; + for (var i = 0; i < size; i++) count++; + return count; } -(function main() { - var test = new time_complexity(); +/* 线性阶 */ +function linear(n) { + var count = 0; + for (var i = 0; i < n; i++) count++; + return count; +} - var n = 8; - console.log("输入数据大小 n = " + n); +/* 线性阶(遍历数组) */ +function arrayTraversal(nums) { + var count = 0; + // 循环次数与数组长度成正比 + for (var i = 0; i < nums.length; i++) { + count++; + } + return count; +} - var count = test.constant(n); - console.log("常数阶的计算操作数量 = " + count); +/* 平方阶 */ +function quadratic(n) { + var count = 0; + // 循环次数与数组长度成平方关系 + for (var i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; +} - count = test.linear(n); - console.log("线性阶的计算操作数量 = " + count); - count = test.arrayTraversal(new Array(n)); - console.log("线性阶(遍历数组)的计算操作数量 = " + count); +/* 平方阶(冒泡排序) */ +function bubbleSort(nums) { + var count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (var i = nums.length - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; +} - count = test.quadratic(n); - console.log("平方阶的计算操作数量 = " + count); - var nums = new Array(n); - for (var i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] - count = test.bubbleSort(nums); - console.log("平方阶(冒泡排序)的计算操作数量 = " + count); +/* 指数阶(循环实现) */ +function exponential(n) { + var count = 0, + base = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (var i = 0; i < n; i++) { + for (let j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} - count = test.exponential(n); - console.log("指数阶(循环实现)的计算操作数量 = " + count); - count = test.expRecur(n); - console.log("指数阶(递归实现)的计算操作数量 = " + count); +/* 指数阶(递归实现) */ +function expRecur(n) { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} - count = test.logarithmic(n); - console.log("对数阶(循环实现)的计算操作数量 = " + count); - count = test.logRecur(n); - console.log("对数阶(递归实现)的计算操作数量 = " + count); +/* 对数阶(循环实现) */ +function logarithmic(n) { + var count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; +} - count = test.linearLogRecur(n); - console.log("线性对数阶(递归实现)的计算操作数量 = " + count); +/* 对数阶(递归实现) */ +function logRecur(n) { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; +} - count = test.factorialRecur(n); - console.log("阶乘阶(递归实现)的计算操作数量 = " + count); -})(); +/* 线性对数阶 */ +function linearLogRecur(n) { + if (n <= 1) return 1; + var count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (var i = 0; i < n; i++) { + count++; + } + return count; +} + +/* 阶乘阶(递归实现) */ +function factorialRecur(n) { + if (n == 0) return 1; + var count = 0; + // 从 1 个分裂出 n 个 + for (var i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; +} + +var n = 8; +console.log("输入数据大小 n = " + n); + +var count = constant(n); +console.log("常数阶的计算操作数量 = " + count); + +count = linear(n); +console.log("线性阶的计算操作数量 = " + count); +count = arrayTraversal(new Array(n)); +console.log("线性阶(遍历数组)的计算操作数量 = " + count); + +count = quadratic(n); +console.log("平方阶的计算操作数量 = " + count); +var nums = new Array(n); +for (var i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] +count = bubbleSort(nums); +console.log("平方阶(冒泡排序)的计算操作数量 = " + count); + +count = exponential(n); +console.log("指数阶(循环实现)的计算操作数量 = " + count); +count = expRecur(n); +console.log("指数阶(递归实现)的计算操作数量 = " + count); + +count = logarithmic(n); +console.log("对数阶(循环实现)的计算操作数量 = " + count); +count = logRecur(n); +console.log("对数阶(递归实现)的计算操作数量 = " + count); + +count = linearLogRecur(n); +console.log("线性对数阶(递归实现)的计算操作数量 = " + count); + +count = factorialRecur(n); +console.log("阶乘阶(递归实现)的计算操作数量 = " + count); From d3e15a88563713444b0a6ef3a5d745bd30165396 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 20:52:15 +0800 Subject: [PATCH 068/158] lint: var to let --- .../time_complexity.js | 42 ++--- .../time_complexity.ts | 154 ++++++++++++++++++ 2 files changed, 175 insertions(+), 21 deletions(-) diff --git a/codes/javascript/chapter_computational_complexity/time_complexity.js b/codes/javascript/chapter_computational_complexity/time_complexity.js index b1a9eb7c5..d044f22f0 100644 --- a/codes/javascript/chapter_computational_complexity/time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -6,24 +6,24 @@ /* 常数阶 */ function constant(n) { - var count = 0; + let count = 0; const size = 100000; - for (var i = 0; i < size; i++) count++; + for (let i = 0; i < size; i++) count++; return count; } /* 线性阶 */ function linear(n) { - var count = 0; - for (var i = 0; i < n; i++) count++; + let count = 0; + for (let i = 0; i < n; i++) count++; return count; } /* 线性阶(遍历数组) */ function arrayTraversal(nums) { - var count = 0; + let count = 0; // 循环次数与数组长度成正比 - for (var i = 0; i < nums.length; i++) { + for (let i = 0; i < nums.length; i++) { count++; } return count; @@ -31,9 +31,9 @@ function arrayTraversal(nums) { /* 平方阶 */ function quadratic(n) { - var count = 0; + let count = 0; // 循环次数与数组长度成平方关系 - for (var i = 0; i < n; i++) { + for (let i = 0; i < n; i++) { for (let j = 0; j < n; j++) { count++; } @@ -43,9 +43,9 @@ function quadratic(n) { /* 平方阶(冒泡排序) */ function bubbleSort(nums) { - var count = 0; // 计数器 + let count = 0; // 计数器 // 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for (var i = nums.length - 1; i > 0; i--) { + for (let i = nums.length - 1; i > 0; i--) { // 内循环:冒泡操作 for (let j = 0; j < i; j++) { if (nums[j] > nums[j + 1]) { @@ -62,10 +62,10 @@ function bubbleSort(nums) { /* 指数阶(循环实现) */ function exponential(n) { - var count = 0, + let count = 0, base = 1; // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (var i = 0; i < n; i++) { + for (let i = 0; i < n; i++) { for (let j = 0; j < base; j++) { count++; } @@ -83,7 +83,7 @@ function expRecur(n) { /* 对数阶(循环实现) */ function logarithmic(n) { - var count = 0; + let count = 0; while (n > 1) { n = n / 2; count++; @@ -100,8 +100,8 @@ function logRecur(n) { /* 线性对数阶 */ function linearLogRecur(n) { if (n <= 1) return 1; - var count = linearLogRecur(n / 2) + linearLogRecur(n / 2); - for (var i = 0; i < n; i++) { + let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (let i = 0; i < n; i++) { count++; } return count; @@ -110,18 +110,18 @@ function linearLogRecur(n) { /* 阶乘阶(递归实现) */ function factorialRecur(n) { if (n == 0) return 1; - var count = 0; + let count = 0; // 从 1 个分裂出 n 个 - for (var i = 0; i < n; i++) { + for (let i = 0; i < n; i++) { count += factorialRecur(n - 1); } return count; } -var n = 8; +let n = 8; console.log("输入数据大小 n = " + n); -var count = constant(n); +let count = constant(n); console.log("常数阶的计算操作数量 = " + count); count = linear(n); @@ -131,8 +131,8 @@ console.log("线性阶(遍历数组)的计算操作数量 = " + count); count = quadratic(n); console.log("平方阶的计算操作数量 = " + count); -var nums = new Array(n); -for (var i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] +let nums = new Array(n); +for (let i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] count = bubbleSort(nums); console.log("平方阶(冒泡排序)的计算操作数量 = " + count); diff --git a/codes/typescript/chapter_computational_complexity/time_complexity.ts b/codes/typescript/chapter_computational_complexity/time_complexity.ts index 9c877602f..f722a1117 100644 --- a/codes/typescript/chapter_computational_complexity/time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -3,3 +3,157 @@ * Created Time: 2023-01-02 * Author: RiverTwilight (contact@rene.wang) */ + +/** + * File: time_complexity.js + * Created Time: 2023-01-02 + * Author: RiverTwilight (contact@rene.wang) + */ + +/* 常数阶 */ +function constant(n: number): number { + let count = 0; + const size = 100000; + for (let i = 0; i < size; i++) count++; + return count; +} + +/* 线性阶 */ +function linear(n: number): number { + let count = 0; + for (let i = 0; i < n; i++) count++; + return count; +} + +/* 线性阶(遍历数组) */ +function arrayTraversal(nums) { + let count = 0; + // 循环次数与数组长度成正比 + for (let i = 0; i < nums.length; i++) { + count++; + } + return count; +} + +/* 平方阶 */ +function quadratic(n: number): number { + let count = 0; + // 循环次数与数组长度成平方关系 + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; +} + +/* 平方阶(冒泡排序) */ +function bubbleSort(nums: number[]): number { + let count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (let i = nums.length - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; +} + +/* 指数阶(循环实现) */ +function exponential(n: number): number { + let count = 0, + base = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (let i = 0; i < n; i++) { + for (let j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} + +/* 指数阶(递归实现) */ +function expRecur(n: number): number { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} + +/* 对数阶(循环实现) */ +function logarithmic(n: number): number { + let count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; +} + +/* 对数阶(递归实现) */ +function logRecur(n: number): number { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; +} + +/* 线性对数阶 */ +function linearLogRecur(n: number): number { + if (n <= 1) return 1; + let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (let i = 0; i < n; i++) { + count++; + } + return count; +} + +/* 阶乘阶(递归实现) */ +function factorialRecur(n: number): number { + if (n == 0) return 1; + let count = 0; + // 从 1 个分裂出 n 个 + for (let i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; +} + +var n: number = 8; +console.log("输入数据大小 n = " + n); + +let count = constant(n); +console.log("常数阶的计算操作数量 = " + count); + +count = linear(n); +console.log("线性阶的计算操作数量 = " + count); +count = arrayTraversal(new Array(n)); +console.log("线性阶(遍历数组)的计算操作数量 = " + count); + +count = quadratic(n); +console.log("平方阶的计算操作数量 = " + count); +var nums = new Array(n); +for (let i = 0; i < n; i++) nums[i] = n - i; // [n,n-1,...,2,1] +count = bubbleSort(nums); +console.log("平方阶(冒泡排序)的计算操作数量 = " + count); + +count = exponential(n); +console.log("指数阶(循环实现)的计算操作数量 = " + count); +count = expRecur(n); +console.log("指数阶(递归实现)的计算操作数量 = " + count); + +count = logarithmic(n); +console.log("对数阶(循环实现)的计算操作数量 = " + count); +count = logRecur(n); +console.log("对数阶(递归实现)的计算操作数量 = " + count); + +count = linearLogRecur(n); +console.log("线性对数阶(递归实现)的计算操作数量 = " + count); + +count = factorialRecur(n); +console.log("阶乘阶(递归实现)的计算操作数量 = " + count); From 621fcb731c3bc833f0de095e3dc67b7fe98bd6a3 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 21:01:16 +0800 Subject: [PATCH 069/158] lint: switch indent type --- .../time_complexity.js | 2 +- .../time_complexity.ts | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/codes/javascript/chapter_computational_complexity/time_complexity.js b/codes/javascript/chapter_computational_complexity/time_complexity.js index d044f22f0..4cd6705bf 100644 --- a/codes/javascript/chapter_computational_complexity/time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -118,7 +118,7 @@ function factorialRecur(n) { return count; } -let n = 8; +const n = 8; console.log("输入数据大小 n = " + n); let count = constant(n); diff --git a/codes/typescript/chapter_computational_complexity/time_complexity.ts b/codes/typescript/chapter_computational_complexity/time_complexity.ts index f722a1117..0b1b5be4c 100644 --- a/codes/typescript/chapter_computational_complexity/time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -1,11 +1,5 @@ /** - * File: time_complexity.js - * Created Time: 2023-01-02 - * Author: RiverTwilight (contact@rene.wang) - */ - -/** - * File: time_complexity.js + * File: time_complexity.ts * Created Time: 2023-01-02 * Author: RiverTwilight (contact@rene.wang) */ @@ -26,7 +20,7 @@ function linear(n: number): number { } /* 线性阶(遍历数组) */ -function arrayTraversal(nums) { +function arrayTraversal(nums: number[]) { let count = 0; // 循环次数与数组长度成正比 for (let i = 0; i < nums.length; i++) { @@ -124,10 +118,10 @@ function factorialRecur(n: number): number { return count; } -var n: number = 8; +var n = 8; console.log("输入数据大小 n = " + n); -let count = constant(n); +var count = constant(n); console.log("常数阶的计算操作数量 = " + count); count = linear(n); From 63cd3e4f65117235461437df3f02604cf93aadc5 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 21:16:24 +0800 Subject: [PATCH 070/158] lint: added prettier config and switch indent type --- .prettierrc | 4 + .../time_complexity.js | 140 +++++++++--------- .../time_complexity.ts | 140 +++++++++--------- 3 files changed, 144 insertions(+), 140 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..5a938ce18 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 4, + "useTabs": false +} diff --git a/codes/javascript/chapter_computational_complexity/time_complexity.js b/codes/javascript/chapter_computational_complexity/time_complexity.js index 4cd6705bf..c8809caa0 100644 --- a/codes/javascript/chapter_computational_complexity/time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -6,116 +6,116 @@ /* 常数阶 */ function constant(n) { - let count = 0; - const size = 100000; - for (let i = 0; i < size; i++) count++; - return count; + let count = 0; + const size = 100000; + for (let i = 0; i < size; i++) count++; + return count; } /* 线性阶 */ function linear(n) { - let count = 0; - for (let i = 0; i < n; i++) count++; - return count; + let count = 0; + for (let i = 0; i < n; i++) count++; + return count; } /* 线性阶(遍历数组) */ function arrayTraversal(nums) { - let count = 0; - // 循环次数与数组长度成正比 - for (let i = 0; i < nums.length; i++) { - count++; - } - return count; + let count = 0; + // 循环次数与数组长度成正比 + for (let i = 0; i < nums.length; i++) { + count++; + } + return count; } /* 平方阶 */ function quadratic(n) { - let count = 0; - // 循环次数与数组长度成平方关系 - for (let i = 0; i < n; i++) { - for (let j = 0; j < n; j++) { - count++; - } - } - return count; + let count = 0; + // 循环次数与数组长度成平方关系 + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; } /* 平方阶(冒泡排序) */ function bubbleSort(nums) { - let count = 0; // 计数器 - // 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for (let i = nums.length - 1; i > 0; i--) { - // 内循环:冒泡操作 - for (let j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - return count; + let count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (let i = nums.length - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; } /* 指数阶(循环实现) */ function exponential(n) { - let count = 0, - base = 1; - // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (let i = 0; i < n; i++) { - for (let j = 0; j < base; j++) { - count++; - } - base *= 2; - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count; + let count = 0, + base = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (let i = 0; i < n; i++) { + for (let j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; } /* 指数阶(递归实现) */ function expRecur(n) { - if (n == 1) return 1; - return expRecur(n - 1) + expRecur(n - 1) + 1; + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; } /* 对数阶(循环实现) */ function logarithmic(n) { - let count = 0; - while (n > 1) { - n = n / 2; - count++; - } - return count; + let count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; } /* 对数阶(递归实现) */ function logRecur(n) { - if (n <= 1) return 0; - return logRecur(n / 2) + 1; + if (n <= 1) return 0; + return logRecur(n / 2) + 1; } /* 线性对数阶 */ function linearLogRecur(n) { - if (n <= 1) return 1; - let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); - for (let i = 0; i < n; i++) { - count++; - } - return count; + if (n <= 1) return 1; + let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (let i = 0; i < n; i++) { + count++; + } + return count; } /* 阶乘阶(递归实现) */ function factorialRecur(n) { - if (n == 0) return 1; - let count = 0; - // 从 1 个分裂出 n 个 - for (let i = 0; i < n; i++) { - count += factorialRecur(n - 1); - } - return count; + if (n == 0) return 1; + let count = 0; + // 从 1 个分裂出 n 个 + for (let i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; } const n = 8; diff --git a/codes/typescript/chapter_computational_complexity/time_complexity.ts b/codes/typescript/chapter_computational_complexity/time_complexity.ts index 0b1b5be4c..900585c81 100644 --- a/codes/typescript/chapter_computational_complexity/time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -6,116 +6,116 @@ /* 常数阶 */ function constant(n: number): number { - let count = 0; - const size = 100000; - for (let i = 0; i < size; i++) count++; - return count; + let count = 0; + const size = 100000; + for (let i = 0; i < size; i++) count++; + return count; } /* 线性阶 */ function linear(n: number): number { - let count = 0; - for (let i = 0; i < n; i++) count++; - return count; + let count = 0; + for (let i = 0; i < n; i++) count++; + return count; } /* 线性阶(遍历数组) */ function arrayTraversal(nums: number[]) { - let count = 0; - // 循环次数与数组长度成正比 - for (let i = 0; i < nums.length; i++) { - count++; - } - return count; + let count = 0; + // 循环次数与数组长度成正比 + for (let i = 0; i < nums.length; i++) { + count++; + } + return count; } /* 平方阶 */ function quadratic(n: number): number { - let count = 0; - // 循环次数与数组长度成平方关系 - for (let i = 0; i < n; i++) { - for (let j = 0; j < n; j++) { - count++; - } - } - return count; + let count = 0; + // 循环次数与数组长度成平方关系 + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; } /* 平方阶(冒泡排序) */ function bubbleSort(nums: number[]): number { - let count = 0; // 计数器 - // 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for (let i = nums.length - 1; i > 0; i--) { - // 内循环:冒泡操作 - for (let j = 0; j < i; j++) { - if (nums[j] > nums[j + 1]) { - // 交换 nums[j] 与 nums[j + 1] - let tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 - } - } - } - return count; + let count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (let i = nums.length - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; } /* 指数阶(循环实现) */ function exponential(n: number): number { - let count = 0, - base = 1; - // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (let i = 0; i < n; i++) { - for (let j = 0; j < base; j++) { - count++; - } - base *= 2; - } - // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 - return count; + let count = 0, + base = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (let i = 0; i < n; i++) { + for (let j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; } /* 指数阶(递归实现) */ function expRecur(n: number): number { - if (n == 1) return 1; - return expRecur(n - 1) + expRecur(n - 1) + 1; + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; } /* 对数阶(循环实现) */ function logarithmic(n: number): number { - let count = 0; - while (n > 1) { - n = n / 2; - count++; - } - return count; + let count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; } /* 对数阶(递归实现) */ function logRecur(n: number): number { - if (n <= 1) return 0; - return logRecur(n / 2) + 1; + if (n <= 1) return 0; + return logRecur(n / 2) + 1; } /* 线性对数阶 */ function linearLogRecur(n: number): number { - if (n <= 1) return 1; - let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); - for (let i = 0; i < n; i++) { - count++; - } - return count; + if (n <= 1) return 1; + let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (let i = 0; i < n; i++) { + count++; + } + return count; } /* 阶乘阶(递归实现) */ function factorialRecur(n: number): number { - if (n == 0) return 1; - let count = 0; - // 从 1 个分裂出 n 个 - for (let i = 0; i < n; i++) { - count += factorialRecur(n - 1); - } - return count; + if (n == 0) return 1; + let count = 0; + // 从 1 个分裂出 n 个 + for (let i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; } var n = 8; From 6e8954672f52fa3a0fb801868212905e5dfc9d41 Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Mon, 2 Jan 2023 21:40:19 +0800 Subject: [PATCH 071/158] feat: add .gitignore file for Swift --- codes/swift/.gitignore | 130 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 codes/swift/.gitignore diff --git a/codes/swift/.gitignore b/codes/swift/.gitignore new file mode 100644 index 000000000..6295af4cc --- /dev/null +++ b/codes/swift/.gitignore @@ -0,0 +1,130 @@ +# Created by https://www.toptal.com/developers/gitignore/api/objective-c,swift,swiftpackagemanager +# Edit at https://www.toptal.com/developers/gitignore?templates=objective-c,swift,swiftpackagemanager + +### Objective-C ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +# CocoaPods +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# fastlane +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ + +### Objective-C Patch ### + +### Swift ### +# Xcode +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + + + + + + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + + +# Code Injection +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + + +### SwiftPackageManager ### +Packages +xcuserdata +*.xcodeproj + + +# End of https://www.toptal.com/developers/gitignore/api/objective-c,swift,swiftpackagemanager From 2890771d93266832c4a1341a25ed6b1b6878336e Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 2 Jan 2023 21:52:40 +0800 Subject: [PATCH 072/158] Update stack.md --- docs/chapter_stack_and_queue/stack.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index 7e35fddbd..c982f5a75 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -6,7 +6,9 @@ comments: true 「栈 Stack」是一种遵循「先入后出 first in, last out」数据操作规则的线性数据结构。我们可以将栈类比为放在桌面上的一摞盘子,如果需要拿出底部的盘子,则需要先将上面的盘子依次取出。 -我们将顶部盘子称为「栈顶」,底部盘子称为「栈底」,将把元素添加到栈顶的操作称为「入栈」,将删除栈顶元素的操作称为「出栈」。 +“盘子”是一种形象比喻,我们将盘子替换为任意一种元素(例如整数、字符、对象等),就得到了栈数据结构。 + +我们将这一摞元素的顶部称为「栈顶」,将底部称为「栈底」,将把元素添加到栈顶的操作称为「入栈」,将删除栈顶元素的操作称为「出栈」。 ![stack_operations](stack.assets/stack_operations.png) From 92c8d34f13ad2b6e1359b41f27d2a5c31ae6a3ff Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 21:53:21 +0800 Subject: [PATCH 073/158] lint: remove extra indent --- .../time_complexity.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 2ba703457..bd7cb119b 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -148,9 +148,9 @@ $$ “时间增长趋势”这个概念比较抽象,我们借助一个例子来理解。设输入数据大小为 $n$ ,给定三个算法 `A` , `B` , `C` 。 -- 算法 `A` 只有 $1$ 个打印操作,算法运行时间不随着 $n$ 增大而增长。我们称此算法的时间复杂度为「常数阶」。 -- 算法 `B` 中的打印操作需要循环 $n$ 次,算法运行时间随着 $n$ 增大成线性增长。此算法的时间复杂度被称为「线性阶」。 -- 算法 `C` 中的打印操作需要循环 $1000000$ 次,但运行时间仍与输入数据大小 $n$ 无关。因此 `C` 的时间复杂度和 `A` 相同,仍为「常数阶」。 +- 算法 `A` 只有 $1$ 个打印操作,算法运行时间不随着 $n$ 增大而增长。我们称此算法的时间复杂度为「常数阶」。 +- 算法 `B` 中的打印操作需要循环 $n$ 次,算法运行时间随着 $n$ 增大成线性增长。此算法的时间复杂度被称为「线性阶」。 +- 算法 `C` 中的打印操作需要循环 $1000000$ 次,但运行时间仍与输入数据大小 $n$ 无关。因此 `C` 的时间复杂度和 `A` 相同,仍为「常数阶」。 === "Java" @@ -1983,8 +1983,8 @@ $$ **某些算法的时间复杂度不是恒定的,而是与输入数据的分布有关。** 举一个例子,输入一个长度为 $n$ 数组 `nums` ,其中 `nums` 由从 $1$ 至 $n$ 的数字组成,但元素顺序是随机打乱的;算法的任务是返回元素 $1$ 的索引。我们可以得出以下结论: -- 当 `nums = [?, ?, ..., 1]`,即当末尾元素是 $1$ 时,则需完整遍历数组,此时达到 **最差时间复杂度 $O(n)$** ; -- 当 `nums = [1, ?, ?, ...]` ,即当首个数字为 $1$ 时,无论数组多长都不需要继续遍历,此时达到 **最佳时间复杂度 $\Omega(1)$** ; +- 当 `nums = [?, ?, ..., 1]`,即当末尾元素是 $1$ 时,则需完整遍历数组,此时达到 **最差时间复杂度 $O(n)$** ; +- 当 `nums = [1, ?, ?, ...]` ,即当首个数字为 $1$ 时,无论数组多长都不需要继续遍历,此时达到 **最佳时间复杂度 $\Omega(1)$** ; 「函数渐近上界」使用大 $O$ 记号表示,代表「最差时间复杂度」。与之对应,「函数渐近下界」用 $\Omega$ 记号(Omega Notation)来表示,代表「最佳时间复杂度」。 From 23b4aa118b413d72dfdbeeeaa766ee2795e84717 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Mon, 2 Jan 2023 22:07:48 +0800 Subject: [PATCH 074/158] code: added scripts to the doc --- .../time_complexity.md | 229 ++++++++++++++++-- 1 file changed, 205 insertions(+), 24 deletions(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index bd7cb119b..4328dc6b3 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -416,7 +416,6 @@ $$ } } - ``` === "TypeScript" @@ -432,7 +431,6 @@ $$ } } - ``` === "C" @@ -609,7 +607,6 @@ $$ } } } - ``` === "TypeScript" @@ -629,7 +626,6 @@ $$ } } } - ``` === "C" @@ -778,13 +774,24 @@ $$ === "JavaScript" ```js title="time_complexity.js" - + /* 常数阶 */ + function constant(n) { + let count = 0; + const size = 100000; + for (let i = 0; i < size; i++) count++; + return count; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" - + function constant(n: number): number { + let count = 0; + const size = 100000; + for (let i = 0; i < size; i++) count++; + return count; + } ``` === "C" @@ -876,13 +883,23 @@ $$ === "JavaScript" ```js title="time_complexity.js" - + /* 线性阶 */ + function linear(n) { + let count = 0; + for (let i = 0; i < n; i++) count++; + return count; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" - + /* 线性阶 */ + function linear(n: number): number { + let count = 0; + for (let i = 0; i < n; i++) count++; + return count; + } ``` === "C" @@ -980,13 +997,29 @@ $$ === "JavaScript" ```js title="time_complexity.js" - + /* 线性阶(遍历数组) */ + function arrayTraversal(nums) { + let count = 0; + // 循环次数与数组长度成正比 + for (let i = 0; i < nums.length; i++) { + count++; + } + return count; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" - + /* 线性阶(遍历数组) */ + function arrayTraversal(nums: number[]) { + let count = 0; + // 循环次数与数组长度成正比 + for (let i = 0; i < nums.length; i++) { + count++; + } + return count; + } ``` === "C" @@ -1093,13 +1126,33 @@ $$ === "JavaScript" ```js title="time_complexity.js" - + /* 平方阶 */ + function quadratic(n) { + let count = 0; + // 循环次数与数组长度成平方关系 + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" - + /* 平方阶 */ + function quadratic(n: number): number { + let count = 0; + // 循环次数与数组长度成平方关系 + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "C" @@ -1244,13 +1297,47 @@ $$ === "JavaScript" ```js title="time_complexity.js" - + /* 平方阶(冒泡排序) */ + function bubbleSort(nums) { + let count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (let i = nums.length - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" - + /* 平方阶(冒泡排序) */ + function bubbleSort(nums: number[]): number { + let count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (let i = nums.length - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; + } ``` === "C" @@ -1390,13 +1477,40 @@ $$ === "JavaScript" ```js title="time_complexity.js" + /* 指数阶(循环实现) */ + function exponential(n) { + let count = 0, + base = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (let i = 0; i < n; i++) { + for (let j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" - + /* 指数阶(循环实现) */ + function exponential(n: number): number { + let count = 0, + base = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (let i = 0; i < n; i++) { + for (let j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; + } ``` === "C" @@ -1495,12 +1609,21 @@ $$ === "JavaScript" ```js title="time_complexity.js" - + /* 指数阶(递归实现) */ + function expRecur(n) { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" + /* 指数阶(递归实现) */ + function expRecur(n: number): number { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` @@ -1598,13 +1721,29 @@ $$ === "JavaScript" ```js title="time_complexity.js" - + /* 对数阶(循环实现) */ + function logarithmic(n) { + let count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" - + /* 对数阶(循环实现) */ + function logarithmic(n: number): number { + let count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "C" @@ -1694,13 +1833,21 @@ $$ === "JavaScript" ```js title="time_complexity.js" - + /* 对数阶(递归实现) */ + function logRecur(n) { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" - + /* 对数阶(递归实现) */ + function logRecur(n: number): number { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; + } ``` === "C" @@ -1801,13 +1948,29 @@ $$ === "JavaScript" ```js title="time_complexity.js" - + /* 线性对数阶 */ + function linearLogRecur(n) { + if (n <= 1) return 1; + let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (let i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" - + /* 线性对数阶 */ + function linearLogRecur(n: number): number { + if (n <= 1) return 1; + let count = linearLogRecur(n / 2) + linearLogRecur(n / 2); + for (let i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "C" @@ -1926,13 +2089,31 @@ $$ === "JavaScript" ```js title="time_complexity.js" - + /* 阶乘阶(递归实现) */ + function factorialRecur(n) { + if (n == 0) return 1; + let count = 0; + // 从 1 个分裂出 n 个 + for (let i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; + } ``` === "TypeScript" ```typescript title="time_complexity.ts" - + /* 阶乘阶(递归实现) */ + function factorialRecur(n: number): number { + if (n == 0) return 1; + let count = 0; + // 从 1 个分裂出 n 个 + for (let i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; + } ``` === "C" From e850152130e89e66bc34621610797c2b0f0e7702 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Tue, 3 Jan 2023 03:40:01 +0800 Subject: [PATCH 075/158] Update the rotation cases of the AVL tree. --- .../avl_tree.assets/left_rotate.png | Bin 0 -> 78956 bytes .../left_rotate_with_grandchild.png | Bin 88169 -> 92532 bytes .../avl_tree.assets/right_rotate_step2.png | Bin 56856 -> 56463 bytes .../avl_tree.assets/right_rotate_step3.png | Bin 60764 -> 61266 bytes .../avl_tree.assets/right_rotate_step4.png | Bin 68870 -> 72098 bytes .../right_rotate_with_grandchild.png | Bin 91488 -> 93429 bytes docs/chapter_tree/avl_tree.md | 26 ++++++++++-------- 7 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 docs/chapter_tree/avl_tree.assets/left_rotate.png diff --git a/docs/chapter_tree/avl_tree.assets/left_rotate.png b/docs/chapter_tree/avl_tree.assets/left_rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..4c07196dce29db38cce0157cdde3bd4d7cb6b4f9 GIT binary patch literal 78956 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 Bdd~m= literal 88169 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

$5Q7q;{ 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

From 1b356ba0a2b0c67b549cee72fe9ea5dd191d24cd Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Tue, 3 Jan 2023 21:19:11 +0800 Subject: [PATCH 078/158] add c code for chapter_computational_complexity --- .../time_complexity.c | 192 ++++++++++++++++++ .../worst_best_time_complexity.c | 65 ++++++ 2 files changed, 257 insertions(+) create mode 100644 codes/c/chapter_computational_complexity/time_complexity.c create mode 100644 codes/c/chapter_computational_complexity/worst_best_time_complexity.c diff --git a/codes/c/chapter_computational_complexity/time_complexity.c b/codes/c/chapter_computational_complexity/time_complexity.c new file mode 100644 index 000000000..b7e6cef9c --- /dev/null +++ b/codes/c/chapter_computational_complexity/time_complexity.c @@ -0,0 +1,192 @@ +// File: time_complexity.c +// Created Time: 2023-01-03 +// Author: sjinzh (sjinzh@gmail.com) + +#include "stdio.h" +#include "stdlib.h" + +// 常数阶 +int constant(int n) +{ + int count = 0; + int size = 100000; + int i = 0; + for(int i = 0; i < size; i++){ + count ++; + } + return count; +} + +// 线性阶 +int linear(int n) +{ + int count = 0; + for(int i = 0; i < n; i++){ + count ++; + } + return count; +} + +// 线性阶(遍历数组) +int arrayTraversal(int *nums, int n) +{ + int count = 0; + // 循环次数与数组长度成正比 + for(int i = 0; i < n; i++){ + count ++; + } + return count; +} + +// 平方阶 +int quadratic(int n) +{ + int count = 0; + // 循环次数与数组长度成平方关系 + for(int i = 0; i < n; i++){ + for(int j = 0; j < n; j++){ + count ++; + } + } + return count; +} + +// 平方阶(冒泡排序) +int bubbleSort(int *nums, int n) +{ + int count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for(int i = n - 1; i > 0; i--){ + // 内循环:冒泡操作 + for (int j = 0; j < i; j++) + { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + + } + return count; +} + +// 指数阶(循环实现) +int exponential(int n) +{ + int count = 0; + int bas = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (int i = 0; i < n; i++) + { + for (int j = 0; j < bas; j++) + { + count++; + } + bas *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} + +// 指数阶(递归实现) +int expRecur(int n) +{ + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} + +// 对数阶(循环实现) +int logarithmic(float n) +{ + int count = 0; + while (n > 1) + { + n = n / 2; + count++; + } + return count; +} + +// 对数阶(递归实现) +int logRecur(float n) +{ + if (n <= 1) return 0; + return logRecur(n / 2) + 1; +} + +// 线性对数阶 +int linearLogRecur(float n) +{ + if (n <= 1) return 1; + int count = linearLogRecur(n / 2) + + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) + { + count ++; + } + return count; +} + +// 阶乘阶(递归实现) +int factorialRecur(int n) +{ + if (n == 0) return 1; + int count = 0; + for (int i = 0; i < n; i++) + { + count += factorialRecur(n - 1); + } + return count; +} + +// Driver Code +int main(int argc, char *argv[]) + { + // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 + int n = 8; + printf("输入数据大小 n = %d\n", n); + + int count = constant(n); + printf("常数阶的计算操作数量 = %d\n", count); + + count = linear(n); + printf("线性阶的计算操作数量 = %d\n", count); + // 分配堆区内存(创建一维可变长数组:数组中元素数量为n,元素类型为int) + int *nums = (int *)malloc(n * sizeof(int)); + count = arrayTraversal(nums, n); + printf("线性阶(遍历数组)的计算操作数量 = %d\n", count); + + count = quadratic(n); + printf("平方阶的计算操作数量 = %d\n", count); + for(int i = 0; i < n; i++){ + nums[i] = n - i; // [n,n-1,...,2,1] + } + count = bubbleSort(nums, n); + printf("平方阶(冒泡排序)的计算操作数量 = %d\n", count); + + count = exponential(n); + printf("指数阶(循环实现)的计算操作数量 = %d\n", count); + count = expRecur(n); + printf("指数阶(递归实现)的计算操作数量 = %d\n", count); + + count = logarithmic(n); + printf("对数阶(循环实现)的计算操作数量 = %d\n", count); + count = logRecur(n); + printf("对数阶(递归实现)的计算操作数量 = %d\n", count); + + count = linearLogRecur(n); + printf("线性对数阶(递归实现)的计算操作数量 = %d\n", count); + + count = factorialRecur(n); + printf("阶乘阶(递归实现)的计算操作数量 = %d\n", count); + + // 释放堆区内存 + if(nums != NULL){ + free(nums); + nums = NULL; + } + getchar(); + return 0; +} + diff --git a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c new file mode 100644 index 000000000..11139630f --- /dev/null +++ b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c @@ -0,0 +1,65 @@ +// File: worst_best_time_complexity.c +// Created Time: 2023-01-03 +// Author: sjinzh (sjinzh@gmail.com) + +#include "stdio.h" +#include "stdlib.h" +#include "time.h" + +// 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 +int *randomNumbers(int n) +{ + // 分配堆区内存(创建一维可变长数组:数组中元素数量为n,元素类型为int) + int *nums = (int *)malloc(n * sizeof(int)); + // 生成数组 nums = { 1, 2, 3, ..., n } + for (int i = 0; i < n; i++) + { + nums[i] = i + 1; + } + // 随机打乱数组元素 + for (int i = n - 1; i > 0; i--) + { + int j = rand() % (i + 1); + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + return nums; +} + +// 查找数组 nums 中数字 1 所在索引 +int findOne(int *nums, int n) +{ + for (int i = 0; i < n; i++) + { + if (nums[i] == 1) return i; + } + return -1; +} + +// Driver Code +int main(int argc, char *argv[]) +{ + // 初始化随机数种子 + srand((unsigned int)time(NULL)); + for (int i = 0; i < 10; i++) + { + int n = 100; + int *nums = randomNumbers(n); + int index = findOne(nums, n); + printf("\n数组 [ 1, 2, ..., n ] 被打乱后 = "); + for (int j = 0; j < n; j++) + { + printf("%d%s", nums[j], (j == n-1)? "" : "," ); + } + printf("\n数字 1 的索引为 %d\n", index); + // 释放堆区内存 + if(nums != NULL){ + free(nums); + nums = NULL; + } + } + getchar(); + return 0; +} + From 377200a39a696974284c1f33a28dddf67945a817 Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Mon, 2 Jan 2023 21:48:32 +0800 Subject: [PATCH 079/158] refactor: use Package.swift to define executable task --- codes/swift/Package.swift | 18 +++ .../space_complexity.swift | 110 +++--------------- .../time_complexity.swift | 59 +++++----- .../worst_best_time_complexity.swift | 21 ++-- codes/swift/utils/ListNode.swift | 14 +++ codes/swift/utils/PrintUtil.swift | 61 ++++++++++ codes/swift/utils/TreeNode.swift | 17 +++ 7 files changed, 169 insertions(+), 131 deletions(-) create mode 100644 codes/swift/Package.swift create mode 100644 codes/swift/utils/ListNode.swift create mode 100644 codes/swift/utils/PrintUtil.swift create mode 100644 codes/swift/utils/TreeNode.swift diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift new file mode 100644 index 000000000..250c09af1 --- /dev/null +++ b/codes/swift/Package.swift @@ -0,0 +1,18 @@ +// swift-tools-version: 5.7 + +import PackageDescription + +let package = Package( + name: "HelloAlgo", + products: [ + .executable(name: "time_complexity", targets: ["time_complexity"]), + .executable(name: "worst_best_time_complexity", targets: ["worst_best_time_complexity"]), + .executable(name: "space_complexity", targets: ["space_complexity"]), + ], + targets: [ + .target(name: "utils", path: "utils"), + .executableTarget(name: "time_complexity", path: "chapter_computational_complexity", sources: ["time_complexity.swift"]), + .executableTarget(name: "worst_best_time_complexity", path: "chapter_computational_complexity", sources: ["worst_best_time_complexity.swift"]), + .executableTarget(name: "space_complexity", dependencies: ["utils"], path: "chapter_computational_complexity", sources: ["space_complexity.swift"]), + ] +) diff --git a/codes/swift/chapter_computational_complexity/space_complexity.swift b/codes/swift/chapter_computational_complexity/space_complexity.swift index 7326c82b4..4931597ca 100644 --- a/codes/swift/chapter_computational_complexity/space_complexity.swift +++ b/codes/swift/chapter_computational_complexity/space_complexity.swift @@ -4,82 +4,7 @@ * Author: nuomi1 (nuomi1@qq.com) */ -class ListNode { - var val: Int - var next: ListNode? - - init(x: Int) { - val = x - } -} - -class TreeNode { - var val: Int // 结点值 - var height: Int // 结点高度 - var left: TreeNode? // 左子结点引用 - var right: TreeNode? // 右子结点引用 - - init(x: Int) { - val = x - height = 0 - } -} - -enum PrintUtil { - private class Trunk { - var prev: Trunk? - var str: String - - init(prev: Trunk?, str: String) { - self.prev = prev - self.str = str - } - } - - static func printTree(root: TreeNode?) { - printTree(root: root, prev: nil, isLeft: false) - } - - private static func printTree(root: TreeNode?, prev: Trunk?, isLeft: Bool) { - if root == nil { - return - } - - var prevStr = " " - let trunk = Trunk(prev: prev, str: prevStr) - - printTree(root: root?.right, prev: trunk, isLeft: true) - - if prev == nil { - trunk.str = "———" - } else if isLeft { - trunk.str = "/———" - prevStr = " |" - } else { - trunk.str = "\\———" - prev?.str = prevStr - } - - showTrunks(p: trunk) - print(" \(root!.val)") - - if prev != nil { - prev?.str = prevStr - } - trunk.str = " |" - - printTree(root: root?.left, prev: trunk, isLeft: false) - } - - private static func showTrunks(p: Trunk?) { - if p == nil { - return - } - - showTrunks(p: p?.prev) - print(p!.str, terminator: "") - } -} +import utils // 函数 @discardableResult @@ -153,20 +78,21 @@ func buildTree(n: Int) -> TreeNode? { return root } -// Driver Code -func main() { - let n = 5 - // 常数阶 - constant(n: n) - // 线性阶 - linear(n: n) - linearRecur(n: n) - // 平方阶 - quadratic(n: n) - quadraticRecur(n: n) - // 指数阶 - let root = buildTree(n: n) - PrintUtil.printTree(root: root) +@main +enum SpaceComplexity { + // Driver Code + static func main() { + let n = 5 + // 常数阶 + constant(n: n) + // 线性阶 + linear(n: n) + linearRecur(n: n) + // 平方阶 + quadratic(n: n) + quadraticRecur(n: n) + // 指数阶 + let root = buildTree(n: n) + PrintUtil.printTree(root: root) + } } - -main() diff --git a/codes/swift/chapter_computational_complexity/time_complexity.swift b/codes/swift/chapter_computational_complexity/time_complexity.swift index 38bac4fae..322b4f09d 100644 --- a/codes/swift/chapter_computational_complexity/time_complexity.swift +++ b/codes/swift/chapter_computational_complexity/time_complexity.swift @@ -131,40 +131,41 @@ func factorialRecur(n: Int) -> Int { return count } -func main() { - // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 - let n = 8 - print("输入数据大小 n =", n) +@main +enum TimeComplexity { + static func main() { + // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 + let n = 8 + print("输入数据大小 n =", n) - var count = constant(n: n) - print("常数阶的计算操作数量 =", count) + var count = constant(n: n) + print("常数阶的计算操作数量 =", count) - count = linear(n: n) - print("线性阶的计算操作数量 =", count) - count = arrayTraversal(nums: Array(repeating: 0, count: n)) - print("线性阶(遍历数组)的计算操作数量 =", count) + count = linear(n: n) + print("线性阶的计算操作数量 =", count) + count = arrayTraversal(nums: Array(repeating: 0, count: n)) + print("线性阶(遍历数组)的计算操作数量 =", count) - count = quadratic(n: n) - print("平方阶的计算操作数量 =", count) - var nums = Array(sequence(first: n, next: { $0 > 0 ? $0 - 1 : nil })) // [n,n-1,...,2,1] - count = bubbleSort(nums: &nums) - print("平方阶(冒泡排序)的计算操作数量 =", count) + count = quadratic(n: n) + print("平方阶的计算操作数量 =", count) + var nums = Array(sequence(first: n, next: { $0 > 0 ? $0 - 1 : nil })) // [n,n-1,...,2,1] + count = bubbleSort(nums: &nums) + print("平方阶(冒泡排序)的计算操作数量 =", count) - count = exponential(n: n) - print("指数阶(循环实现)的计算操作数量 =", count) - count = expRecur(n: n) - print("指数阶(递归实现)的计算操作数量 =", count) + count = exponential(n: n) + print("指数阶(循环实现)的计算操作数量 =", count) + count = expRecur(n: n) + print("指数阶(递归实现)的计算操作数量 =", count) - count = logarithmic(n: n) - print("对数阶(循环实现)的计算操作数量 =", count) - count = logRecur(n: n) - print("对数阶(递归实现)的计算操作数量 =", count) + count = logarithmic(n: n) + print("对数阶(循环实现)的计算操作数量 =", count) + count = logRecur(n: n) + print("对数阶(递归实现)的计算操作数量 =", count) - count = linearLogRecur(n: Double(n)) - print("线性对数阶(递归实现)的计算操作数量 =", count) + count = linearLogRecur(n: Double(n)) + print("线性对数阶(递归实现)的计算操作数量 =", count) - count = factorialRecur(n: n) - print("阶乘阶(递归实现)的计算操作数量 =", count) + count = factorialRecur(n: n) + print("阶乘阶(递归实现)的计算操作数量 =", count) + } } - -main() diff --git a/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift b/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift index 73db6954d..4fa2cf2a0 100644 --- a/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift +++ b/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift @@ -23,15 +23,16 @@ func findOne(nums: [Int]) -> Int { return -1 } -// Driver Code -func main() { - for _ in 0 ..< 10 { - let n = 100 - let nums = randomNumbers(n: n) - let index = findOne(nums: nums) - print("数组 [ 1, 2, ..., n ] 被打乱后 =", nums) - print("数字 1 的索引为", index) +@main +enum WorstBestTimeComplexity { + // Driver Code + static func main() { + for _ in 0 ..< 10 { + let n = 100 + let nums = randomNumbers(n: n) + let index = findOne(nums: nums) + print("数组 [ 1, 2, ..., n ] 被打乱后 =", nums) + print("数字 1 的索引为", index) + } } } - -main() diff --git a/codes/swift/utils/ListNode.swift b/codes/swift/utils/ListNode.swift new file mode 100644 index 000000000..796ee5125 --- /dev/null +++ b/codes/swift/utils/ListNode.swift @@ -0,0 +1,14 @@ +/* + * File: ListNode.swift + * Created Time: 2023-01-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +public class ListNode { + public var val: Int // 结点值 + public var next: ListNode? // 后继结点引用 + + public init(x: Int) { + val = x + } +} diff --git a/codes/swift/utils/PrintUtil.swift b/codes/swift/utils/PrintUtil.swift new file mode 100644 index 000000000..3f3fc2688 --- /dev/null +++ b/codes/swift/utils/PrintUtil.swift @@ -0,0 +1,61 @@ +/* + * File: PrintUtil.swift + * Created Time: 2023-01-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +public enum PrintUtil { + private class Trunk { + var prev: Trunk? + var str: String + + init(prev: Trunk?, str: String) { + self.prev = prev + self.str = str + } + } + + public static func printTree(root: TreeNode?) { + printTree(root: root, prev: nil, isLeft: false) + } + + private static func printTree(root: TreeNode?, prev: Trunk?, isLeft: Bool) { + if root == nil { + return + } + + var prevStr = " " + let trunk = Trunk(prev: prev, str: prevStr) + + printTree(root: root?.right, prev: trunk, isLeft: true) + + if prev == nil { + trunk.str = "———" + } else if isLeft { + trunk.str = "/———" + prevStr = " |" + } else { + trunk.str = "\\———" + prev?.str = prevStr + } + + showTrunks(p: trunk) + print(" \(root!.val)") + + if prev != nil { + prev?.str = prevStr + } + trunk.str = " |" + + printTree(root: root?.left, prev: trunk, isLeft: false) + } + + private static func showTrunks(p: Trunk?) { + if p == nil { + return + } + + showTrunks(p: p?.prev) + print(p!.str, terminator: "") + } +} diff --git a/codes/swift/utils/TreeNode.swift b/codes/swift/utils/TreeNode.swift new file mode 100644 index 000000000..b6ce30575 --- /dev/null +++ b/codes/swift/utils/TreeNode.swift @@ -0,0 +1,17 @@ +/* + * File: TreeNode.swift + * Created Time: 2023-01-02 + * Author: nuomi1 (nuomi1@qq.com) + */ + +public class TreeNode { + public var val: Int // 结点值 + public var height: Int // 结点高度 + public var left: TreeNode? // 左子结点引用 + public var right: TreeNode? // 右子结点引用 + + public init(x: Int) { + val = x + height = 0 + } +} From 2069dddd3b1c8a5271d269f853c1e3f44e153962 Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Tue, 3 Jan 2023 22:24:50 +0800 Subject: [PATCH 080/158] feat: add Swift codes for space time tradeoff article --- codes/swift/Package.swift | 2 + .../leetcode_two_sum.swift | 46 +++++++++++++++++++ .../space_time_tradeoff.md | 33 +++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 codes/swift/chapter_computational_complexity/leetcode_two_sum.swift diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index 250c09af1..a292d9fd3 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -8,11 +8,13 @@ let package = Package( .executable(name: "time_complexity", targets: ["time_complexity"]), .executable(name: "worst_best_time_complexity", targets: ["worst_best_time_complexity"]), .executable(name: "space_complexity", targets: ["space_complexity"]), + .executable(name: "leetcode_two_sum", targets: ["leetcode_two_sum"]), ], targets: [ .target(name: "utils", path: "utils"), .executableTarget(name: "time_complexity", path: "chapter_computational_complexity", sources: ["time_complexity.swift"]), .executableTarget(name: "worst_best_time_complexity", path: "chapter_computational_complexity", sources: ["worst_best_time_complexity.swift"]), .executableTarget(name: "space_complexity", dependencies: ["utils"], path: "chapter_computational_complexity", sources: ["space_complexity.swift"]), + .executableTarget(name: "leetcode_two_sum", path: "chapter_computational_complexity", sources: ["leetcode_two_sum.swift"]), ] ) diff --git a/codes/swift/chapter_computational_complexity/leetcode_two_sum.swift b/codes/swift/chapter_computational_complexity/leetcode_two_sum.swift new file mode 100644 index 000000000..6ed0e580c --- /dev/null +++ b/codes/swift/chapter_computational_complexity/leetcode_two_sum.swift @@ -0,0 +1,46 @@ +/* + * File: leetcode_two_sum.swift + * Created Time: 2023-01-03 + * Author: nuomi1 (nuomi1@qq.com) + */ + +func twoSumBruteForce(nums: [Int], target: Int) -> [Int] { + // 两层循环,时间复杂度 O(n^2) + for i in nums.indices.dropLast() { + for j in nums.indices.dropFirst(i + 1) { + if nums[i] + nums[j] == target { + return [i, j] + } + } + } + return [0] +} + +func twoSumHashTable(nums: [Int], target: Int) -> [Int] { + // 辅助哈希表,空间复杂度 O(n) + var dic: [Int: Int] = [:] + // 单层循环,时间复杂度 O(n) + for i in nums.indices { + if let j = dic[target - nums[i]] { + return [j, i] + } + dic[nums[i]] = i + } + return [0] +} + +@main +enum LeetcodeTwoSum { + static func main() { + // ======= Test Case ======= + let nums = [2, 7, 11, 15] + let target = 9 + // ====== Driver Code ====== + // 方法一 + var res = twoSumBruteForce(nums: nums, target: target) + print("方法一 res = \(res)") + // 方法二 + res = twoSumHashTable(nums: nums, target: target) + print("方法二 res = \(res)") + } +} diff --git a/docs/chapter_computational_complexity/space_time_tradeoff.md b/docs/chapter_computational_complexity/space_time_tradeoff.md index 651aff148..0b86c11ca 100644 --- a/docs/chapter_computational_complexity/space_time_tradeoff.md +++ b/docs/chapter_computational_complexity/space_time_tradeoff.md @@ -149,6 +149,22 @@ comments: true } ``` +=== "Swift" + + ```swift title="leetcode_two_sum.swift" + func twoSumBruteForce(nums: [Int], target: Int) -> [Int] { + // 两层循环,时间复杂度 O(n^2) + for i in nums.indices.dropLast() { + for j in nums.indices.dropFirst(i + 1) { + if nums[i] + nums[j] == target { + return [i, j] + } + } + } + return [0] + } + ``` + ### 方法二:辅助哈希表 时间复杂度 $O(N)$ ,空间复杂度 $O(N)$ ,属于「空间换时间」。 @@ -294,3 +310,20 @@ comments: true } } ``` + +=== "Swift" + + ```swift title="leetcode_two_sum.swift" + func twoSumHashTable(nums: [Int], target: Int) -> [Int] { + // 辅助哈希表,空间复杂度 O(n) + var dic: [Int: Int] = [:] + // 单层循环,时间复杂度 O(n) + for i in nums.indices { + if let j = dic[target - nums[i]] { + return [j, i] + } + dic[nums[i]] = i + } + return [0] + } + ``` From 9e4a5fd6d897bb70e049758ef3ccb975e96c30a8 Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Wed, 4 Jan 2023 08:09:22 +0800 Subject: [PATCH 081/158] update c code for chapter_computational_complexity, add c code to time_complexity.md --- .../time_complexity.c | 35 +-- .../worst_best_time_complexity.c | 18 +- codes/c/include/include.h | 4 +- .../time_complexity.md | 249 ++++++++++++++++-- 4 files changed, 264 insertions(+), 42 deletions(-) diff --git a/codes/c/chapter_computational_complexity/time_complexity.c b/codes/c/chapter_computational_complexity/time_complexity.c index b7e6cef9c..4355ea3ad 100644 --- a/codes/c/chapter_computational_complexity/time_complexity.c +++ b/codes/c/chapter_computational_complexity/time_complexity.c @@ -1,11 +1,12 @@ -// File: time_complexity.c -// Created Time: 2023-01-03 -// Author: sjinzh (sjinzh@gmail.com) +/* + * File: time_complexity.c + * Created Time: 2023-01-03 + * Author: sjinzh (sjinzh@gmail.com) + */ -#include "stdio.h" -#include "stdlib.h" +#include "../include/include.h" -// 常数阶 +/* 常数阶 */ int constant(int n) { int count = 0; @@ -17,7 +18,7 @@ int constant(int n) return count; } -// 线性阶 +/* 线性阶 */ int linear(int n) { int count = 0; @@ -27,7 +28,7 @@ int linear(int n) return count; } -// 线性阶(遍历数组) +/* 线性阶(遍历数组) */ int arrayTraversal(int *nums, int n) { int count = 0; @@ -38,7 +39,7 @@ int arrayTraversal(int *nums, int n) return count; } -// 平方阶 +/* 平方阶 */ int quadratic(int n) { int count = 0; @@ -51,7 +52,7 @@ int quadratic(int n) return count; } -// 平方阶(冒泡排序) +/* 平方阶(冒泡排序) */ int bubbleSort(int *nums, int n) { int count = 0; // 计数器 @@ -71,7 +72,7 @@ int bubbleSort(int *nums, int n) return count; } -// 指数阶(循环实现) +/* 指数阶(循环实现) */ int exponential(int n) { int count = 0; @@ -89,14 +90,14 @@ int exponential(int n) return count; } -// 指数阶(递归实现) +/* 指数阶(递归实现) */ int expRecur(int n) { if (n == 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } -// 对数阶(循环实现) +/* 对数阶(循环实现) */ int logarithmic(float n) { int count = 0; @@ -108,14 +109,14 @@ int logarithmic(float n) return count; } -// 对数阶(递归实现) +/* 对数阶(递归实现) */ int logRecur(float n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } -// 线性对数阶 +/* 线性对数阶 */ int linearLogRecur(float n) { if (n <= 1) return 1; @@ -128,7 +129,7 @@ int linearLogRecur(float n) return count; } -// 阶乘阶(递归实现) +/* 阶乘阶(递归实现) */ int factorialRecur(int n) { if (n == 0) return 1; @@ -140,7 +141,7 @@ int factorialRecur(int n) return count; } -// Driver Code +/* Driver Code */ int main(int argc, char *argv[]) { // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 diff --git a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c index 11139630f..fc37ae0f0 100644 --- a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c +++ b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c @@ -1,12 +1,12 @@ -// File: worst_best_time_complexity.c -// Created Time: 2023-01-03 -// Author: sjinzh (sjinzh@gmail.com) +/* + * File: worst_best_time_complexity.c + * Created Time: 2023-01-03 + * Author: sjinzh (sjinzh@gmail.com) + */ -#include "stdio.h" -#include "stdlib.h" -#include "time.h" +#include "../include/include.h" -// 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 +/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ int *randomNumbers(int n) { // 分配堆区内存(创建一维可变长数组:数组中元素数量为n,元素类型为int) @@ -27,7 +27,7 @@ int *randomNumbers(int n) return nums; } -// 查找数组 nums 中数字 1 所在索引 +/* 查找数组 nums 中数字 1 所在索引 */ int findOne(int *nums, int n) { for (int i = 0; i < n; i++) @@ -37,7 +37,7 @@ int findOne(int *nums, int n) return -1; } -// Driver Code +/* Driver Code */ int main(int argc, char *argv[]) { // 初始化随机数种子 diff --git a/codes/c/include/include.h b/codes/c/include/include.h index 44843254e..8ce104cd2 100644 --- a/codes/c/include/include.h +++ b/codes/c/include/include.h @@ -1,2 +1,4 @@ #include -#include \ No newline at end of file +#include +#include +#include \ No newline at end of file diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 539c9b5cb..5d61dc005 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -91,7 +91,17 @@ $$ === "C" ```c title="" - + // 在某运行平台下 + void algorithm(int n) + { + int a = 2; // 1 ns + a = a + 1; // 1 ns + a = a * 2; // 10 ns + // 循环 n 次 + for (int i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ + printf("%d", 0); // 5 ns + } + } ``` === "C#" @@ -232,7 +242,25 @@ $$ === "C" ```c title="" - + // 算法 A 时间复杂度:常数阶 + void algorithm_A(int n) + { + printf("%d", 0); + } + // 算法 B 时间复杂度:线性阶 + void algorithm_B(int n) + { + for (int i = 0; i < n; i++) { + printf("%d", 0); + } + } + // 算法 C 时间复杂度:常数阶 + void algorithm_C(int n) + { + for (int i = 0; i < 1000000; i++) { + printf("%d", 0); + } + } ``` === "C#" @@ -373,7 +401,16 @@ $$ === "C" ```c title="" - + void algorithm(int n) + { + int a = 1; // +1 + a = a + 1; // +1 + a = a * 2; // +1 + // 循环 n 次 + for (int i = 0; i < n; i++) { // +1(每轮都执行 i ++) + printf("%d", 0); // +1 + } + } ``` === "C#" @@ -542,7 +579,21 @@ $$ === "C" ```c title="" - + void algorithm(int n) + { + int a = 1; // +0(技巧 1) + a = a + n; // +0(技巧 1) + // +n(技巧 2) + for (int i = 0; i < 5 * n + 1; i++) { + printf("%d", 0); + } + // +n*n(技巧 3) + for (int i = 0; i < 2 * n; i++) { + for (int j = 0; j < n + 1; j++) { + printf("%d", 0); + } + } + } ``` === "C#" @@ -697,7 +748,17 @@ $$ === "C" ```c title="time_complexity.c" - + /* 常数阶 */ + int constant(int n) + { + int count = 0; + int size = 100000; + int i = 0; + for(int i = 0; i < size; i++){ + count ++; + } + return count; + } ``` === "C#" @@ -795,7 +856,15 @@ $$ === "C" ```c title="time_complexity.c" - + /* 线性阶 */ + int linear(int n) + { + int count = 0; + for(int i = 0; i < n; i++){ + count ++; + } + return count; + } ``` === "C#" @@ -899,7 +968,16 @@ $$ === "C" ```c title="time_complexity.c" - + /* 线性阶(遍历数组) */ + int arrayTraversal(int *nums, int n) + { + int count = 0; + // 循环次数与数组长度成正比 + for(int i = 0; i < n; i++){ + count ++; + } + return count; + } ``` === "C#" @@ -1012,7 +1090,18 @@ $$ === "C" ```c title="time_complexity.c" - + /* 平方阶 */ + int quadratic(int n) + { + int count = 0; + // 循环次数与数组长度成平方关系 + for(int i = 0; i < n; i++){ + for(int j = 0; j < n; j++){ + count ++; + } + } + return count; + } ``` === "C#" @@ -1163,7 +1252,25 @@ $$ === "C" ```c title="time_complexity.c" - + /* 平方阶(冒泡排序) */ + int bubbleSort(int *nums, int n) + { + int count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for(int i = n - 1; i > 0; i--){ + // 内循环:冒泡操作 + for (int j = 0; j < i; j++) + { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + + } + return count; + } ``` === "C#" @@ -1309,7 +1416,23 @@ $$ === "C" ```c title="time_complexity.c" - + /* 指数阶(循环实现) */ + int exponential(int n) + { + int count = 0; + int bas = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (int i = 0; i < n; i++) + { + for (int j = 0; j < bas; j++) + { + count++; + } + bas *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; + } ``` === "C#" @@ -1414,7 +1537,12 @@ $$ === "C" ```c title="time_complexity.c" - + /* 指数阶(递归实现) */ + int expRecur(int n) + { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "C#" @@ -1517,7 +1645,17 @@ $$ === "C" ```c title="time_complexity.c" - + /* 对数阶(循环实现) */ + int logarithmic(float n) + { + int count = 0; + while (n > 1) + { + n = n / 2; + count++; + } + return count; + } ``` === "C#" @@ -1613,7 +1751,12 @@ $$ === "C" ```c title="time_complexity.c" - + /* 对数阶(递归实现) */ + int logRecur(float n) + { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; + } ``` === "C#" @@ -1720,7 +1863,18 @@ $$ === "C" ```c title="time_complexity.c" - + /* 线性对数阶 */ + int linearLogRecur(float n) + { + if (n <= 1) return 1; + int count = linearLogRecur(n / 2) + + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) + { + count ++; + } + return count; + } ``` === "C#" @@ -1845,7 +1999,17 @@ $$ === "C" ```c title="time_complexity.c" - + /* 阶乘阶(递归实现) */ + int factorialRecur(int n) + { + if (n == 0) return 1; + int count = 0; + for (int i = 0; i < n; i++) + { + count += factorialRecur(n - 1); + } + return count; + } ``` === "C#" @@ -2061,7 +2225,62 @@ $$ === "C" ```c title="worst_best_time_complexity.c" + /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ + int *randomNumbers(int n) + { + // 分配堆区内存(创建一维可变长数组:数组中元素数量为n,元素类型为int) + int *nums = (int *)malloc(n * sizeof(int)); + // 生成数组 nums = { 1, 2, 3, ..., n } + for (int i = 0; i < n; i++) + { + nums[i] = i + 1; + } + // 随机打乱数组元素 + for (int i = n - 1; i > 0; i--) + { + int j = rand() % (i + 1); + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + return nums; + } + /* 查找数组 nums 中数字 1 所在索引 */ + int findOne(int *nums, int n) + { + for (int i = 0; i < n; i++) + { + if (nums[i] == 1) return i; + } + return -1; + } + + /* Driver Code */ + int main(int argc, char *argv[]) + { + // 初始化随机数种子 + srand((unsigned int)time(NULL)); + for (int i = 0; i < 10; i++) + { + int n = 100; + int *nums = randomNumbers(n); + int index = findOne(nums, n); + printf("\n数组 [ 1, 2, ..., n ] 被打乱后 = "); + for (int j = 0; j < n; j++) + { + printf("%d%s", nums[j], (j == n-1)? "" : "," ); + } + printf("\n数字 1 的索引为 %d\n", index); + // 释放堆区内存 + if(nums != NULL){ + free(nums); + nums = NULL; + } + } + getchar(); + return 0; + } ``` === "C#" From e0a3189f91c2af5c0d82d413ddd5c272594951a2 Mon Sep 17 00:00:00 2001 From: MolDuM <39506381+MolDuM@users.noreply.github.com> Date: Wed, 4 Jan 2023 14:56:16 +0800 Subject: [PATCH 082/158] Update array.c 3 changes: 1. In the enlarge part, I didn't change because I didn't find a good way to deal with the local variable and the clear definition. malloc is commonly used in LeetCode so I think it is not bad for a beginner. 2. I changed the initialization of the second array to make it in the same style as C++. 3. In the enlarge part in main, I deleted the code of pointer free to match the array operations. I also changed the operate array in the later part because the enlarged array cannot be assigned to the older array name. BTW, sorry for updating so late. Reading different version documents and book are really tiring and boring. --- codes/c/chapter_array_and_linkedlist/array.c | 30 ++++++-------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/codes/c/chapter_array_and_linkedlist/array.c b/codes/c/chapter_array_and_linkedlist/array.c index 6217a566f..300124a70 100644 --- a/codes/c/chapter_array_and_linkedlist/array.c +++ b/codes/c/chapter_array_and_linkedlist/array.c @@ -76,16 +76,7 @@ int main() { printf("数组 arr = "); printArray(arr, size); - int arr2[5] = { 1, 3, 2, 5, 4 }; - printf("数组 arr2 = "); - printArray(arr2, size); - - int* nums = (int *)malloc(sizeof(int) * size); - nums[0] = 1; - nums[1] = 3; - nums[2] = 2; - nums[3] = 5; - nums[4] = 4; + int nums[5] = { 1, 3, 2, 5, 4 }; printf("数组 nums = "); printArray(nums, size); @@ -96,29 +87,26 @@ int main() { /* 长度扩展 */ int enlarge = 3; int* res = extend(nums, size, enlarge); - int* temp = nums; - nums = res; - free(temp); size += enlarge; printf("将数组长度扩展至 8 ,得到 nums = "); - printArray(nums, size); + printArray(res, size); /* 插入元素 */ - insert(nums, size, 6, 3); + insert(res, size, 6, 3); printf("在索引 3 处插入数字 6 ,得到 nums = "); - printArray(nums, size); + printArray(res, size); /* 删除元素 */ - removeItem(nums, size, 2); + removeItem(res, size, 2); printf("删除索引 2 处的元素,得到 nums = "); - printArray(nums, size); + printArray(res, size); /* 遍历数组 */ - traverse(nums, size); + traverse(res, size); /* 查找元素 */ - int index = find(nums, size, 3); - printf("在 nums 中查找元素 3 ,得到索引 = %d\n", index); + int index = find(res, size, 3); + printf("在 res 中查找元素 3 ,得到索引 = %d\n", index); return 0; } From 57757943511c9fc4577bfd0aa72298dc1603fe85 Mon Sep 17 00:00:00 2001 From: moonache <476681765@qq.com> Date: Wed, 4 Jan 2023 19:27:35 +0800 Subject: [PATCH 083/158] =?UTF-8?q?=E9=94=99=E5=88=AB=E5=AD=97=EF=BC=9A?= =?UTF-8?q?=E4=BB=8E=E9=A1=B6=E7=BD=AE=E5=BA=95=20->=20=E4=BB=8E=E9=A1=B6?= =?UTF-8?q?=E8=87=B3=E5=BA=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/chapter_tree/binary_tree.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 0c858fd3a..3e3222222 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -120,7 +120,7 @@ comments: true - 「根结点 Root Node」:二叉树最顶层的结点,其没有父结点; - 「叶结点 Leaf Node」:没有子结点的结点,其两个指针都指向 $\text{null}$ ; -- 结点所处「层 Level」:从顶置底依次增加,根结点所处层为 1 ; +- 结点所处「层 Level」:从顶至底依次增加,根结点所处层为 1 ; - 结点「度 Degree」:结点的子结点数量。二叉树中,度的范围是 0, 1, 2 ; - 「边 Edge」:连接两个结点的边,即结点指针; - 二叉树「高度」:二叉树中根结点到最远叶结点走过边的数量; From b5c9db935e8e0904ced4e0e09369a8b3fda13c64 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:00:56 +0800 Subject: [PATCH 084/158] Update docs/chapter_computational_complexity/time_complexity.md Co-authored-by: Justin Tse --- docs/chapter_computational_complexity/time_complexity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 22dc3472a..8ef61d061 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -684,7 +684,7 @@ $$
-| 操作数量 $T(n)$ | 时间复杂度 $O(f(n))$ | +| 操作数量 $T(n)$ | 时间复杂度 $O(f(n))$ | | ---------------------- | -------------------- | | $100000$ | $O(1)$ | | $3n + 2$ | $O(n)$ | From abdf1f3117fded4a4721462b37ffb960da869841 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:01:03 +0800 Subject: [PATCH 085/158] Update codes/javascript/chapter_computational_complexity/time_complexity.js Co-authored-by: Justin Tse --- .../chapter_computational_complexity/time_complexity.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codes/javascript/chapter_computational_complexity/time_complexity.js b/codes/javascript/chapter_computational_complexity/time_complexity.js index c8809caa0..2e4688dd2 100644 --- a/codes/javascript/chapter_computational_complexity/time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/time_complexity.js @@ -118,6 +118,8 @@ function factorialRecur(n) { return count; } +/* Driver Code */ +// 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 const n = 8; console.log("输入数据大小 n = " + n); From c5a9eea0a94b1b5d6c184262bc49236b6489c0de Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:01:17 +0800 Subject: [PATCH 086/158] Update codes/typescript/chapter_computational_complexity/time_complexity.ts Co-authored-by: Justin Tse --- .../chapter_computational_complexity/time_complexity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codes/typescript/chapter_computational_complexity/time_complexity.ts b/codes/typescript/chapter_computational_complexity/time_complexity.ts index 900585c81..fbfb470e7 100644 --- a/codes/typescript/chapter_computational_complexity/time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -20,7 +20,7 @@ function linear(n: number): number { } /* 线性阶(遍历数组) */ -function arrayTraversal(nums: number[]) { +function arrayTraversal(nums: number[]): number { let count = 0; // 循环次数与数组长度成正比 for (let i = 0; i < nums.length; i++) { From a29a584b6f7b6ee3d94dbcdfa8132fdeb6178123 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:01:28 +0800 Subject: [PATCH 087/158] Update codes/typescript/chapter_computational_complexity/time_complexity.ts Co-authored-by: Justin Tse --- .../chapter_computational_complexity/time_complexity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codes/typescript/chapter_computational_complexity/time_complexity.ts b/codes/typescript/chapter_computational_complexity/time_complexity.ts index fbfb470e7..136e5e25f 100644 --- a/codes/typescript/chapter_computational_complexity/time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -121,7 +121,7 @@ function factorialRecur(n: number): number { var n = 8; console.log("输入数据大小 n = " + n); -var count = constant(n); +let count = constant(n); console.log("常数阶的计算操作数量 = " + count); count = linear(n); From d5969e407057a1a940aff1c7e0786aa6b66406fd Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:01:42 +0800 Subject: [PATCH 088/158] Update docs/chapter_computational_complexity/time_complexity.md Co-authored-by: Justin Tse --- docs/chapter_computational_complexity/time_complexity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 8ef61d061..5a44a17f1 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -1013,7 +1013,7 @@ $$ ```typescript title="time_complexity.ts" /* 线性阶(遍历数组) */ - function arrayTraversal(nums: number[]) { + function arrayTraversal(nums: number[]): number { let count = 0; // 循环次数与数组长度成正比 for (let i = 0; i < nums.length; i++) { From 3906c3df06e4bd33e38dcfcd9131a8f2413e25db Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:01:53 +0800 Subject: [PATCH 089/158] Update docs/chapter_computational_complexity/time_complexity.md Co-authored-by: Justin Tse --- docs/chapter_computational_complexity/time_complexity.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 5a44a17f1..e567718a5 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -601,8 +601,8 @@ $$ console.log(0); } // +n*n(技巧 3) - for (var i = 0; i < 2 * n; i++) { - for (var j = 0; j < n + 1; j++) { + for (let i = 0; i < 2 * n; i++) { + for (let j = 0; j < n + 1; j++) { console.log(0); } } From 9f7a9fde20a3cbca9cf5a647c23f46e9f9a04c5d Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:02:00 +0800 Subject: [PATCH 090/158] Update codes/typescript/chapter_computational_complexity/time_complexity.ts Co-authored-by: Justin Tse --- .../chapter_computational_complexity/time_complexity.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/codes/typescript/chapter_computational_complexity/time_complexity.ts b/codes/typescript/chapter_computational_complexity/time_complexity.ts index 136e5e25f..e5dde67c6 100644 --- a/codes/typescript/chapter_computational_complexity/time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/time_complexity.ts @@ -118,7 +118,9 @@ function factorialRecur(n: number): number { return count; } -var n = 8; +/* Driver Code */ +// 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 +const n = 8; console.log("输入数据大小 n = " + n); let count = constant(n); From 2acf85a6260a2c134aea77b29d7243f0854c2961 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:02:09 +0800 Subject: [PATCH 091/158] Update docs/chapter_computational_complexity/time_complexity.md Co-authored-by: Justin Tse --- docs/chapter_computational_complexity/time_complexity.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index e567718a5..b3dc3c35d 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -79,6 +79,7 @@ $$ === "JavaScript" ```js title="" + // 在某运行平台下 function algorithm(n) { var a = 2; // 1 ns a = a + 1; // 1 ns From 3fa04aeb4a2e7140292efbd2b24d9d3536ffbcd4 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:02:19 +0800 Subject: [PATCH 092/158] Update docs/chapter_computational_complexity/time_complexity.md Co-authored-by: Justin Tse --- docs/chapter_computational_complexity/time_complexity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index b3dc3c35d..b26fdf2bf 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -617,7 +617,7 @@ $$ var a: number = 1; // +0(技巧 1) a = a + n; // +0(技巧 1) // +n(技巧 2) - for (var i = 0; i < 5 * n + 1; i++) { + for (let i = 0; i < 5 * n + 1; i++) { console.log(0); } // +n*n(技巧 3) From 03aeda84e22d2237e17078213e23680f93fb9332 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:02:51 +0800 Subject: [PATCH 093/158] Update docs/chapter_computational_complexity/time_complexity.md Co-authored-by: Justin Tse --- docs/chapter_computational_complexity/time_complexity.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index b26fdf2bf..ee1f6f0ce 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -621,8 +621,8 @@ $$ console.log(0); } // +n*n(技巧 3) - for (var i = 0; i < 2 * n; i++) { - for (var j = 0; j < n + 1; j++) { + for (let i = 0; i < 2 * n; i++) { + for (let j = 0; j < n + 1; j++) { console.log(0); } } From 51004b8a85d2a9d2efa35237c58e57994c2a32f0 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:05:59 +0800 Subject: [PATCH 094/158] Apply suggestions from code review Co-authored-by: Justin Tse --- .../time_complexity.md | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index ee1f6f0ce..9dcb5d84b 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -84,7 +84,8 @@ $$ var a = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns - for(var i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ + // 循环 n 次 + for(let i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ console.log(0); // 5 ns } } @@ -93,11 +94,13 @@ $$ === "TypeScript" ```typescript title="" + // 在某运行平台下 function algorithm(n: number): void { var a: number = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns - for(var i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ + // 循环 n 次 + for(let i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ console.log(0); // 5 ns } } @@ -241,13 +244,13 @@ $$ } // 算法 B 时间复杂度:线性阶 function algorithm_B(n) { - for (var i = 0; i < n; i++) { + for (let i = 0; i < n; i++) { console.log(0); } } // 算法 C 时间复杂度:常数阶 function algorithm_C(n) { - for (var i = 0; i < 1000000; i++) { + for (let i = 0; i < 1000000; i++) { console.log(0); } } @@ -263,13 +266,13 @@ $$ } // 算法 B 时间复杂度:线性阶 function algorithm_B(n: number): void { - for (var i = 0; i < n; i++) { + for (let i = 0; i < n; i++) { console.log(0); } } // 算法 C 时间复杂度:常数阶 function algorithm_C(n: number): void { - for (var i = 0; i < 1000000; i++) { + for (let i = 0; i < 1000000; i++) { console.log(0); } } @@ -412,7 +415,7 @@ $$ a += 1; // +1 a *= 2; // +1 // 循环 n 次 - for(var i = 0; i < n; i++){ // +1 + for(let i = 0; i < n; i++){ // +1(每轮都执行 i ++) console.log(0); // +1 } @@ -427,7 +430,7 @@ $$ a += 1; // +1 a *= 2; // +1 // 循环 n 次 - for(var i = 0; i < n; i++){ // +1 + for(let i = 0; i < n; i++){ // +1(每轮都执行 i ++) console.log(0); // +1 } @@ -595,10 +598,10 @@ $$ ```js title="" function algorithm(n) { - var a = 1; // +0(技巧 1) + let a = 1; // +0(技巧 1) a = a + n; // +0(技巧 1) // +n(技巧 2) - for (var i = 0; i < 5 * n + 1; i++) { + for (let i = 0; i < 5 * n + 1; i++) { console.log(0); } // +n*n(技巧 3) @@ -614,7 +617,7 @@ $$ ```typescript title="" function algorithm(n: number): void { - var a: number = 1; // +0(技巧 1) + let a = 1; // +0(技巧 1) a = a + n; // +0(技巧 1) // +n(技巧 2) for (let i = 0; i < 5 * n + 1; i++) { From 4223cceda1552bba4b8d84e00a28a4eff7e14e9a Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:39:22 +0800 Subject: [PATCH 095/158] Update stack.md --- docs/chapter_preface/about_the_book.md | 2 +- docs/chapter_stack_and_queue/stack.md | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index bc79115a3..54cd2844c 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -86,7 +86,7 @@ comments: true - 文章中的重要名词会用「」符号标注,例如「数组 Array」。名词混淆会导致不必要的歧义,因此最好可以记住这类名词(包括中文和英文),以便后续阅读文献时使用。 - 重点内容、总起句、总结句会被 **加粗** ,此类文字值得特别关注。 - 专有名词和有特指含义的词句会使用 “ ” 标注,以避免歧义。 -- 在工程应用中,每种语言都有相应的注释规范;而本书放弃了一部分的注释规范性,以换取更加紧凑的内容排版。注释主要分为三种类型:标题注释、内容注释、多行注释。 +- 在工程应用中,每种语言都有注释规范;而本书放弃了一部分的注释规范性,以换取更加紧凑的内容排版。注释主要分为三种类型:标题注释、内容注释、多行注释。 === "Java" diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index c982f5a75..a8b06575b 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -236,7 +236,9 @@ comments: true ### 基于链表的实现 -使用「链表」实现栈时,将链表的尾结点看作栈顶即可。 +使用「链表」实现栈时,将链表的头结点看作栈顶,尾结点看作栈底。 + +对于入栈操作,将元素插入到链表头部即可,这种结点添加方式被称为“头插法”。而对于出栈操作,则将头结点从链表中删除即可。 受益于链表的离散存储方式,栈的扩容更加灵活,删除元素的内存也会被系统自动回收;缺点是无法像数组一样高效地随机访问,并且由于链表结点需存储指针,导致单个元素占用空间更大。 @@ -592,7 +594,7 @@ comments: true ### 基于数组的实现 -使用「数组」实现栈时,将数组的尾部当作栈顶。准确地说,我们需要使用「列表」,因为入栈的元素可能是源源不断的,因此使用动态数组可以方便扩容。 +使用「数组」实现栈时,将数组的尾部当作栈顶,这样可以保证入栈与出栈操作的时间复杂度都为 $O(1)$ 。准确地说,由于入栈的元素可能是源源不断的,我们需要使用可以动态扩容的「列表」。 基于数组实现的栈,优点是支持随机访问,缺点是会造成一定的空间浪费,因为列表的容量始终 $\geq$ 元素数量。 From 33023549095ae85eab68ab1c0e580a4ef14ad002 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:45:08 +0800 Subject: [PATCH 096/158] Update cpp codes for the stack and the queue. --- codes/cpp/chapter_stack_and_queue/array_queue.cpp | 7 +++---- codes/cpp/chapter_stack_and_queue/array_stack.cpp | 7 +++---- codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp | 7 +++---- codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp | 7 +++---- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/codes/cpp/chapter_stack_and_queue/array_queue.cpp b/codes/cpp/chapter_stack_and_queue/array_queue.cpp index 387ffd142..8e87da501 100644 --- a/codes/cpp/chapter_stack_and_queue/array_queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/array_queue.cpp @@ -50,11 +50,10 @@ public: } /* 出队 */ - int poll() { + void poll() { int num = peek(); // 队头指针向后移动一位,若越过尾部则返回到数组头部 front = (front + 1) % capacity(); - return num; } /* 访问队首元素 */ @@ -98,8 +97,8 @@ int main() { cout << "队首元素 peek = " << peek << endl; /* 元素出队 */ - int poll = queue->poll(); - cout << "出队元素 poll = " << poll << ",出队后 queue = "; + queue->poll(); + cout << "出队元素 poll = " << peek << ",出队后 queue = "; PrintUtil::printVector(queue->toVector()); /* 获取队列的长度 */ diff --git a/codes/cpp/chapter_stack_and_queue/array_stack.cpp b/codes/cpp/chapter_stack_and_queue/array_stack.cpp index 517a18a4d..e9ecae2a8 100644 --- a/codes/cpp/chapter_stack_and_queue/array_stack.cpp +++ b/codes/cpp/chapter_stack_and_queue/array_stack.cpp @@ -28,10 +28,9 @@ public: } /* 出栈 */ - int pop() { + void pop() { int oldTop = top(); stack.pop_back(); - return oldTop; } /* 访问栈顶元素 */ @@ -67,8 +66,8 @@ int main() { cout << "栈顶元素 top = " << top << endl; /* 元素出栈 */ - int pop = stack->pop(); - cout << "出栈元素 pop = " << pop << ",出栈后 stack = "; + stack->pop(); + cout << "出栈元素 pop = " << top << ",出栈后 stack = "; PrintUtil::printVector(stack->toVector()); /* 获取栈的长度 */ diff --git a/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp b/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp index dcaac225f..10cba0aaf 100644 --- a/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp @@ -47,7 +47,7 @@ public: } /* 出队 */ - int poll() { + void poll() { int num = peek(); // 删除头结点 ListNode *tmp = front; @@ -55,7 +55,6 @@ public: // 释放内存 delete tmp; queSize--; - return num; } /* 访问队首元素 */ @@ -97,8 +96,8 @@ int main() { cout << "队首元素 peek = " << peek << endl; /* 元素出队 */ - int poll = queue->poll(); - cout << "出队元素 poll = " << poll << ",出队后 queue = "; + queue->poll(); + cout << "出队元素 poll = " << peek << ",出队后 queue = "; PrintUtil::printVector(queue->toVector()); /* 获取队列的长度 */ diff --git a/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp b/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp index 312b75135..cf6f6ab3d 100644 --- a/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp +++ b/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp @@ -37,14 +37,13 @@ public: } /* 出栈 */ - int pop() { + void pop() { int num = top(); ListNode *tmp = stackTop; stackTop = stackTop->next; // 释放内存 delete tmp; stkSize--; - return num; } /* 访问栈顶元素 */ @@ -86,8 +85,8 @@ int main() { cout << "栈顶元素 top = " << top << endl; /* 元素出栈 */ - int pop = stack->pop(); - cout << "出栈元素 pop = " << pop << ",出栈后 stack = "; + stack->pop(); + cout << "出栈元素 pop = " << top << ",出栈后 stack = "; PrintUtil::printVector(stack->toVector()); /* 获取栈的长度 */ From fd3eaaf3fd6ef25080a9c886cd678cd1704bfab1 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 01:59:31 +0800 Subject: [PATCH 097/158] Update stack.md and queue.md --- docs/chapter_stack_and_queue/queue.md | 6 ++---- docs/chapter_stack_and_queue/stack.md | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index cc815b3ab..cbf854f9c 100644 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -328,7 +328,7 @@ comments: true queSize++; } /* 出队 */ - int poll() { + void poll() { int num = peek(); // 删除头结点 ListNode *tmp = front; @@ -336,7 +336,6 @@ comments: true // 释放内存 delete tmp; queSize--; - return num; } /* 访问队首元素 */ int peek() { @@ -719,11 +718,10 @@ comments: true rear = (rear + 1) % capacity(); } /* 出队 */ - int poll() { + void poll() { int num = peek(); // 队头指针向后移动一位,若越过尾部则返回到数组头部 front = (front + 1) % capacity(); - return num; } /* 访问队首元素 */ int peek() { diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index a8b06575b..ac4cf7cca 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -313,14 +313,13 @@ comments: true stkSize++; } /* 出栈 */ - int pop() { + void pop() { int num = top(); ListNode *tmp = stackTop; stackTop = stackTop->next; // 释放内存 delete tmp; stkSize--; - return num; } /* 访问栈顶元素 */ int top() { @@ -657,10 +656,9 @@ comments: true stack.push_back(num); } /* 出栈 */ - int pop() { + void pop() { int oldTop = top(); stack.pop_back(); - return oldTop; } /* 访问栈顶元素 */ int top() { From 92a89d04dec923746c6ba94368ee01664e391322 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 5 Jan 2023 03:12:46 +0800 Subject: [PATCH 098/158] Update stack.md --- docs/chapter_stack_and_queue/stack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index ac4cf7cca..2bc8b398e 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -872,7 +872,7 @@ comments: true !!! tip - 实际编程中,我们一般直接将 `ArrayList` 或 `LinkedList` 当作「栈」来使用。我们仅需通过脑补来屏蔽无关操作,而不用专门去包装它。 + 某些语言并未专门提供栈类,但我们可以直接把该语言的「数组」或「链表」看作栈来使用,并通过“脑补”来屏蔽无关操作,而无需像上述代码一样去特意包装一层。 ## 栈典型应用 From e9fc6b362e9dedff90b47f0882b55d92dbaffbeb Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Tue, 3 Jan 2023 23:50:40 +0800 Subject: [PATCH 099/158] feat: add Swift codes for data_and_memory article --- docs/chapter_data_structure/data_and_memory.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/chapter_data_structure/data_and_memory.md b/docs/chapter_data_structure/data_and_memory.md index 3e1e2167c..c620032aa 100644 --- a/docs/chapter_data_structure/data_and_memory.md +++ b/docs/chapter_data_structure/data_and_memory.md @@ -114,6 +114,16 @@ comments: true bool[] booleans = new bool[5]; ``` +=== "Swift" + + ```swift title="" + // 使用多种「基本数据类型」来初始化「数组」 + let numbers = Array(repeating: Int(), count: 5) + let decimals = Array(repeating: Double(), count: 5) + let characters = Array(repeating: Character("a"), count: 5) + let booleans = Array(repeating: Bool(), count: 5) + ``` + ## 计算机内存 在计算机中,内存和硬盘是两种主要的存储硬件设备。「硬盘」主要用于长期存储数据,容量较大(通常可达到 TB 级别)、速度较慢。「内存」用于运行程序时暂存数据,速度更快,但容量较小(通常为 GB 级别)。 From 7cd1347b4453b207c28f12e7d353398cbb4a4b20 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Thu, 5 Jan 2023 10:16:05 +0800 Subject: [PATCH 100/158] code(js): worst best time complexity --- .../worst_best_time_complexity.js | 41 +++++++++++++++++++ .../time_complexity.md | 34 +++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js diff --git a/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js b/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js new file mode 100644 index 000000000..db966b6fd --- /dev/null +++ b/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js @@ -0,0 +1,41 @@ +/* + * File: worst_best_time_complexity.js + * Created Time: 2023-01-05 + * Author: RiverTwilight (contact@rene.wang) + */ + +function randomNumbers(n) { + nums = Array(n); + for (let i = 0; i < n; i++) { + nums[i] = i + 1; + } + // 随机打乱数组元素 + for (let i = 0; i < n; i++) { + let r = Math.floor(Math.random() * n); + let temp = nums[i]; + nums[i] = nums[r]; + nums[r] = temp; + } + return nums; +} + +function findOne(nums) { + for (let i = 0; i < nums.length; i++) { + if (nums[i] == 1) { + return i; + } + } + return -1; +} + +function main() { + for (let i = 0; i < 10; i++) { + let n = 100; + let nums = randomNumbers(n); + let index = findOne(nums); + console.log( + "\n数组 [ 1, 2, ..., n ] 被打乱后 = [" + nums.join(", ") + "]" + ); + console.log("数字 1 的索引为 " + index); + } +} diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 9dcb5d84b..9f9e36c96 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -2328,7 +2328,41 @@ $$ === "JavaScript" ```js title="worst_best_time_complexity.js" + function randomNumbers(n) { + nums = Array(n); + for (let i = 0; i < n; i++) { + nums[i] = i + 1; + } + // 随机打乱数组元素 + for (let i = 0; i < n; i++) { + let r = Math.floor(Math.random() * n); + let temp = nums[i]; + nums[i] = nums[r]; + nums[r] = temp; + } + return nums; + } + function findOne(nums) { + for (let i = 0; i < nums.length; i++) { + if (nums[i] == 1) { + return i; + } + } + return -1; + } + + function main() { + for (let i = 0; i < 10; i++) { + let n = 100; + let nums = randomNumbers(n); + let index = findOne(nums); + console.log( + "\n数组 [ 1, 2, ..., n ] 被打乱后 = [" + nums.join(", ") + "]" + ); + console.log("数字 1 的索引为 " + index); + } + } ``` === "TypeScript" From 8031e0e2c541846d90240b72431fca00b04b5c0e Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Thu, 5 Jan 2023 10:20:19 +0800 Subject: [PATCH 101/158] lint: added missing keyword --- .../worst_best_time_complexity.js | 2 +- .../worst_best_time_complexity.ts | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts diff --git a/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js b/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js index db966b6fd..f887fe28d 100644 --- a/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js @@ -5,7 +5,7 @@ */ function randomNumbers(n) { - nums = Array(n); + let nums = Array(n); for (let i = 0; i < n; i++) { nums[i] = i + 1; } diff --git a/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts b/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts new file mode 100644 index 000000000..b263f994d --- /dev/null +++ b/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts @@ -0,0 +1,41 @@ +/* + * File: worst_best_time_complexity.ts + * Created Time: 2023-01-05 + * Author: RiverTwilight (contact@rene.wang) + */ + +function randomNumbers(n: number): number[] { + nums = Array(n); + for (let i = 0; i < n; i++) { + nums[i] = i + 1; + } + // 随机打乱数组元素 + for (let i = 0; i < n; i++) { + let r = Math.floor(Math.random() * n); + let temp = nums[i]; + nums[i] = nums[r]; + nums[r] = temp; + } + return nums; +} + +function findOne(nums) { + for (let i = 0; i < nums.length; i++) { + if (nums[i] == 1) { + return i; + } + } + return -1; +} + +function main() { + for (let i = 0; i < 10; i++) { + let n = 100; + let nums = randomNumbers(n); + let index = findOne(nums); + console.log( + "\n数组 [ 1, 2, ..., n ] 被打乱后 = [" + nums.join(", ") + "]" + ); + console.log("数字 1 的索引为 " + index); + } +} From c53f64d56ba3d29b04cfc0657032fd76bd195143 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Thu, 5 Jan 2023 10:23:12 +0800 Subject: [PATCH 102/158] code(ts): worst best time complexity --- .../worst_best_time_complexity.ts | 6 ++-- .../time_complexity.md | 36 ++++++++++++++++++- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts b/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts index b263f994d..97ca003a1 100644 --- a/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts @@ -5,7 +5,7 @@ */ function randomNumbers(n: number): number[] { - nums = Array(n); + let nums = Array(n); for (let i = 0; i < n; i++) { nums[i] = i + 1; } @@ -19,7 +19,7 @@ function randomNumbers(n: number): number[] { return nums; } -function findOne(nums) { +function findOne(nums: number[]): number { for (let i = 0; i < nums.length; i++) { if (nums[i] == 1) { return i; @@ -28,7 +28,7 @@ function findOne(nums) { return -1; } -function main() { +function main(): void { for (let i = 0; i < 10; i++) { let n = 100; let nums = randomNumbers(n); diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 9f9e36c96..815424dd3 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -2329,7 +2329,7 @@ $$ ```js title="worst_best_time_complexity.js" function randomNumbers(n) { - nums = Array(n); + let nums = Array(n); for (let i = 0; i < n; i++) { nums[i] = i + 1; } @@ -2368,7 +2368,41 @@ $$ === "TypeScript" ```typescript title="worst_best_time_complexity.ts" + function randomNumbers(n: number): number[] { + let nums = Array(n); + for (let i = 0; i < n; i++) { + nums[i] = i + 1; + } + // 随机打乱数组元素 + for (let i = 0; i < n; i++) { + let r = Math.floor(Math.random() * n); + let temp = nums[i]; + nums[i] = nums[r]; + nums[r] = temp; + } + return nums; + } + function findOne(nums: number[]): number { + for (let i = 0; i < nums.length; i++) { + if (nums[i] == 1) { + return i; + } + } + return -1; + } + + function main(): void { + for (let i = 0; i < 10; i++) { + let n = 100; + let nums = randomNumbers(n); + let index = findOne(nums); + console.log( + "\n数组 [ 1, 2, ..., n ] 被打乱后 = [" + nums.join(", ") + "]" + ); + console.log("数字 1 的索引为 " + index); + } + } ``` === "C" From 21096c8d0aba4cf68a00b2ce9e3159f0ee27bce4 Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Thu, 5 Jan 2023 10:27:48 +0800 Subject: [PATCH 103/158] lint: added comments --- .../worst_best_time_complexity.js | 4 ++++ .../worst_best_time_complexity.ts | 4 ++++ docs/chapter_computational_complexity/time_complexity.md | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js b/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js index f887fe28d..791cfe7a2 100644 --- a/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js @@ -4,8 +4,10 @@ * Author: RiverTwilight (contact@rene.wang) */ +/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ function randomNumbers(n) { let nums = Array(n); + // 生成数组 nums = { 1, 2, 3, ..., n } for (let i = 0; i < n; i++) { nums[i] = i + 1; } @@ -19,6 +21,7 @@ function randomNumbers(n) { return nums; } +/* 查找数组 nums 中数字 1 所在索引 */ function findOne(nums) { for (let i = 0; i < nums.length; i++) { if (nums[i] == 1) { @@ -28,6 +31,7 @@ function findOne(nums) { return -1; } +/* Driver Code */ function main() { for (let i = 0; i < 10; i++) { let n = 100; diff --git a/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts b/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts index 97ca003a1..68ed9bc35 100644 --- a/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts @@ -4,8 +4,10 @@ * Author: RiverTwilight (contact@rene.wang) */ +/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ function randomNumbers(n: number): number[] { let nums = Array(n); + // 生成数组 nums = { 1, 2, 3, ..., n } for (let i = 0; i < n; i++) { nums[i] = i + 1; } @@ -19,6 +21,7 @@ function randomNumbers(n: number): number[] { return nums; } +/* 查找数组 nums 中数字 1 所在索引 */ function findOne(nums: number[]): number { for (let i = 0; i < nums.length; i++) { if (nums[i] == 1) { @@ -28,6 +31,7 @@ function findOne(nums: number[]): number { return -1; } +/* Driver Code */ function main(): void { for (let i = 0; i < 10; i++) { let n = 100; diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 815424dd3..db2c5614d 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -2328,8 +2328,10 @@ $$ === "JavaScript" ```js title="worst_best_time_complexity.js" + /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ function randomNumbers(n) { let nums = Array(n); + // 生成数组 nums = { 1, 2, 3, ..., n } for (let i = 0; i < n; i++) { nums[i] = i + 1; } @@ -2343,6 +2345,7 @@ $$ return nums; } + /* 查找数组 nums 中数字 1 所在索引 */ function findOne(nums) { for (let i = 0; i < nums.length; i++) { if (nums[i] == 1) { @@ -2352,6 +2355,7 @@ $$ return -1; } + /* Driver Code */ function main() { for (let i = 0; i < 10; i++) { let n = 100; @@ -2368,8 +2372,10 @@ $$ === "TypeScript" ```typescript title="worst_best_time_complexity.ts" + /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ function randomNumbers(n: number): number[] { let nums = Array(n); + // 生成数组 nums = { 1, 2, 3, ..., n } for (let i = 0; i < n; i++) { nums[i] = i + 1; } @@ -2383,6 +2389,7 @@ $$ return nums; } + /* 查找数组 nums 中数字 1 所在索引 */ function findOne(nums: number[]): number { for (let i = 0; i < nums.length; i++) { if (nums[i] == 1) { @@ -2392,6 +2399,7 @@ $$ return -1; } + /* Driver Code */ function main(): void { for (let i = 0; i < 10; i++) { let n = 100; From f34dde9c651c5a854a7829948cc2d2c50ae44ead Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 6 Jan 2023 03:13:06 +0800 Subject: [PATCH 104/158] Add back-to-top button. --- docs/chapter_stack_and_queue/stack.md | 2 +- mkdocs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index 2bc8b398e..825cda9f9 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -872,7 +872,7 @@ comments: true !!! tip - 某些语言并未专门提供栈类,但我们可以直接把该语言的「数组」或「链表」看作栈来使用,并通过“脑补”来屏蔽无关操作,而无需像上述代码一样去特意包装一层。 + 某些语言并未专门提供栈类,但我们可以直接把该语言的「数组」或「链表」看作栈来使用,并通过“脑补”来屏蔽无关操作,而无需像上述代码去特意包装一层。 ## 栈典型应用 diff --git a/mkdocs.yml b/mkdocs.yml index 4aca74c24..9ddae9f39 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -32,7 +32,7 @@ theme: - navigation.sections # - navigation.tabs # - navigation.tabs.sticky - # - navigation.top + - navigation.top - navigation.footer - navigation.tracking - search.highlight From e96272a06fcec961e401daa942cb89c61fd75d8a Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 6 Jan 2023 03:14:52 +0800 Subject: [PATCH 105/158] Apply suggestions from code review Co-authored-by: Justin Tse --- .../worst_best_time_complexity.js | 4 ++-- .../worst_best_time_complexity.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js b/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js index 791cfe7a2..949e50b04 100644 --- a/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js @@ -13,7 +13,7 @@ function randomNumbers(n) { } // 随机打乱数组元素 for (let i = 0; i < n; i++) { - let r = Math.floor(Math.random() * n); + let r = Math.floor(Math.random() * (i + 1)); let temp = nums[i]; nums[i] = nums[r]; nums[r] = temp; @@ -24,7 +24,7 @@ function randomNumbers(n) { /* 查找数组 nums 中数字 1 所在索引 */ function findOne(nums) { for (let i = 0; i < nums.length; i++) { - if (nums[i] == 1) { + if (nums[i] === 1) { return i; } } diff --git a/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts b/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts index 68ed9bc35..a7318634f 100644 --- a/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts @@ -13,7 +13,7 @@ function randomNumbers(n: number): number[] { } // 随机打乱数组元素 for (let i = 0; i < n; i++) { - let r = Math.floor(Math.random() * n); + let r = Math.floor(Math.random() * (i + 1)); let temp = nums[i]; nums[i] = nums[r]; nums[r] = temp; @@ -24,7 +24,7 @@ function randomNumbers(n: number): number[] { /* 查找数组 nums 中数字 1 所在索引 */ function findOne(nums: number[]): number { for (let i = 0; i < nums.length; i++) { - if (nums[i] == 1) { + if (nums[i] === 1) { return i; } } From 30b4ab6ba7c95a896e305e5091cff7f1b1275179 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 6 Jan 2023 03:28:27 +0800 Subject: [PATCH 106/158] Update array (Go). --- codes/go/chapter_array_and_linkedlist/array.go | 5 ----- codes/go/chapter_array_and_linkedlist/array_test.go | 5 ++++- docs/chapter_array_and_linkedlist/array.md | 5 ++++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/codes/go/chapter_array_and_linkedlist/array.go b/codes/go/chapter_array_and_linkedlist/array.go index 5740811d8..0cf0d5d84 100644 --- a/codes/go/chapter_array_and_linkedlist/array.go +++ b/codes/go/chapter_array_and_linkedlist/array.go @@ -8,11 +8,6 @@ import ( "math/rand" ) -/** -我们将 Go 中的 Slice 切片看作 Array 数组,降低理解成本, -有利于我们将关注点放在数据结构与算法上。 -*/ - /* 随机返回一个数组元素 */ func randomAccess(nums []int) (randomNum int) { // 在区间 [0, nums.length) 中随机抽取一个数字 diff --git a/codes/go/chapter_array_and_linkedlist/array_test.go b/codes/go/chapter_array_and_linkedlist/array_test.go index 7dff6a034..3d4e8f887 100644 --- a/codes/go/chapter_array_and_linkedlist/array_test.go +++ b/codes/go/chapter_array_and_linkedlist/array_test.go @@ -17,8 +17,11 @@ import ( /* Driver Code */ func TestArray(t *testing.T) { /* 初始化数组 */ - var arr []int + var arr [5]int fmt.Println("数组 arr =", arr) + // 在 Go 中,指定长度时([5]int)为数组,不指定长度时([]int)为切片 + // 由于 Go 的数组被设计为在编译期确定长度,因此只能使用常量来指定长度 + // 为了方便实现扩容 extend() 方法,以下将切片(Slice)看作数组(Array) nums := []int{1, 3, 2, 5, 4} fmt.Println("数组 nums =", nums) diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 7f853a8f5..648a9c753 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -44,7 +44,10 @@ comments: true ```go title="array.go" /* 初始化数组 */ - var arr []int + var arr [5]int + // 在 Go 中,指定长度时([5]int)为数组,不指定长度时([]int)为切片 + // 由于 Go 的数组被设计为在编译期确定长度,因此只能使用常量来指定长度 + // 为了方便实现扩容 extend() 方法,以下将切片(Slice)看作数组(Array) nums := []int{1, 3, 2, 5, 4} ``` From 75217d5e8bf0478ed86da5c24a171d263231b90f Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 6 Jan 2023 03:31:58 +0800 Subject: [PATCH 107/158] Update bubble sort (C) and insertion sort (C). --- codes/c/chapter_sorting/bubble_sort.c | 11 ++++------- codes/c/chapter_sorting/insertion_sort.c | 6 ++---- docs/chapter_sorting/bubble_sort.md | 6 ++---- docs/chapter_sorting/insertion_sort.md | 3 +-- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/codes/c/chapter_sorting/bubble_sort.c b/codes/c/chapter_sorting/bubble_sort.c index 6b67bd727..5c74d9e4e 100644 --- a/codes/c/chapter_sorting/bubble_sort.c +++ b/codes/c/chapter_sorting/bubble_sort.c @@ -7,8 +7,7 @@ #include "../include/include.h" /* 冒泡排序 */ -void bubble_sort(int nums[], int size) -{ +void bubble_sort(int nums[], int size) { // 外循环:待排序元素数量为 n-1, n-2, ..., 1 for (int i = 0; i < size - 1; i++) { @@ -26,8 +25,7 @@ void bubble_sort(int nums[], int size) } /* 冒泡排序(标志优化)*/ -void bubble_sort_with_flag(int nums[], int size) -{ +void bubble_sort_with_flag(int nums[], int size) { // 外循环:待排序元素数量为 n-1, n-2, ..., 1 for (int i = 0; i < size - 1; i++) { @@ -50,8 +48,7 @@ void bubble_sort_with_flag(int nums[], int size) /* Driver Code */ -int main() -{ +int main() { int nums[6] = {4, 1, 3, 1, 5, 2}; printf("冒泡排序后:\n"); bubble_sort(nums, 6); @@ -69,4 +66,4 @@ int main() printf("\n"); return 0; -} \ No newline at end of file +} diff --git a/codes/c/chapter_sorting/insertion_sort.c b/codes/c/chapter_sorting/insertion_sort.c index ae1f6e7de..318a40278 100644 --- a/codes/c/chapter_sorting/insertion_sort.c +++ b/codes/c/chapter_sorting/insertion_sort.c @@ -7,8 +7,7 @@ #include "../include/include.h" /* 插入排序 */ -void insertionSort(int nums[], int size) -{ +void insertionSort(int nums[], int size) { // 外循环:base = nums[1], nums[2], ..., nums[n-1] for (int i = 1; i < size; i++) { @@ -26,8 +25,7 @@ void insertionSort(int nums[], int size) } /* Driver Code */ -int main() -{ +int main() { int nums[] = {4, 1, 3, 1, 5, 2}; insertionSort(nums, 6); printf("插入排序完成后 nums = \n"); diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index 336f3c2d5..bb35fabe4 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -170,8 +170,7 @@ comments: true ```c title="bubble_sort.c" /* 冒泡排序 */ - void bubble_sort(int nums[], int size) - { + void bubble_sort(int nums[], int size) { // 外循环:待排序元素数量为 n-1, n-2, ..., 1 for (int i = 0; i < size - 1; i++) { @@ -368,8 +367,7 @@ comments: true ```c title="bubble_sort.c" /* 冒泡排序 */ - void bubble_sort(int nums[], int size) - { + void bubble_sort(int nums[], int size) { // 外循环:待排序元素数量为 n-1, n-2, ..., 1 for (int i = 0; i < size - 1; i++) { diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index faaf38358..85016f718 100644 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -136,8 +136,7 @@ comments: true ```c title="insertion_sort.c" /* 插入排序 */ - void insertionSort(int nums[], int size) - { + void insertionSort(int nums[], int size) { // 外循环:base = nums[1], nums[2], ..., nums[n-1] for (int i = 1; i < size; i++) { From 0e49f0053a866f512235c55cf52b0f49044502e9 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Fri, 6 Jan 2023 03:39:19 +0800 Subject: [PATCH 108/158] Update the format of the file headers of c, cpp, java, js, ts, swift. --- codes/c/chapter_array_and_linkedlist/array.c | 2 +- codes/c/chapter_computational_complexity/time_complexity.c | 3 +-- .../worst_best_time_complexity.c | 3 +-- codes/c/chapter_sorting/insertion_sort.c | 2 +- codes/c/include/PrintUtil.h | 4 +--- codes/c/include/include.h | 4 +--- codes/cpp/chapter_array_and_linkedlist/array.cpp | 2 +- codes/cpp/chapter_array_and_linkedlist/linked_list.cpp | 2 +- codes/cpp/chapter_array_and_linkedlist/list.cpp | 2 +- codes/cpp/chapter_array_and_linkedlist/my_list.cpp | 2 +- .../cpp/chapter_computational_complexity/leetcode_two_sum.cpp | 2 +- .../cpp/chapter_computational_complexity/space_complexity.cpp | 2 +- .../cpp/chapter_computational_complexity/time_complexity.cpp | 2 +- .../worst_best_time_complexity.cpp | 2 +- codes/cpp/chapter_hashing/array_hash_map.cpp | 2 +- codes/cpp/chapter_hashing/hash_map.cpp | 2 +- codes/cpp/chapter_searching/binary_search.cpp | 2 +- codes/cpp/chapter_searching/hashing_search.cpp | 2 +- codes/cpp/chapter_searching/linear_search.cpp | 2 +- codes/cpp/chapter_sorting/bubble_sort.cpp | 2 +- codes/cpp/chapter_sorting/insertion_sort.cpp | 2 +- codes/cpp/chapter_sorting/merge_sort.cpp | 2 +- codes/cpp/chapter_sorting/quick_sort.cpp | 2 +- codes/cpp/chapter_stack_and_queue/array_queue.cpp | 2 +- codes/cpp/chapter_stack_and_queue/array_stack.cpp | 2 +- codes/cpp/chapter_stack_and_queue/deque.cpp | 2 +- codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp | 2 +- codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp | 2 +- codes/cpp/chapter_stack_and_queue/queue.cpp | 2 +- codes/cpp/chapter_stack_and_queue/stack.cpp | 2 +- codes/cpp/chapter_tree/avl_tree.cpp | 2 +- codes/cpp/chapter_tree/binary_search_tree.cpp | 2 +- codes/cpp/chapter_tree/binary_tree.cpp | 2 +- codes/cpp/chapter_tree/binary_tree_bfs.cpp | 2 +- codes/cpp/chapter_tree/binary_tree_dfs.cpp | 2 +- codes/cpp/include/ListNode.hpp | 2 +- codes/cpp/include/PrintUtil.hpp | 2 +- codes/cpp/include/TreeNode.hpp | 2 +- codes/cpp/include/include.hpp | 2 +- codes/java/chapter_array_and_linkedlist/array.java | 2 +- codes/java/chapter_array_and_linkedlist/linked_list.java | 2 +- codes/java/chapter_array_and_linkedlist/list.java | 2 +- codes/java/chapter_array_and_linkedlist/my_list.java | 2 +- .../chapter_computational_complexity/leetcode_two_sum.java | 2 +- .../chapter_computational_complexity/space_complexity.java | 2 +- .../chapter_computational_complexity/time_complexity.java | 2 +- .../worst_best_time_complexity.java | 2 +- codes/java/chapter_hashing/array_hash_map.java | 2 +- codes/java/chapter_hashing/hash_map.java | 2 +- codes/java/chapter_searching/binary_search.java | 2 +- codes/java/chapter_searching/hashing_search.java | 2 +- codes/java/chapter_searching/linear_search.java | 2 +- codes/java/chapter_sorting/bubble_sort.java | 2 +- codes/java/chapter_sorting/insertion_sort.java | 2 +- codes/java/chapter_sorting/merge_sort.java | 2 +- codes/java/chapter_sorting/quick_sort.java | 2 +- codes/java/chapter_stack_and_queue/array_queue.java | 2 +- codes/java/chapter_stack_and_queue/array_stack.java | 2 +- codes/java/chapter_stack_and_queue/deque.java | 2 +- codes/java/chapter_stack_and_queue/linkedlist_queue.java | 2 +- codes/java/chapter_stack_and_queue/linkedlist_stack.java | 2 +- codes/java/chapter_stack_and_queue/queue.java | 2 +- codes/java/chapter_stack_and_queue/stack.java | 2 +- codes/java/chapter_tree/avl_tree.java | 2 +- codes/java/chapter_tree/binary_search_tree.java | 2 +- codes/java/chapter_tree/binary_tree.java | 2 +- codes/java/chapter_tree/binary_tree_bfs.java | 2 +- codes/java/chapter_tree/binary_tree_dfs.java | 2 +- codes/java/include/ListNode.java | 2 +- codes/java/include/PrintUtil.java | 2 +- codes/java/include/TreeNode.java | 2 +- codes/javascript/chapter_array_and_linkedlist/list.js | 2 +- codes/javascript/chapter_array_and_linkedlist/my_list.js | 2 +- codes/javascript/chapter_hashing/array_hash_map.js | 2 +- codes/javascript/chapter_hashing/hash_map.js | 2 +- .../chapter_computational_complexity/leetcode_two_sum.swift | 2 +- .../chapter_computational_complexity/space_complexity.swift | 2 +- .../chapter_computational_complexity/time_complexity.swift | 2 +- .../worst_best_time_complexity.swift | 2 +- codes/swift/utils/ListNode.swift | 2 +- codes/swift/utils/PrintUtil.swift | 2 +- codes/swift/utils/TreeNode.swift | 2 +- codes/typescript/chapter_array_and_linkedlist/array.ts | 2 +- codes/typescript/chapter_array_and_linkedlist/linked_list.ts | 2 +- codes/typescript/chapter_array_and_linkedlist/list.ts | 2 +- codes/typescript/chapter_array_and_linkedlist/my_list.ts | 2 +- .../chapter_computational_complexity/leetcode_two_sum.ts | 2 +- codes/typescript/chapter_hashing/array_hash_map.ts | 2 +- codes/typescript/chapter_hashing/hash_map.ts | 2 +- codes/typescript/module/ListNode.ts | 2 +- codes/typescript/module/PrintUtil.ts | 2 +- codes/typescript/module/TreeNode.ts | 2 +- 92 files changed, 92 insertions(+), 98 deletions(-) diff --git a/codes/c/chapter_array_and_linkedlist/array.c b/codes/c/chapter_array_and_linkedlist/array.c index 300124a70..33dc99903 100644 --- a/codes/c/chapter_array_and_linkedlist/array.c +++ b/codes/c/chapter_array_and_linkedlist/array.c @@ -1,4 +1,4 @@ -/* +/** * File: array.c * Created Time: 2022-12-20 * Author: MolDuM (moldum@163.com) diff --git a/codes/c/chapter_computational_complexity/time_complexity.c b/codes/c/chapter_computational_complexity/time_complexity.c index 8df06a756..b8821ec6a 100644 --- a/codes/c/chapter_computational_complexity/time_complexity.c +++ b/codes/c/chapter_computational_complexity/time_complexity.c @@ -1,4 +1,4 @@ -/* +/** * File: time_complexity.c * Created Time: 2023-01-03 * Author: sjinzh (sjinzh@gmail.com) @@ -172,4 +172,3 @@ int main(int argc, char *argv[]) { getchar(); return 0; } - diff --git a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c index 88ed4dd2d..9173f32d4 100644 --- a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c +++ b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c @@ -1,4 +1,4 @@ -/* +/** * File: worst_best_time_complexity.c * Created Time: 2023-01-03 * Author: sjinzh (sjinzh@gmail.com) @@ -52,4 +52,3 @@ int main(int argc, char *argv[]) { getchar(); return 0; } - diff --git a/codes/c/chapter_sorting/insertion_sort.c b/codes/c/chapter_sorting/insertion_sort.c index 318a40278..80e8b127b 100644 --- a/codes/c/chapter_sorting/insertion_sort.c +++ b/codes/c/chapter_sorting/insertion_sort.c @@ -1,4 +1,4 @@ -/* +/** * File: insertion_sort.c * Created Time: 2022-12-29 * Author: Listening (https://github.com/L-Super) diff --git a/codes/c/include/PrintUtil.h b/codes/c/include/PrintUtil.h index e471f0388..59a8eac1b 100644 --- a/codes/c/include/PrintUtil.h +++ b/codes/c/include/PrintUtil.h @@ -1,4 +1,4 @@ -/* +/** * File: PrintUtil.h * Created Time: 2022-12-21 * Author: MolDum (moldum@163.com) @@ -26,5 +26,3 @@ static void printArray(int* arr, int n) } printf("%d]\n", arr[n-1]); } - - diff --git a/codes/c/include/include.h b/codes/c/include/include.h index c567aa2ec..2c4fd9252 100644 --- a/codes/c/include/include.h +++ b/codes/c/include/include.h @@ -1,4 +1,4 @@ -/* +/** * File: include.h * Created Time: 2022-12-20 * Author: MolDuM (moldum@163.com) @@ -11,5 +11,3 @@ #include #include "PrintUtil.h" - - diff --git a/codes/cpp/chapter_array_and_linkedlist/array.cpp b/codes/cpp/chapter_array_and_linkedlist/array.cpp index 22c5e50dd..6cf4919be 100644 --- a/codes/cpp/chapter_array_and_linkedlist/array.cpp +++ b/codes/cpp/chapter_array_and_linkedlist/array.cpp @@ -1,4 +1,4 @@ -/* +/** * File: array.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp b/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp index 4cf049204..13fa68691 100644 --- a/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp +++ b/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp @@ -1,4 +1,4 @@ -/* +/** * File: linked_list.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_array_and_linkedlist/list.cpp b/codes/cpp/chapter_array_and_linkedlist/list.cpp index 287cae1bb..44bbf88c8 100644 --- a/codes/cpp/chapter_array_and_linkedlist/list.cpp +++ b/codes/cpp/chapter_array_and_linkedlist/list.cpp @@ -1,4 +1,4 @@ -/* +/** * File: list.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_array_and_linkedlist/my_list.cpp b/codes/cpp/chapter_array_and_linkedlist/my_list.cpp index f64bd27c8..0b550475e 100644 --- a/codes/cpp/chapter_array_and_linkedlist/my_list.cpp +++ b/codes/cpp/chapter_array_and_linkedlist/my_list.cpp @@ -1,4 +1,4 @@ -/* +/** * File: my_list.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_computational_complexity/leetcode_two_sum.cpp b/codes/cpp/chapter_computational_complexity/leetcode_two_sum.cpp index 9df3b0a43..dd561d544 100644 --- a/codes/cpp/chapter_computational_complexity/leetcode_two_sum.cpp +++ b/codes/cpp/chapter_computational_complexity/leetcode_two_sum.cpp @@ -1,4 +1,4 @@ -/* +/** * File: leetcode_two_sum.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_computational_complexity/space_complexity.cpp b/codes/cpp/chapter_computational_complexity/space_complexity.cpp index 136e85bf6..6352b9d47 100644 --- a/codes/cpp/chapter_computational_complexity/space_complexity.cpp +++ b/codes/cpp/chapter_computational_complexity/space_complexity.cpp @@ -1,4 +1,4 @@ -/* +/** * File: space_complexity.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_computational_complexity/time_complexity.cpp b/codes/cpp/chapter_computational_complexity/time_complexity.cpp index 68408bcc8..cb1a20779 100644 --- a/codes/cpp/chapter_computational_complexity/time_complexity.cpp +++ b/codes/cpp/chapter_computational_complexity/time_complexity.cpp @@ -1,4 +1,4 @@ -/* +/** * File: time_complexity.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp b/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp index 9b215cf81..c2916cb40 100644 --- a/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp +++ b/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp @@ -1,4 +1,4 @@ -/* +/** * File: worst_best_time_complexity.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_hashing/array_hash_map.cpp b/codes/cpp/chapter_hashing/array_hash_map.cpp index 394e36810..8b92174e2 100644 --- a/codes/cpp/chapter_hashing/array_hash_map.cpp +++ b/codes/cpp/chapter_hashing/array_hash_map.cpp @@ -1,4 +1,4 @@ -/* +/** * File: array_hash_map.cpp * Created Time: 2022-12-14 * Author: msk397 (machangxinq@gmail.com) diff --git a/codes/cpp/chapter_hashing/hash_map.cpp b/codes/cpp/chapter_hashing/hash_map.cpp index 898f679a0..829265d69 100644 --- a/codes/cpp/chapter_hashing/hash_map.cpp +++ b/codes/cpp/chapter_hashing/hash_map.cpp @@ -1,4 +1,4 @@ -/* +/** * File: hash_map.cpp * Created Time: 2022-12-14 * Author: msk397 (machangxinq@gmail.com) diff --git a/codes/cpp/chapter_searching/binary_search.cpp b/codes/cpp/chapter_searching/binary_search.cpp index 9729128a6..8d727ab40 100644 --- a/codes/cpp/chapter_searching/binary_search.cpp +++ b/codes/cpp/chapter_searching/binary_search.cpp @@ -1,4 +1,4 @@ -/* +/** * File: binary_search.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_searching/hashing_search.cpp b/codes/cpp/chapter_searching/hashing_search.cpp index 674376ff0..ebc2fb01c 100644 --- a/codes/cpp/chapter_searching/hashing_search.cpp +++ b/codes/cpp/chapter_searching/hashing_search.cpp @@ -1,4 +1,4 @@ -/* +/** * File: hashing_search.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_searching/linear_search.cpp b/codes/cpp/chapter_searching/linear_search.cpp index e1f2a567b..7b5092762 100644 --- a/codes/cpp/chapter_searching/linear_search.cpp +++ b/codes/cpp/chapter_searching/linear_search.cpp @@ -1,4 +1,4 @@ -/* +/** * File: linear_search.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_sorting/bubble_sort.cpp b/codes/cpp/chapter_sorting/bubble_sort.cpp index 87ca305d1..27427cae3 100644 --- a/codes/cpp/chapter_sorting/bubble_sort.cpp +++ b/codes/cpp/chapter_sorting/bubble_sort.cpp @@ -1,4 +1,4 @@ -/* +/** * File: bubble_sort.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_sorting/insertion_sort.cpp b/codes/cpp/chapter_sorting/insertion_sort.cpp index 9ae5988ce..a40e4a5f7 100644 --- a/codes/cpp/chapter_sorting/insertion_sort.cpp +++ b/codes/cpp/chapter_sorting/insertion_sort.cpp @@ -1,4 +1,4 @@ -/* +/** * File: insertion_sort.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_sorting/merge_sort.cpp b/codes/cpp/chapter_sorting/merge_sort.cpp index b406529a3..89b5b09cd 100644 --- a/codes/cpp/chapter_sorting/merge_sort.cpp +++ b/codes/cpp/chapter_sorting/merge_sort.cpp @@ -1,4 +1,4 @@ -/* +/** * File: merge_sort.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_sorting/quick_sort.cpp b/codes/cpp/chapter_sorting/quick_sort.cpp index 4e4430e77..c298f5be8 100644 --- a/codes/cpp/chapter_sorting/quick_sort.cpp +++ b/codes/cpp/chapter_sorting/quick_sort.cpp @@ -1,4 +1,4 @@ -/* +/** * File: quick_sort.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_stack_and_queue/array_queue.cpp b/codes/cpp/chapter_stack_and_queue/array_queue.cpp index 8e87da501..de5e780f6 100644 --- a/codes/cpp/chapter_stack_and_queue/array_queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/array_queue.cpp @@ -1,4 +1,4 @@ -/* +/** * File: array_queue.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_stack_and_queue/array_stack.cpp b/codes/cpp/chapter_stack_and_queue/array_stack.cpp index e9ecae2a8..e6159e8cf 100644 --- a/codes/cpp/chapter_stack_and_queue/array_stack.cpp +++ b/codes/cpp/chapter_stack_and_queue/array_stack.cpp @@ -1,4 +1,4 @@ -/* +/** * File: array_stack.cpp * Created Time: 2022-11-28 * Author: qualifier1024 (2539244001@qq.com) diff --git a/codes/cpp/chapter_stack_and_queue/deque.cpp b/codes/cpp/chapter_stack_and_queue/deque.cpp index 96cf58111..e60af11e1 100644 --- a/codes/cpp/chapter_stack_and_queue/deque.cpp +++ b/codes/cpp/chapter_stack_and_queue/deque.cpp @@ -1,4 +1,4 @@ -/* +/** * File: deque.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp b/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp index 10cba0aaf..ae22740d6 100644 --- a/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp @@ -1,4 +1,4 @@ -/* +/** * File: linkedlist_queue.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp b/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp index cf6f6ab3d..495f2660f 100644 --- a/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp +++ b/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp @@ -1,4 +1,4 @@ -/* +/** * File: linkedlist_stack.cpp * Created Time: 2022-11-28 * Author: qualifier1024 (2539244001@qq.com) diff --git a/codes/cpp/chapter_stack_and_queue/queue.cpp b/codes/cpp/chapter_stack_and_queue/queue.cpp index 2ca504a20..6bc37302c 100644 --- a/codes/cpp/chapter_stack_and_queue/queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/queue.cpp @@ -1,4 +1,4 @@ -/* +/** * File: queue.cpp * Created Time: 2022-11-28 * Author: qualifier1024 (2539244001@qq.com) diff --git a/codes/cpp/chapter_stack_and_queue/stack.cpp b/codes/cpp/chapter_stack_and_queue/stack.cpp index cc0276970..7bbfd6b9f 100644 --- a/codes/cpp/chapter_stack_and_queue/stack.cpp +++ b/codes/cpp/chapter_stack_and_queue/stack.cpp @@ -1,4 +1,4 @@ -/* +/** * File: stack.cpp * Created Time: 2022-11-28 * Author: qualifier1024 (2539244001@qq.com) diff --git a/codes/cpp/chapter_tree/avl_tree.cpp b/codes/cpp/chapter_tree/avl_tree.cpp index b816f9752..8bc0741dc 100644 --- a/codes/cpp/chapter_tree/avl_tree.cpp +++ b/codes/cpp/chapter_tree/avl_tree.cpp @@ -1,4 +1,4 @@ -/* +/** * File: avl_tree.cpp * Created Time: 2022-12-2 * Author: mgisr (maguagua0706@gmail.com) diff --git a/codes/cpp/chapter_tree/binary_search_tree.cpp b/codes/cpp/chapter_tree/binary_search_tree.cpp index c7f8734da..78e797746 100644 --- a/codes/cpp/chapter_tree/binary_search_tree.cpp +++ b/codes/cpp/chapter_tree/binary_search_tree.cpp @@ -1,4 +1,4 @@ -/* +/** * File: binary_search_tree.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_tree/binary_tree.cpp b/codes/cpp/chapter_tree/binary_tree.cpp index fb00431eb..cd06778ad 100644 --- a/codes/cpp/chapter_tree/binary_tree.cpp +++ b/codes/cpp/chapter_tree/binary_tree.cpp @@ -1,4 +1,4 @@ -/* +/** * File: binary_tree.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_tree/binary_tree_bfs.cpp b/codes/cpp/chapter_tree/binary_tree_bfs.cpp index 30c2d6007..236f62449 100644 --- a/codes/cpp/chapter_tree/binary_tree_bfs.cpp +++ b/codes/cpp/chapter_tree/binary_tree_bfs.cpp @@ -1,4 +1,4 @@ -/* +/** * File: binary_tree_bfs.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/chapter_tree/binary_tree_dfs.cpp b/codes/cpp/chapter_tree/binary_tree_dfs.cpp index 08a0a331e..51287b736 100644 --- a/codes/cpp/chapter_tree/binary_tree_dfs.cpp +++ b/codes/cpp/chapter_tree/binary_tree_dfs.cpp @@ -1,4 +1,4 @@ -/* +/** * File: binary_tree_dfs.cpp * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/include/ListNode.hpp b/codes/cpp/include/ListNode.hpp index bd91f2558..9e6bd0692 100644 --- a/codes/cpp/include/ListNode.hpp +++ b/codes/cpp/include/ListNode.hpp @@ -1,4 +1,4 @@ -/* +/** * File: PrintUtil.hpp * Created Time: 2021-12-19 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/include/PrintUtil.hpp b/codes/cpp/include/PrintUtil.hpp index 544ee85db..06397855b 100644 --- a/codes/cpp/include/PrintUtil.hpp +++ b/codes/cpp/include/PrintUtil.hpp @@ -1,4 +1,4 @@ -/* +/** * File: PrintUtil.hpp * Created Time: 2021-12-19 * Author: Krahets (krahets@163.com), msk397 (machangxinq@gmail.com) diff --git a/codes/cpp/include/TreeNode.hpp b/codes/cpp/include/TreeNode.hpp index b3b27a65b..ab54c7746 100644 --- a/codes/cpp/include/TreeNode.hpp +++ b/codes/cpp/include/TreeNode.hpp @@ -1,4 +1,4 @@ -/* +/** * File: PrintUtil.hpp * Created Time: 2021-12-19 * Author: Krahets (krahets@163.com) diff --git a/codes/cpp/include/include.hpp b/codes/cpp/include/include.hpp index 28eab2cdf..8e4a8070d 100644 --- a/codes/cpp/include/include.hpp +++ b/codes/cpp/include/include.hpp @@ -1,4 +1,4 @@ -/* +/** * File: PrintUtil.hpp * Created Time: 2021-12-19 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_array_and_linkedlist/array.java b/codes/java/chapter_array_and_linkedlist/array.java index 6afd3dfdb..d3cb176c6 100644 --- a/codes/java/chapter_array_and_linkedlist/array.java +++ b/codes/java/chapter_array_and_linkedlist/array.java @@ -1,4 +1,4 @@ -/* +/** * File: array.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_array_and_linkedlist/linked_list.java b/codes/java/chapter_array_and_linkedlist/linked_list.java index 3d4451178..570778ff5 100644 --- a/codes/java/chapter_array_and_linkedlist/linked_list.java +++ b/codes/java/chapter_array_and_linkedlist/linked_list.java @@ -1,4 +1,4 @@ -/* +/** * File: linked_list.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_array_and_linkedlist/list.java b/codes/java/chapter_array_and_linkedlist/list.java index 1f13bc2ef..dc4d897f7 100644 --- a/codes/java/chapter_array_and_linkedlist/list.java +++ b/codes/java/chapter_array_and_linkedlist/list.java @@ -1,4 +1,4 @@ -/* +/** * File: list.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_array_and_linkedlist/my_list.java b/codes/java/chapter_array_and_linkedlist/my_list.java index 1f9fdc3eb..cce8da683 100644 --- a/codes/java/chapter_array_and_linkedlist/my_list.java +++ b/codes/java/chapter_array_and_linkedlist/my_list.java @@ -1,4 +1,4 @@ -/* +/** * File: my_list.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_computational_complexity/leetcode_two_sum.java b/codes/java/chapter_computational_complexity/leetcode_two_sum.java index 45106867a..b9f8bf4d7 100644 --- a/codes/java/chapter_computational_complexity/leetcode_two_sum.java +++ b/codes/java/chapter_computational_complexity/leetcode_two_sum.java @@ -1,4 +1,4 @@ -/* +/** * File: leetcode_two_sum.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_computational_complexity/space_complexity.java b/codes/java/chapter_computational_complexity/space_complexity.java index a81026d8d..275a37ec0 100644 --- a/codes/java/chapter_computational_complexity/space_complexity.java +++ b/codes/java/chapter_computational_complexity/space_complexity.java @@ -1,4 +1,4 @@ -/* +/** * File: space_complexity.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_computational_complexity/time_complexity.java b/codes/java/chapter_computational_complexity/time_complexity.java index d303c5107..5a232e5b8 100644 --- a/codes/java/chapter_computational_complexity/time_complexity.java +++ b/codes/java/chapter_computational_complexity/time_complexity.java @@ -1,4 +1,4 @@ -/* +/** * File: time_complexity.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_computational_complexity/worst_best_time_complexity.java b/codes/java/chapter_computational_complexity/worst_best_time_complexity.java index 536bf3af0..c0fe0baa7 100644 --- a/codes/java/chapter_computational_complexity/worst_best_time_complexity.java +++ b/codes/java/chapter_computational_complexity/worst_best_time_complexity.java @@ -1,4 +1,4 @@ -/* +/** * File: worst_best_time_complexity.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_hashing/array_hash_map.java b/codes/java/chapter_hashing/array_hash_map.java index fb0073abd..68df1f3b9 100644 --- a/codes/java/chapter_hashing/array_hash_map.java +++ b/codes/java/chapter_hashing/array_hash_map.java @@ -1,4 +1,4 @@ -/* +/** * File: hash_map.java * Created Time: 2022-12-04 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_hashing/hash_map.java b/codes/java/chapter_hashing/hash_map.java index bb72a4b0f..fe3f822f4 100644 --- a/codes/java/chapter_hashing/hash_map.java +++ b/codes/java/chapter_hashing/hash_map.java @@ -1,4 +1,4 @@ -/* +/** * File: hash_map.java * Created Time: 2022-12-04 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_searching/binary_search.java b/codes/java/chapter_searching/binary_search.java index cb5bd9419..05c2a4d32 100644 --- a/codes/java/chapter_searching/binary_search.java +++ b/codes/java/chapter_searching/binary_search.java @@ -1,4 +1,4 @@ -/* +/** * File: binary_search.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_searching/hashing_search.java b/codes/java/chapter_searching/hashing_search.java index c68025de0..cfd015313 100644 --- a/codes/java/chapter_searching/hashing_search.java +++ b/codes/java/chapter_searching/hashing_search.java @@ -1,4 +1,4 @@ -/* +/** * File: hashing_search.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_searching/linear_search.java b/codes/java/chapter_searching/linear_search.java index 2e8e410ec..c33b51178 100644 --- a/codes/java/chapter_searching/linear_search.java +++ b/codes/java/chapter_searching/linear_search.java @@ -1,4 +1,4 @@ -/* +/** * File: linear_search.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_sorting/bubble_sort.java b/codes/java/chapter_sorting/bubble_sort.java index f006a3ab8..082d2d909 100644 --- a/codes/java/chapter_sorting/bubble_sort.java +++ b/codes/java/chapter_sorting/bubble_sort.java @@ -1,4 +1,4 @@ -/* +/** * File: bubble_sort.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_sorting/insertion_sort.java b/codes/java/chapter_sorting/insertion_sort.java index 9a6a03102..fb5b2cd66 100644 --- a/codes/java/chapter_sorting/insertion_sort.java +++ b/codes/java/chapter_sorting/insertion_sort.java @@ -1,4 +1,4 @@ -/* +/** * File: insertion_sort.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_sorting/merge_sort.java b/codes/java/chapter_sorting/merge_sort.java index fe9f183a5..68f76c9b1 100644 --- a/codes/java/chapter_sorting/merge_sort.java +++ b/codes/java/chapter_sorting/merge_sort.java @@ -1,4 +1,4 @@ -/* +/** * File: merge_sort.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_sorting/quick_sort.java b/codes/java/chapter_sorting/quick_sort.java index d65aa8e4b..b121b757b 100644 --- a/codes/java/chapter_sorting/quick_sort.java +++ b/codes/java/chapter_sorting/quick_sort.java @@ -1,4 +1,4 @@ -/* +/** * File: quick_sort.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_stack_and_queue/array_queue.java b/codes/java/chapter_stack_and_queue/array_queue.java index 198c90714..5a1ab645d 100644 --- a/codes/java/chapter_stack_and_queue/array_queue.java +++ b/codes/java/chapter_stack_and_queue/array_queue.java @@ -1,4 +1,4 @@ -/* +/** * File: array_queue.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_stack_and_queue/array_stack.java b/codes/java/chapter_stack_and_queue/array_stack.java index 12ac52831..e32b16404 100644 --- a/codes/java/chapter_stack_and_queue/array_stack.java +++ b/codes/java/chapter_stack_and_queue/array_stack.java @@ -1,4 +1,4 @@ -/* +/** * File: array_stack.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_stack_and_queue/deque.java b/codes/java/chapter_stack_and_queue/deque.java index a29e6e7c3..1a8f196b1 100644 --- a/codes/java/chapter_stack_and_queue/deque.java +++ b/codes/java/chapter_stack_and_queue/deque.java @@ -1,4 +1,4 @@ -/* +/** * File: deque.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_stack_and_queue/linkedlist_queue.java b/codes/java/chapter_stack_and_queue/linkedlist_queue.java index 92cdfab0e..82e2571c2 100644 --- a/codes/java/chapter_stack_and_queue/linkedlist_queue.java +++ b/codes/java/chapter_stack_and_queue/linkedlist_queue.java @@ -1,4 +1,4 @@ -/* +/** * File: linkedlist_queue.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_stack_and_queue/linkedlist_stack.java b/codes/java/chapter_stack_and_queue/linkedlist_stack.java index 794c80c31..b1a78aa4a 100644 --- a/codes/java/chapter_stack_and_queue/linkedlist_stack.java +++ b/codes/java/chapter_stack_and_queue/linkedlist_stack.java @@ -1,4 +1,4 @@ -/* +/** * File: linkedlist_stack.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_stack_and_queue/queue.java b/codes/java/chapter_stack_and_queue/queue.java index 232943fb1..25457fbdb 100644 --- a/codes/java/chapter_stack_and_queue/queue.java +++ b/codes/java/chapter_stack_and_queue/queue.java @@ -1,4 +1,4 @@ -/* +/** * File: queue.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_stack_and_queue/stack.java b/codes/java/chapter_stack_and_queue/stack.java index 9c94eb62b..2a7259220 100644 --- a/codes/java/chapter_stack_and_queue/stack.java +++ b/codes/java/chapter_stack_and_queue/stack.java @@ -1,4 +1,4 @@ -/* +/** * File: stack.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_tree/avl_tree.java b/codes/java/chapter_tree/avl_tree.java index 2f731b7e4..9120262f4 100644 --- a/codes/java/chapter_tree/avl_tree.java +++ b/codes/java/chapter_tree/avl_tree.java @@ -1,4 +1,4 @@ -/* +/** * File: avl_tree.java * Created Time: 2022-12-10 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_tree/binary_search_tree.java b/codes/java/chapter_tree/binary_search_tree.java index 4a7c53dba..4a7f3194e 100644 --- a/codes/java/chapter_tree/binary_search_tree.java +++ b/codes/java/chapter_tree/binary_search_tree.java @@ -1,4 +1,4 @@ -/* +/** * File: binary_search_tree.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_tree/binary_tree.java b/codes/java/chapter_tree/binary_tree.java index 8f25a9a16..65a511e8c 100644 --- a/codes/java/chapter_tree/binary_tree.java +++ b/codes/java/chapter_tree/binary_tree.java @@ -1,4 +1,4 @@ -/* +/** * File: binary_tree.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_tree/binary_tree_bfs.java b/codes/java/chapter_tree/binary_tree_bfs.java index 524274a68..c000eacea 100644 --- a/codes/java/chapter_tree/binary_tree_bfs.java +++ b/codes/java/chapter_tree/binary_tree_bfs.java @@ -1,4 +1,4 @@ -/* +/** * File: binary_tree_bfs.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/chapter_tree/binary_tree_dfs.java b/codes/java/chapter_tree/binary_tree_dfs.java index fa866d965..1d3026dfb 100644 --- a/codes/java/chapter_tree/binary_tree_dfs.java +++ b/codes/java/chapter_tree/binary_tree_dfs.java @@ -1,4 +1,4 @@ -/* +/** * File: binary_tree_dfs.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/include/ListNode.java b/codes/java/include/ListNode.java index 14308feb4..60590100a 100755 --- a/codes/java/include/ListNode.java +++ b/codes/java/include/ListNode.java @@ -1,4 +1,4 @@ -/* +/** * File: ListNode.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/include/PrintUtil.java b/codes/java/include/PrintUtil.java index b8c970504..f21bc17c9 100755 --- a/codes/java/include/PrintUtil.java +++ b/codes/java/include/PrintUtil.java @@ -1,4 +1,4 @@ -/* +/** * File: PrintUtil.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/java/include/TreeNode.java b/codes/java/include/TreeNode.java index 98b9f826e..b98252665 100644 --- a/codes/java/include/TreeNode.java +++ b/codes/java/include/TreeNode.java @@ -1,4 +1,4 @@ -/* +/** * File: TreeNode.java * Created Time: 2022-11-25 * Author: Krahets (krahets@163.com) diff --git a/codes/javascript/chapter_array_and_linkedlist/list.js b/codes/javascript/chapter_array_and_linkedlist/list.js index 19318337f..593917589 100644 --- a/codes/javascript/chapter_array_and_linkedlist/list.js +++ b/codes/javascript/chapter_array_and_linkedlist/list.js @@ -1,4 +1,4 @@ -/* +/** * File: list.js * Created Time: 2022-12-12 * Author: Justin (xiefahit@gmail.com) diff --git a/codes/javascript/chapter_array_and_linkedlist/my_list.js b/codes/javascript/chapter_array_and_linkedlist/my_list.js index a861b1f0d..388bb1f3d 100644 --- a/codes/javascript/chapter_array_and_linkedlist/my_list.js +++ b/codes/javascript/chapter_array_and_linkedlist/my_list.js @@ -1,4 +1,4 @@ -/* +/** * File: my_list.js * Created Time: 2022-12-12 * Author: Justin (xiefahit@gmail.com) diff --git a/codes/javascript/chapter_hashing/array_hash_map.js b/codes/javascript/chapter_hashing/array_hash_map.js index f5c77aa2b..405d0f318 100644 --- a/codes/javascript/chapter_hashing/array_hash_map.js +++ b/codes/javascript/chapter_hashing/array_hash_map.js @@ -1,4 +1,4 @@ -/* +/** * File: array_hash_map.js * Created Time: 2022-12-26 * Author: Justin (xiefahit@gmail.com) diff --git a/codes/javascript/chapter_hashing/hash_map.js b/codes/javascript/chapter_hashing/hash_map.js index 197fb7dd9..9157b3159 100644 --- a/codes/javascript/chapter_hashing/hash_map.js +++ b/codes/javascript/chapter_hashing/hash_map.js @@ -1,4 +1,4 @@ -/* +/** * File: hash_map.js * Created Time: 2022-12-26 * Author: Justin (xiefahit@gmail.com) diff --git a/codes/swift/chapter_computational_complexity/leetcode_two_sum.swift b/codes/swift/chapter_computational_complexity/leetcode_two_sum.swift index 6ed0e580c..e82872d38 100644 --- a/codes/swift/chapter_computational_complexity/leetcode_two_sum.swift +++ b/codes/swift/chapter_computational_complexity/leetcode_two_sum.swift @@ -1,4 +1,4 @@ -/* +/** * File: leetcode_two_sum.swift * Created Time: 2023-01-03 * Author: nuomi1 (nuomi1@qq.com) diff --git a/codes/swift/chapter_computational_complexity/space_complexity.swift b/codes/swift/chapter_computational_complexity/space_complexity.swift index 4931597ca..92a1187a0 100644 --- a/codes/swift/chapter_computational_complexity/space_complexity.swift +++ b/codes/swift/chapter_computational_complexity/space_complexity.swift @@ -1,4 +1,4 @@ -/* +/** * File: space_complexity.swift * Created Time: 2023-01-01 * Author: nuomi1 (nuomi1@qq.com) diff --git a/codes/swift/chapter_computational_complexity/time_complexity.swift b/codes/swift/chapter_computational_complexity/time_complexity.swift index 322b4f09d..44addb800 100644 --- a/codes/swift/chapter_computational_complexity/time_complexity.swift +++ b/codes/swift/chapter_computational_complexity/time_complexity.swift @@ -1,4 +1,4 @@ -/* +/** * File: time_complexity.swift * Created Time: 2022-12-26 * Author: nuomi1 (nuomi1@qq.com) diff --git a/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift b/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift index 4fa2cf2a0..09d7de1f4 100644 --- a/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift +++ b/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift @@ -1,4 +1,4 @@ -/* +/** * File: worst_best_time_complexity.swift * Created Time: 2022-12-26 * Author: nuomi1 (nuomi1@qq.com) diff --git a/codes/swift/utils/ListNode.swift b/codes/swift/utils/ListNode.swift index 796ee5125..5fa3671eb 100644 --- a/codes/swift/utils/ListNode.swift +++ b/codes/swift/utils/ListNode.swift @@ -1,4 +1,4 @@ -/* +/** * File: ListNode.swift * Created Time: 2023-01-02 * Author: nuomi1 (nuomi1@qq.com) diff --git a/codes/swift/utils/PrintUtil.swift b/codes/swift/utils/PrintUtil.swift index 3f3fc2688..f2b54693d 100644 --- a/codes/swift/utils/PrintUtil.swift +++ b/codes/swift/utils/PrintUtil.swift @@ -1,4 +1,4 @@ -/* +/** * File: PrintUtil.swift * Created Time: 2023-01-02 * Author: nuomi1 (nuomi1@qq.com) diff --git a/codes/swift/utils/TreeNode.swift b/codes/swift/utils/TreeNode.swift index b6ce30575..161cebd57 100644 --- a/codes/swift/utils/TreeNode.swift +++ b/codes/swift/utils/TreeNode.swift @@ -1,4 +1,4 @@ -/* +/** * File: TreeNode.swift * Created Time: 2023-01-02 * Author: nuomi1 (nuomi1@qq.com) diff --git a/codes/typescript/chapter_array_and_linkedlist/array.ts b/codes/typescript/chapter_array_and_linkedlist/array.ts index 237a04385..a85816b97 100644 --- a/codes/typescript/chapter_array_and_linkedlist/array.ts +++ b/codes/typescript/chapter_array_and_linkedlist/array.ts @@ -1,4 +1,4 @@ -/* +/** * File: array.ts * Created Time: 2022-12-04 * Author: Justin (xiefahit@gmail.com) diff --git a/codes/typescript/chapter_array_and_linkedlist/linked_list.ts b/codes/typescript/chapter_array_and_linkedlist/linked_list.ts index 484322581..b5eb988ae 100644 --- a/codes/typescript/chapter_array_and_linkedlist/linked_list.ts +++ b/codes/typescript/chapter_array_and_linkedlist/linked_list.ts @@ -1,4 +1,4 @@ -/* +/** * File: linked_list.ts * Created Time: 2022-12-10 * Author: Justin (xiefahit@gmail.com) diff --git a/codes/typescript/chapter_array_and_linkedlist/list.ts b/codes/typescript/chapter_array_and_linkedlist/list.ts index 8fca2bfa1..e1d2d518a 100644 --- a/codes/typescript/chapter_array_and_linkedlist/list.ts +++ b/codes/typescript/chapter_array_and_linkedlist/list.ts @@ -1,4 +1,4 @@ -/* +/** * File: list.ts * Created Time: 2022-12-10 * Author: Justin (xiefahit@gmail.com) diff --git a/codes/typescript/chapter_array_and_linkedlist/my_list.ts b/codes/typescript/chapter_array_and_linkedlist/my_list.ts index b2ce46840..e60075daf 100644 --- a/codes/typescript/chapter_array_and_linkedlist/my_list.ts +++ b/codes/typescript/chapter_array_and_linkedlist/my_list.ts @@ -1,4 +1,4 @@ -/* +/** * File: my_list.ts * Created Time: 2022-12-11 * Author: Justin (xiefahit@gmail.com) diff --git a/codes/typescript/chapter_computational_complexity/leetcode_two_sum.ts b/codes/typescript/chapter_computational_complexity/leetcode_two_sum.ts index 8e3c4cd76..47c52d80d 100644 --- a/codes/typescript/chapter_computational_complexity/leetcode_two_sum.ts +++ b/codes/typescript/chapter_computational_complexity/leetcode_two_sum.ts @@ -1,4 +1,4 @@ -/* +/** * File: leetcode_two_sum.ts * Created Time: 2022-12-15 * Author: gyt95 (gytkwan@gmail.com) diff --git a/codes/typescript/chapter_hashing/array_hash_map.ts b/codes/typescript/chapter_hashing/array_hash_map.ts index 627205f7f..027cdc8e1 100644 --- a/codes/typescript/chapter_hashing/array_hash_map.ts +++ b/codes/typescript/chapter_hashing/array_hash_map.ts @@ -1,4 +1,4 @@ -/* +/** * File: array_hash_map.ts * Created Time: 2022-12-20 * Author: Daniel (better.sunjian@gmail.com) diff --git a/codes/typescript/chapter_hashing/hash_map.ts b/codes/typescript/chapter_hashing/hash_map.ts index 7e54cf5cb..8d73da0c0 100644 --- a/codes/typescript/chapter_hashing/hash_map.ts +++ b/codes/typescript/chapter_hashing/hash_map.ts @@ -1,4 +1,4 @@ -/* +/** * File: hash_map.ts * Created Time: 2022-12-20 * Author: Daniel (better.sunjian@gmail.com) diff --git a/codes/typescript/module/ListNode.ts b/codes/typescript/module/ListNode.ts index d6d60616d..17fdbb955 100644 --- a/codes/typescript/module/ListNode.ts +++ b/codes/typescript/module/ListNode.ts @@ -1,4 +1,4 @@ -/* +/** * File: ListNode.ts * Created Time: 2022-12-10 * Author: Justin (xiefahit@gmail.com) diff --git a/codes/typescript/module/PrintUtil.ts b/codes/typescript/module/PrintUtil.ts index 9174be159..59f1af0e5 100644 --- a/codes/typescript/module/PrintUtil.ts +++ b/codes/typescript/module/PrintUtil.ts @@ -1,4 +1,4 @@ -/* +/** * File: PrintUtil.ts * Created Time: 2022-12-13 * Author: Justin (xiefahit@gmail.com) diff --git a/codes/typescript/module/TreeNode.ts b/codes/typescript/module/TreeNode.ts index 0bc783aa9..5a886763e 100644 --- a/codes/typescript/module/TreeNode.ts +++ b/codes/typescript/module/TreeNode.ts @@ -1,4 +1,4 @@ -/* +/** * File: TreeNode.ts * Created Time: 2022-12-13 * Author: Justin (xiefahit@gmail.com) From 52927cadad684a0d9e1d9583892566cf1ed185be Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Fri, 6 Jan 2023 08:28:59 +0800 Subject: [PATCH 109/158] fix: change shuffle algo in the doc --- docs/chapter_computational_complexity/time_complexity.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index db2c5614d..fa07bd542 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -2337,7 +2337,7 @@ $$ } // 随机打乱数组元素 for (let i = 0; i < n; i++) { - let r = Math.floor(Math.random() * n); + let r = Math.floor(Math.random() * (i + 1)); let temp = nums[i]; nums[i] = nums[r]; nums[r] = temp; @@ -2381,7 +2381,7 @@ $$ } // 随机打乱数组元素 for (let i = 0; i < n; i++) { - let r = Math.floor(Math.random() * n); + let r = Math.floor(Math.random() * (i + 1)); let temp = nums[i]; nums[i] = nums[r]; nums[r] = temp; From 5a24254f6b95d19bae6a366e0695df6ee91d026a Mon Sep 17 00:00:00 2001 From: RiverTwilight Date: Fri, 6 Jan 2023 08:32:10 +0800 Subject: [PATCH 110/158] lint --- docs/chapter_computational_complexity/time_complexity.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index fa07bd542..b6b68c443 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -2348,7 +2348,7 @@ $$ /* 查找数组 nums 中数字 1 所在索引 */ function findOne(nums) { for (let i = 0; i < nums.length; i++) { - if (nums[i] == 1) { + if (nums[i] === 1) { return i; } } @@ -2392,7 +2392,7 @@ $$ /* 查找数组 nums 中数字 1 所在索引 */ function findOne(nums: number[]): number { for (let i = 0; i < nums.length; i++) { - if (nums[i] == 1) { + if (nums[i] === 1) { return i; } } From 98d1244e32df673959deb2ddd1484b72ed61d1c8 Mon Sep 17 00:00:00 2001 From: dumingyu Date: Fri, 6 Jan 2023 11:00:12 +0800 Subject: [PATCH 111/158] fix(codes/cpp): add climits headers This fixes clang++ compile error when using INT_MAX in PrintUtil. --- codes/cpp/include/PrintUtil.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/codes/cpp/include/PrintUtil.hpp b/codes/cpp/include/PrintUtil.hpp index 06397855b..e788d1650 100644 --- a/codes/cpp/include/PrintUtil.hpp +++ b/codes/cpp/include/PrintUtil.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "ListNode.hpp" #include "TreeNode.hpp" From c8dac9b5c136874bdc310d224417cbd4f532861a Mon Sep 17 00:00:00 2001 From: Yuelin Xin Date: Fri, 6 Jan 2023 20:08:25 +0000 Subject: [PATCH 112/158] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=99=E5=88=AB?= =?UTF-8?q?=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/chapter_sorting/intro_to_sort.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_sorting/intro_to_sort.md b/docs/chapter_sorting/intro_to_sort.md index df84ce13e..a3cb071dc 100644 --- a/docs/chapter_sorting/intro_to_sort.md +++ b/docs/chapter_sorting/intro_to_sort.md @@ -22,7 +22,7 @@ comments: true - 「稳定排序」在完成排序后,**不改变** 相等元素在数组中的相对顺序。 - 「非稳定排序」在完成排序后,相等元素在数组中的相对位置 **可能被改变**。 -假设我们有一个存储学生信息当表格,第 1, 2 列分别是姓名和年龄。那么在以下示例中,「非稳定排序」会导致输入数据的有序性丢失。因此「稳定排序」是很好的特性,**在多级排序中是必须的**。 +假设我们有一个存储学生信息的表格,第 1, 2 列分别是姓名和年龄。那么在以下示例中,「非稳定排序」会导致输入数据的有序性丢失。因此「稳定排序」是很好的特性,**在多级排序中是必须的**。 ```shell # 输入数据是按照姓名排序好的 From e1d561bc088b9f4623f06f3ba49420cc815ef56b Mon Sep 17 00:00:00 2001 From: danielsss Date: Sat, 7 Jan 2023 16:38:13 +1100 Subject: [PATCH 113/158] added: linear search for Typescript --- .../chapter_searching/linear_search.ts | 47 +++++++++++++++++++ codes/typescript/module/ListNode.ts | 15 ++++++ 2 files changed, 62 insertions(+) create mode 100644 codes/typescript/chapter_searching/linear_search.ts diff --git a/codes/typescript/chapter_searching/linear_search.ts b/codes/typescript/chapter_searching/linear_search.ts new file mode 100644 index 000000000..05620f913 --- /dev/null +++ b/codes/typescript/chapter_searching/linear_search.ts @@ -0,0 +1,47 @@ +/** + * File: linear_search.ts + * Created Time: 2023-01-07 + * Author: Daniel (better.sunjian@gmail.com) + */ + +import ListNode from '../module/ListNode.ts'; + +/* 线性查找(数组) */ +function linearSearchArray(nums: number[], target: number): number { + // 遍历数组 + for (let i = 0; i < nums.length; i++) { + // 找到目标元素,返回其索引 + if (nums[i] === target) { + return i; + } + } + // 未找到目标元素,返回 -1 + return -1; +} + +/* 线性查找(链表)*/ +function linearSearchLinkedList(head: ListNode | null, target: number): ListNode | null { + // 遍历链表 + while (head) { + // 找到目标结点,返回之 + if (head.val === target) { + return head; + } + head = head.next; + } + // 未找到目标结点,返回 null + return null; +} + +/* Driver Code */ +const target = 3; + +/* 在数组中执行线性查找 */ +const nums = [ 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 ]; +const index = linearSearchArray(nums, target); +console.log('目标元素 3 的索引 =', index); + +/* 在链表中执行线性查找 */ +const head = ListNode.arrToLinkedList(nums); +const node = linearSearchLinkedList(head, target); +console.log('目标结点值 3 的对应结点对象为', node); diff --git a/codes/typescript/module/ListNode.ts b/codes/typescript/module/ListNode.ts index 17fdbb955..84f1822f8 100644 --- a/codes/typescript/module/ListNode.ts +++ b/codes/typescript/module/ListNode.ts @@ -14,4 +14,19 @@ export default class ListNode { this.val = val === undefined ? 0 : val; this.next = next === undefined ? null : next; } + + /** + * Generate a linked-list with an array + * @param arr + * @return ListNode + */ + public static arrToLinkedList(arr: number[]): ListNode | null { + const dum = new ListNode(0); + let head = dum; + for (const val of arr) { + head.next = new ListNode(val); + head = head.next; + } + return dum.next; + } } From 2b0d7d1c1b1a9d1eda3efd6300782d4179ab27a7 Mon Sep 17 00:00:00 2001 From: danielsss Date: Sat, 7 Jan 2023 16:41:46 +1100 Subject: [PATCH 114/158] Added linear search docs for Typescript --- .../chapter_searching/linear_search.ts | 2 +- docs/chapter_searching/linear_search.md | 27 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/codes/typescript/chapter_searching/linear_search.ts b/codes/typescript/chapter_searching/linear_search.ts index 05620f913..f0de33f78 100644 --- a/codes/typescript/chapter_searching/linear_search.ts +++ b/codes/typescript/chapter_searching/linear_search.ts @@ -6,7 +6,7 @@ import ListNode from '../module/ListNode.ts'; -/* 线性查找(数组) */ +/* 线性查找(数组)*/ function linearSearchArray(nums: number[], target: number): number { // 遍历数组 for (let i = 0; i < nums.length; i++) { diff --git a/docs/chapter_searching/linear_search.md b/docs/chapter_searching/linear_search.md index 3d592962e..777d2d53b 100644 --- a/docs/chapter_searching/linear_search.md +++ b/docs/chapter_searching/linear_search.md @@ -94,7 +94,18 @@ comments: true === "TypeScript" ```typescript title="linear_search.ts" - + /* 线性查找(数组)*/ + function linearSearchArray(nums: number[], target: number): number { + // 遍历数组 + for (let i = 0; i < nums.length; i++) { + // 找到目标元素,返回其索引 + if (nums[i] === target) { + return i; + } + } + // 未找到目标元素,返回 -1 + return -1; + } ``` === "C" @@ -210,7 +221,19 @@ comments: true === "TypeScript" ```typescript title="linear_search.ts" - + /* 线性查找(链表)*/ + function linearSearchLinkedList(head: ListNode | null, target: number): ListNode | null { + // 遍历链表 + while (head) { + // 找到目标结点,返回之 + if (head.val === target) { + return head; + } + head = head.next; + } + // 未找到目标结点,返回 null + return null; + } ``` === "C" From 1c3b4aea8bff7a0fcfe06e2ec56d8ce361b6a3a5 Mon Sep 17 00:00:00 2001 From: danielsss Date: Sat, 7 Jan 2023 16:46:18 +1100 Subject: [PATCH 115/158] Added .editorconfig for default code format --- .editorconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..3ccd9ac0c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +[codes/**.{java,js,ts,go,py,cpp,c,cs,swift,zig}] +charset = utf-8 +indent_style = space +indent_size = 2 From f29f6c460ee5bbd4dc91373730d6f02741eb15a1 Mon Sep 17 00:00:00 2001 From: danielsss Date: Sat, 7 Jan 2023 16:47:45 +1100 Subject: [PATCH 116/158] Added .editorconfig for default code format --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 3ccd9ac0c..f57b4f2fb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,4 +11,4 @@ insert_final_newline = true [codes/**.{java,js,ts,go,py,cpp,c,cs,swift,zig}] charset = utf-8 indent_style = space -indent_size = 2 +indent_size = 4 From 0b778f27a112824e21f1b8b8e3b16142868a4008 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Sat, 7 Jan 2023 17:12:25 +0800 Subject: [PATCH 117/158] Update time complexity. --- .../performance_evaluation.md | 2 +- docs/chapter_computational_complexity/time_complexity.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/chapter_computational_complexity/performance_evaluation.md b/docs/chapter_computational_complexity/performance_evaluation.md index c664a2083..3244f41d8 100644 --- a/docs/chapter_computational_complexity/performance_evaluation.md +++ b/docs/chapter_computational_complexity/performance_evaluation.md @@ -16,7 +16,7 @@ comments: true - **时间效率** ,即算法的运行速度的快慢。 - **空间效率** ,即算法占用的内存空间大小。 -数据结构与算法追求“运行得快、内存占用少”,而如何去评价算法效率则是非常重要的问题,因为只有知道如何评价算法,才能去做算法之间的对比分析,以及优化算法设计。 +数据结构与算法追求“运行速度快、占用内存少”,而如何去评价算法效率则是非常重要的问题,因为只有知道如何评价算法,才能去做算法之间的对比分析,以及优化算法设计。 ## 效率评估方法 diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 42d43624b..03e59e256 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -365,9 +365,9 @@ $$ **时间复杂度可以有效评估算法效率。** 算法 `B` 运行时间的增长是线性的,在 $n > 1$ 时慢于算法 `A` ,在 $n > 1000000$ 时慢于算法 `C` 。实质上,只要输入数据大小 $n$ 足够大,复杂度为「常数阶」的算法一定优于「线性阶」的算法,这也正是时间增长趋势的含义。 -**时间复杂度分析将统计「计算操作的运行时间」简化为统计「计算操作的数量」。** 这是因为,无论是运行平台、还是计算操作类型,都与算法运行时间的增长趋势无关。因此,我们可以简单地将所有计算操作的执行时间统一看作是相同的“单位时间”。 +**时间复杂度的推算方法更加简便。** 在时间复杂度分析中,我们可以将统计「计算操作的运行时间」简化为统计「计算操作的数量」,这是因为,无论是运行平台还是计算操作类型,都与算法运行时间的增长趋势无关。因而,我们可以简单地将所有计算操作的执行时间统一看作是相同的“单位时间”,这样的简化做法大大降低了估算难度。 -**时间复杂度也存在一定的局限性。** 比如,虽然算法 `A` 和 `C` 的时间复杂度相同,但是实际的运行时间有非常大的差别。再比如,虽然算法 `B` 比 `C` 的时间复杂度要更高,但在输入数据大小 $n$ 比较小时,算法 `B` 是要明显优于算法 `C` 的。即使存在这些问题,计算复杂度仍然是评判算法效率的最有效、最常用方法。 +**时间复杂度也存在一定的局限性。** 比如,虽然算法 `A` 和 `C` 的时间复杂度相同,但是实际的运行时间有非常大的差别。再比如,虽然算法 `B` 比 `C` 的时间复杂度要更高,但在输入数据大小 $n$ 比较小时,算法 `B` 是要明显优于算法 `C` 的。对于以上情况,我们很难仅凭时间复杂度来判定算法效率高低。然而,即使存在这些问题,计算复杂度仍然是评判算法效率的最有效且常用的方法。 ## 函数渐近上界 From 694ea4f665733ca81fa0a21f72c5bae2f5aae8c5 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Sat, 7 Jan 2023 20:34:32 +0800 Subject: [PATCH 118/158] =?UTF-8?q?Modify=20`=E3=80=82**=20`=20to=20`**?= =?UTF-8?q?=E3=80=82`=20for=20better=20visualization.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/chapter_array_and_linkedlist/array.md | 18 +-- .../linked_list.md | 16 +-- docs/chapter_array_and_linkedlist/list.md | 14 +- .../performance_evaluation.md | 10 +- .../space_complexity.md | 6 +- .../space_time_tradeoff.md | 2 +- .../time_complexity.md | 18 +-- .../classification_of_data_structure.md | 6 +- .../chapter_data_structure/data_and_memory.md | 6 +- docs/chapter_heap/heap.md | 32 +++++ .../algorithms_are_everywhere.md | 6 +- docs/chapter_preface/about_the_book.md | 6 +- docs/chapter_preface/suggestions.md | 6 +- docs/chapter_searching/binary_search.md | 10 +- docs/chapter_searching/linear_search.md | 4 +- docs/chapter_sorting/quick_sort.md | 4 +- docs/chapter_stack_and_queue/deque.md | 58 ++++---- docs/chapter_stack_and_queue/queue.md | 104 +++++++------- docs/chapter_stack_and_queue/stack.md | 132 +++++++++--------- docs/chapter_tree/avl_tree.md | 2 +- docs/chapter_tree/binary_search_tree.md | 6 +- docs/chapter_tree/binary_tree.md | 6 +- 22 files changed, 252 insertions(+), 220 deletions(-) create mode 100644 docs/chapter_heap/heap.md diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 648a9c753..8befdbea6 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -14,7 +14,7 @@ comments: true 观察上图,我们发现 **数组首元素的索引为 $0$** 。你可能会想,这并不符合日常习惯,首个元素的索引为什么不是 $1$ 呢,这不是更加自然吗?我认同你的想法,但请先记住这个设定,后面讲内存地址计算时,我会尝试解答这个问题。 -**数组有多种初始化写法。** 根据实际需要,选代码最短的那一种就好。 +**数组有多种初始化写法**。根据实际需要,选代码最短的那一种就好。 === "Java" @@ -83,7 +83,7 @@ comments: true ## 数组优点 -**在数组中访问元素非常高效。** 这是因为在数组中,计算元素的内存地址非常容易。给定数组首个元素的地址、和一个元素的索引,利用以下公式可以直接计算得到该元素的内存地址,从而直接访问此元素。 +**在数组中访问元素非常高效**。这是因为在数组中,计算元素的内存地址非常容易。给定数组首个元素的地址、和一个元素的索引,利用以下公式可以直接计算得到该元素的内存地址,从而直接访问此元素。 ![array_memory_location_calculation](array.assets/array_memory_location_calculation.png) @@ -195,7 +195,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex ## 数组缺点 -**数组在初始化后长度不可变。** 由于系统无法保证数组之后的内存空间是可用的,因此数组长度无法扩展。而若希望扩容数组,则需新建一个数组,然后把原数组元素依次拷贝到新数组,在数组很大的情况下,这是非常耗时的。 +**数组在初始化后长度不可变**。由于系统无法保证数组之后的内存空间是可用的,因此数组长度无法扩展。而若希望扩容数组,则需新建一个数组,然后把原数组元素依次拷贝到新数组,在数组很大的情况下,这是非常耗时的。 === "Java" @@ -317,7 +317,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex } ``` -**数组中插入或删除元素效率低下。** 假设我们想要在数组中间某位置插入一个元素,由于数组元素在内存中是“紧挨着的”,它们之间没有空间再放任何数据。因此,我们不得不将此索引之后的所有元素都向后移动一位,然后再把元素赋值给该索引。删除元素也是类似,需要把此索引之后的元素都向前移动一位。总体看有以下缺点: +**数组中插入或删除元素效率低下**。假设我们想要在数组中间某位置插入一个元素,由于数组元素在内存中是“紧挨着的”,它们之间没有空间再放任何数据。因此,我们不得不将此索引之后的所有元素都向后移动一位,然后再把元素赋值给该索引。删除元素也是类似,需要把此索引之后的元素都向前移动一位。总体看有以下缺点: - **时间复杂度高:** 数组的插入和删除的平均时间复杂度均为 $O(N)$ ,其中 $N$ 为数组长度。 - **丢失元素:** 由于数组的长度不可变,因此在插入元素后,超出数组长度范围的元素会被丢失。 @@ -488,7 +488,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex ## 数组常用操作 -**数组遍历。** 以下介绍两种常用的遍历方法。 +**数组遍历**。以下介绍两种常用的遍历方法。 === "Java" @@ -611,7 +611,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex } ``` -**数组查找。** 通过遍历数组,查找数组内的指定元素,并输出对应索引。 +**数组查找**。通过遍历数组,查找数组内的指定元素,并输出对应索引。 === "Java" @@ -715,8 +715,8 @@ elementAddr = firtstElementAddr + elementLength * elementIndex ## 数组典型应用 -**随机访问。** 如果我们想要随机抽取一些样本,那么可以用数组存储,并生成一个随机序列,根据索引实现样本的随机抽取。 +**随机访问**。如果我们想要随机抽取一些样本,那么可以用数组存储,并生成一个随机序列,根据索引实现样本的随机抽取。 -**二分查找。** 例如前文查字典的例子,我们可以将字典中的所有字按照拼音顺序存储在数组中,然后使用与日常查纸质字典相同的“翻开中间,排除一半”的方式,来实现一个查电子字典的算法。 +**二分查找**。例如前文查字典的例子,我们可以将字典中的所有字按照拼音顺序存储在数组中,然后使用与日常查纸质字典相同的“翻开中间,排除一半”的方式,来实现一个查电子字典的算法。 -**深度学习。** 神经网络中大量使用了向量、矩阵、张量之间的线性代数运算,这些数据都是以数组的形式构建的。数组是神经网络编程中最常使用的数据结构。 +**深度学习**。神经网络中大量使用了向量、矩阵、张量之间的线性代数运算,这些数据都是以数组的形式构建的。数组是神经网络编程中最常使用的数据结构。 diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index 5b6888c06..f054802d4 100644 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -114,7 +114,7 @@ comments: true **尾结点指向什么?** 我们一般将链表的最后一个结点称为「尾结点」,其指向的是「空」,在 Java / C++ / Python 中分别记为 `null` / `nullptr` / `None` 。在不引起歧义下,本书都使用 `null` 来表示空。 -**链表初始化方法。** 建立链表分为两步,第一步是初始化各个结点对象,第二步是构建引用指向关系。完成后,即可以从链表的首个结点(即头结点)出发,访问其余所有的结点。 +**链表初始化方法**。建立链表分为两步,第一步是初始化各个结点对象,第二步是构建引用指向关系。完成后,即可以从链表的首个结点(即头结点)出发,访问其余所有的结点。 !!! tip @@ -248,7 +248,7 @@ comments: true ## 链表优点 -**在链表中,插入与删除结点的操作效率高。** 例如,如果想在链表中间的两个结点 `A` , `B` 之间插入一个新结点 `P` ,我们只需要改变两个结点指针即可,时间复杂度为 $O(1)$ ,相比数组的插入操作高效很多。在链表中删除某个结点也很方便,只需要改变一个结点指针即可。 +**在链表中,插入与删除结点的操作效率高**。例如,如果想在链表中间的两个结点 `A` , `B` 之间插入一个新结点 `P` ,我们只需要改变两个结点指针即可,时间复杂度为 $O(1)$ ,相比数组的插入操作高效很多。在链表中删除某个结点也很方便,只需要改变一个结点指针即可。 ![linkedlist_insert_remove_node](linked_list.assets/linkedlist_insert_remove_node.png) @@ -412,7 +412,7 @@ comments: true ## 链表缺点 -**链表访问结点效率低。** 上节提到,数组可以在 $O(1)$ 时间下访问任意元素,但链表无法直接访问任意结点。这是因为计算机需要从头结点出发,一个一个地向后遍历到目标结点。例如,倘若想要访问链表索引为 `index` (即第 `index + 1` 个)的结点,那么需要 `index` 次访问操作。 +**链表访问结点效率低**。上节提到,数组可以在 $O(1)$ 时间下访问任意元素,但链表无法直接访问任意结点。这是因为计算机需要从头结点出发,一个一个地向后遍历到目标结点。例如,倘若想要访问链表索引为 `index` (即第 `index + 1` 个)的结点,那么需要 `index` 次访问操作。 === "Java" @@ -520,11 +520,11 @@ comments: true } ``` -**链表的内存占用多。** 链表以结点为单位,每个结点除了保存值外,还需额外保存指针(引用)。这意味着同样数据量下,链表比数组需要占用更多内存空间。 +**链表的内存占用多**。链表以结点为单位,每个结点除了保存值外,还需额外保存指针(引用)。这意味着同样数据量下,链表比数组需要占用更多内存空间。 ## 链表常用操作 -**遍历链表查找。** 遍历链表,查找链表内值为 `target` 的结点,输出结点在链表中的索引。 +**遍历链表查找**。遍历链表,查找链表内值为 `target` 的结点,输出结点在链表中的索引。 === "Java" @@ -649,11 +649,11 @@ comments: true ## 常见链表类型 -**单向链表。** 即上述介绍的普通链表。单向链表的结点有「值」和指向下一结点的「指针(引用)」两项数据。我们将首个结点称为头结点,尾结点指向 `null` 。 +**单向链表**。即上述介绍的普通链表。单向链表的结点有「值」和指向下一结点的「指针(引用)」两项数据。我们将首个结点称为头结点,尾结点指向 `null` 。 -**环形链表。** 如果我们令单向链表的尾结点指向头结点(即首尾相接),则得到一个环形链表。在环形链表中,我们可以将任意结点看作是头结点。 +**环形链表**。如果我们令单向链表的尾结点指向头结点(即首尾相接),则得到一个环形链表。在环形链表中,我们可以将任意结点看作是头结点。 -**双向链表。** 单向链表仅记录了一个方向的指针(引用),在双向链表的结点定义中,同时有指向下一结点(后继结点)和上一结点(前驱结点)的「指针(引用)」。双向链表相对于单向链表更加灵活,即可以朝两个方向遍历链表,但也需要占用更多的内存空间。 +**双向链表**。单向链表仅记录了一个方向的指针(引用),在双向链表的结点定义中,同时有指向下一结点(后继结点)和上一结点(前驱结点)的「指针(引用)」。双向链表相对于单向链表更加灵活,即可以朝两个方向遍历链表,但也需要占用更多的内存空间。 === "Java" diff --git a/docs/chapter_array_and_linkedlist/list.md b/docs/chapter_array_and_linkedlist/list.md index 749b19482..19de6d778 100644 --- a/docs/chapter_array_and_linkedlist/list.md +++ b/docs/chapter_array_and_linkedlist/list.md @@ -4,13 +4,13 @@ comments: true # 列表 -**由于长度不可变,数组的实用性大大降低。** 在很多情况下,我们事先并不知道会输入多少数据,这就为数组长度的选择带来了很大困难。长度选小了,需要在添加数据中频繁地扩容数组;长度选大了,又造成内存空间的浪费。 +**由于长度不可变,数组的实用性大大降低**。在很多情况下,我们事先并不知道会输入多少数据,这就为数组长度的选择带来了很大困难。长度选小了,需要在添加数据中频繁地扩容数组;长度选大了,又造成内存空间的浪费。 为了解决此问题,诞生了一种被称为「列表 List」的数据结构。列表可以被理解为长度可变的数组,因此也常被称为「动态数组 Dynamic Array」。列表基于数组实现,继承了数组的优点,同时还可以在程序运行中实时扩容。在列表中,我们可以自由地添加元素,而不用担心超过容量限制。 ## 列表常用操作 -**初始化列表。** 我们通常会使用到“无初始值”和“有初始值”的两种初始化方法。 +**初始化列表**。我们通常会使用到“无初始值”和“有初始值”的两种初始化方法。 === "Java" @@ -91,7 +91,7 @@ comments: true List list = numbers.ToList(); ``` -**访问与更新元素。** 列表的底层数据结构是数组,因此可以在 $O(1)$ 时间内访问与更新元素,效率很高。 +**访问与更新元素**。列表的底层数据结构是数组,因此可以在 $O(1)$ 时间内访问与更新元素,效率很高。 === "Java" @@ -169,7 +169,7 @@ comments: true list[1] = 0; // 将索引 1 处的元素更新为 0 ``` -**在列表中添加、插入、删除元素。** 相对于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但是插入与删除元素的效率仍与数组一样低,时间复杂度为 $O(N)$ 。 +**在列表中添加、插入、删除元素**。相对于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但是插入与删除元素的效率仍与数组一样低,时间复杂度为 $O(N)$ 。 === "Java" @@ -317,7 +317,7 @@ comments: true list.RemoveAt(3); ``` -**遍历列表。** 与数组一样,列表可以使用索引遍历,也可以使用 `for-each` 直接遍历。 +**遍历列表**。与数组一样,列表可以使用索引遍历,也可以使用 `for-each` 直接遍历。 === "Java" @@ -437,7 +437,7 @@ comments: true } ``` -**拼接两个列表。** 再创建一个新列表 `list1` ,我们可以将其中一个列表拼接到另一个的尾部。 +**拼接两个列表**。再创建一个新列表 `list1` ,我们可以将其中一个列表拼接到另一个的尾部。 === "Java" @@ -502,7 +502,7 @@ comments: true list.AddRange(list1); // 将列表 list1 拼接到 list 之后 ``` -**排序列表。** 排序也是常用的方法之一,完成列表排序后,我们就可以使用在数组类算法题中经常考察的「二分查找」和「双指针」算法了。 +**排序列表**。排序也是常用的方法之一,完成列表排序后,我们就可以使用在数组类算法题中经常考察的「二分查找」和「双指针」算法了。 === "Java" diff --git a/docs/chapter_computational_complexity/performance_evaluation.md b/docs/chapter_computational_complexity/performance_evaluation.md index 3244f41d8..f75b4d8d4 100644 --- a/docs/chapter_computational_complexity/performance_evaluation.md +++ b/docs/chapter_computational_complexity/performance_evaluation.md @@ -8,8 +8,8 @@ comments: true 在开始学习算法之前,我们首先要想清楚算法的设计目标是什么,或者说,如何来评判算法的好与坏。整体上看,我们设计算法时追求两个层面的目标。 -1. **找到问题解法。** 算法需要能够在规定的输入范围下,可靠地求得问题的正确解。 -2. **寻求最优解法。** 同一个问题可能存在多种解法,而我们希望算法效率尽可能的高。 +1. **找到问题解法**。算法需要能够在规定的输入范围下,可靠地求得问题的正确解。 +2. **寻求最优解法**。同一个问题可能存在多种解法,而我们希望算法效率尽可能的高。 换言之,在可以解决问题的前提下,算法效率则是主要评价维度,包括: @@ -24,9 +24,9 @@ comments: true 假设我们现在有算法 A 和 算法 B ,都能够解决同一问题,现在需要对比两个算法之间的效率。我们能够想到的最直接的方式,就是找一台计算机,把两个算法都完整跑一遍,并监控记录运行时间和内存占用情况。这种评估方式能够反映真实情况,但是也存在很大的硬伤。 -**难以排除测试环境的干扰因素。** 硬件配置会影响到算法的性能表现。例如,在某台计算机中,算法 A 比算法 B 运行时间更短;但换到另一台配置不同的计算机中,可能会得到相反的测试结果。这意味着我们需要在各种机器上展开测试,而这是不现实的。 +**难以排除测试环境的干扰因素**。硬件配置会影响到算法的性能表现。例如,在某台计算机中,算法 A 比算法 B 运行时间更短;但换到另一台配置不同的计算机中,可能会得到相反的测试结果。这意味着我们需要在各种机器上展开测试,而这是不现实的。 -**展开完整测试非常耗费资源。** 随着输入数据量的大小变化,算法会呈现出不同的效率表现。比如,有可能输入数据量较小时,算法 A 运行时间短于算法 B ,而在输入数据量较大时,测试结果截然相反。因此,若想要达到具有说服力的对比结果,那么需要输入各种体量数据,这样的测试需要占用大量计算资源。 +**展开完整测试非常耗费资源**。随着输入数据量的大小变化,算法会呈现出不同的效率表现。比如,有可能输入数据量较小时,算法 A 运行时间短于算法 B ,而在输入数据量较大时,测试结果截然相反。因此,若想要达到具有说服力的对比结果,那么需要输入各种体量数据,这样的测试需要占用大量计算资源。 ### 理论估算 @@ -34,7 +34,7 @@ comments: true **复杂度分析评估随着输入数据量的增长,算法的运行时间和占用空间的增长趋势** 。根据时间和空间两方面,复杂度可分为「时间复杂度 Time Complexity」和「空间复杂度 Space Complexity」。 -**复杂度分析克服了实际测试方法的弊端。** 一是独立于测试环境,分析结果适用于所有运行平台。二是可以体现不同数据量下的算法效率,尤其是可以反映大数据量下的算法性能。 +**复杂度分析克服了实际测试方法的弊端**。一是独立于测试环境,分析结果适用于所有运行平台。二是可以体现不同数据量下的算法效率,尤其是可以反映大数据量下的算法性能。 ## 复杂度分析的重要性 diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index 1d561792b..1756f2e24 100644 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -208,8 +208,8 @@ comments: true **最差空间复杂度中的“最差”有两层含义**,分别为输入数据的最差分布、算法运行中的最差时间点。 -- **以最差输入数据为准。** 当 $n < 10$ 时,空间复杂度为 $O(1)$ ;但是当 $n > 10$ 时,初始化的数组 `nums` 使用 $O(n)$ 空间;因此最差空间复杂度为 $O(n)$ ; -- **以算法运行过程中的峰值内存为准。** 程序在执行最后一行之前,使用 $O(1)$ 空间;当初始化数组 `nums` 时,程序使用 $O(n)$ 空间;因此最差空间复杂度为 $O(n)$ ; +- **以最差输入数据为准**。当 $n < 10$ 时,空间复杂度为 $O(1)$ ;但是当 $n > 10$ 时,初始化的数组 `nums` 使用 $O(n)$ 空间;因此最差空间复杂度为 $O(n)$ ; +- **以算法运行过程中的峰值内存为准**。程序在执行最后一行之前,使用 $O(1)$ 空间;当初始化数组 `nums` 时,程序使用 $O(n)$ 空间;因此最差空间复杂度为 $O(n)$ ; === "Java" @@ -301,7 +301,7 @@ comments: true } ``` -**在递归函数中,需要注意统计栈帧空间。** 例如函数 `loop()`,在循环中调用了 $n$ 次 `function()` ,每轮中的 `function()` 都返回并释放了栈帧空间,因此空间复杂度仍为 $O(1)$ 。而递归函数 `recur()` 在运行中会同时存在 $n$ 个未返回的 `recur()` ,从而使用 $O(n)$ 的栈帧空间。 +**在递归函数中,需要注意统计栈帧空间**。例如函数 `loop()`,在循环中调用了 $n$ 次 `function()` ,每轮中的 `function()` 都返回并释放了栈帧空间,因此空间复杂度仍为 $O(1)$ 。而递归函数 `recur()` 在运行中会同时存在 $n$ 个未返回的 `recur()` ,从而使用 $O(n)$ 的栈帧空间。 === "Java" diff --git a/docs/chapter_computational_complexity/space_time_tradeoff.md b/docs/chapter_computational_complexity/space_time_tradeoff.md index 0b86c11ca..49cf33778 100644 --- a/docs/chapter_computational_complexity/space_time_tradeoff.md +++ b/docs/chapter_computational_complexity/space_time_tradeoff.md @@ -6,7 +6,7 @@ comments: true 理想情况下,我们希望算法的时间复杂度和空间复杂度都能够达到最优,而实际上,同时优化时间复杂度和空间复杂度是非常困难的。 -**降低时间复杂度,往往是以提升空间复杂度为代价的,反之亦然。** 我们把牺牲内存空间来提升算法运行速度的思路称为「以空间换时间」;反之,称之为「以时间换空间」。选择哪种思路取决于我们更看重哪个方面。 +**降低时间复杂度,往往是以提升空间复杂度为代价的,反之亦然**。我们把牺牲内存空间来提升算法运行速度的思路称为「以空间换时间」;反之,称之为「以时间换空间」。选择哪种思路取决于我们更看重哪个方面。 大多数情况下,时间都是比空间更宝贵的,只要空间复杂度不要太离谱、能接受就行,**因此以空间换时间最为常用**。 diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 03e59e256..217981ae8 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -153,7 +153,7 @@ $$ } ``` -但实际上, **统计算法的运行时间既不合理也不现实。** 首先,我们不希望预估时间和运行平台绑定,毕竟算法需要跑在各式各样的平台之上。其次,我们很难获知每一种操作的运行时间,这为预估过程带来了极大的难度。 +但实际上, **统计算法的运行时间既不合理也不现实**。首先,我们不希望预估时间和运行平台绑定,毕竟算法需要跑在各式各样的平台之上。其次,我们很难获知每一种操作的运行时间,这为预估过程带来了极大的难度。 ## 统计时间增长趋势 @@ -363,11 +363,11 @@ $$ 相比直接统计算法运行时间,时间复杂度分析的做法有什么好处呢?以及有什么不足? -**时间复杂度可以有效评估算法效率。** 算法 `B` 运行时间的增长是线性的,在 $n > 1$ 时慢于算法 `A` ,在 $n > 1000000$ 时慢于算法 `C` 。实质上,只要输入数据大小 $n$ 足够大,复杂度为「常数阶」的算法一定优于「线性阶」的算法,这也正是时间增长趋势的含义。 +**时间复杂度可以有效评估算法效率**。算法 `B` 运行时间的增长是线性的,在 $n > 1$ 时慢于算法 `A` ,在 $n > 1000000$ 时慢于算法 `C` 。实质上,只要输入数据大小 $n$ 足够大,复杂度为「常数阶」的算法一定优于「线性阶」的算法,这也正是时间增长趋势的含义。 -**时间复杂度的推算方法更加简便。** 在时间复杂度分析中,我们可以将统计「计算操作的运行时间」简化为统计「计算操作的数量」,这是因为,无论是运行平台还是计算操作类型,都与算法运行时间的增长趋势无关。因而,我们可以简单地将所有计算操作的执行时间统一看作是相同的“单位时间”,这样的简化做法大大降低了估算难度。 +**时间复杂度的推算方法更加简便**。在时间复杂度分析中,我们可以将统计「计算操作的运行时间」简化为统计「计算操作的数量」,这是因为,无论是运行平台还是计算操作类型,都与算法运行时间的增长趋势无关。因而,我们可以简单地将所有计算操作的执行时间统一看作是相同的“单位时间”,这样的简化做法大大降低了估算难度。 -**时间复杂度也存在一定的局限性。** 比如,虽然算法 `A` 和 `C` 的时间复杂度相同,但是实际的运行时间有非常大的差别。再比如,虽然算法 `B` 比 `C` 的时间复杂度要更高,但在输入数据大小 $n$ 比较小时,算法 `B` 是要明显优于算法 `C` 的。对于以上情况,我们很难仅凭时间复杂度来判定算法效率高低。然而,即使存在这些问题,计算复杂度仍然是评判算法效率的最有效且常用的方法。 +**时间复杂度也存在一定的局限性**。比如,虽然算法 `A` 和 `C` 的时间复杂度相同,但是实际的运行时间有非常大的差别。再比如,虽然算法 `B` 比 `C` 的时间复杂度要更高,但在输入数据大小 $n$ 比较小时,算法 `B` 是要明显优于算法 `C` 的。对于以上情况,我们很难仅凭时间复杂度来判定算法效率高低。然而,即使存在这些问题,计算复杂度仍然是评判算法效率的最有效且常用的方法。 ## 函数渐近上界 @@ -538,9 +538,9 @@ $T(n)$ 是个一次函数,说明时间增长趋势是线性的,因此易得 对着代码,从上到下一行一行地计数即可。然而,**由于上述 $c \cdot f(n)$ 中的常数项 $c$ 可以取任意大小,因此操作数量 $T(n)$ 中的各种系数、常数项都可以被忽略**。根据此原则,可以总结出以下计数偷懒技巧: -1. **跳过数量与 $n$ 无关的操作。** 因为他们都是 $T(n)$ 中的常数项,对时间复杂度不产生影响。 -2. **省略所有系数。** 例如,循环 $2n$ 次、$5n + 1$ 次、……,都可以化简记为 $n$ 次,因为 $n$ 前面的系数对时间复杂度也不产生影响。 -3. **循环嵌套时使用乘法。** 总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套用上述 `1.` 和 `2.` 技巧。 +1. **跳过数量与 $n$ 无关的操作**。因为他们都是 $T(n)$ 中的常数项,对时间复杂度不产生影响。 +2. **省略所有系数**。例如,循环 $2n$ 次、$5n + 1$ 次、……,都可以化简记为 $n$ 次,因为 $n$ 前面的系数对时间复杂度也不产生影响。 +3. **循环嵌套时使用乘法**。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套用上述 `1.` 和 `2.` 技巧。 根据以下示例,使用上述技巧前、后的统计结果分别为 @@ -1004,7 +1004,7 @@ $$ !!! tip - **数据大小 $n$ 是根据输入数据的类型来确定的。** 比如,在上述示例中,我们直接将 $n$ 看作输入数据大小;以下遍历数组示例中,数据大小 $n$ 为数组的长度。 + **数据大小 $n$ 是根据输入数据的类型来确定的**。比如,在上述示例中,我们直接将 $n$ 看作输入数据大小;以下遍历数组示例中,数据大小 $n$ 为数组的长度。 === "Java" @@ -2308,7 +2308,7 @@ $$ ## 最差、最佳、平均时间复杂度 -**某些算法的时间复杂度不是恒定的,而是与输入数据的分布有关。** 举一个例子,输入一个长度为 $n$ 数组 `nums` ,其中 `nums` 由从 $1$ 至 $n$ 的数字组成,但元素顺序是随机打乱的;算法的任务是返回元素 $1$ 的索引。我们可以得出以下结论: +**某些算法的时间复杂度不是恒定的,而是与输入数据的分布有关**。举一个例子,输入一个长度为 $n$ 数组 `nums` ,其中 `nums` 由从 $1$ 至 $n$ 的数字组成,但元素顺序是随机打乱的;算法的任务是返回元素 $1$ 的索引。我们可以得出以下结论: - 当 `nums = [?, ?, ..., 1]`,即当末尾元素是 $1$ 时,则需完整遍历数组,此时达到 **最差时间复杂度 $O(n)$** ; - 当 `nums = [1, ?, ?, ...]` ,即当首个数字为 $1$ 时,无论数组多长都不需要继续遍历,此时达到 **最佳时间复杂度 $\Omega(1)$** ; diff --git a/docs/chapter_data_structure/classification_of_data_structure.md b/docs/chapter_data_structure/classification_of_data_structure.md index d4397c5da..749542eb9 100644 --- a/docs/chapter_data_structure/classification_of_data_structure.md +++ b/docs/chapter_data_structure/classification_of_data_structure.md @@ -8,7 +8,7 @@ comments: true ## 逻辑结构:线性与非线性 -**「逻辑结构」反映了数据之间的逻辑关系。** 数组和链表的数据按照顺序依次排列,反映了数据间的线性关系;树从顶至底按层级排列,反映了祖先与后代之间的派生关系;图由结点和边组成,反映了复杂网络关系。 +**「逻辑结构」反映了数据之间的逻辑关系**。数组和链表的数据按照顺序依次排列,反映了数据间的线性关系;树从顶至底按层级排列,反映了祖先与后代之间的派生关系;图由结点和边组成,反映了复杂网络关系。 我们一般将逻辑结构分为「线性」和「非线性」两种。“线性”这个概念很直观,即表明数据在逻辑关系上是排成一条线的;而如果数据之间的逻辑关系是非线形的(例如是网状或树状的),那么就是非线性数据结构。 @@ -25,13 +25,13 @@ comments: true 若感到阅读困难,建议先看完下个章节「数组与链表」,再回过头来理解物理结构的含义。 -**「物理结构」反映了数据在计算机内存中的存储方式。** 从本质上看,分别是 **数组的连续空间存储** 和 **链表的离散空间存储** 。物理结构从底层上决定了数据的访问、更新、增删等操作方法,在时间效率和空间效率方面呈现出此消彼长的特性。 +**「物理结构」反映了数据在计算机内存中的存储方式**。从本质上看,分别是 **数组的连续空间存储** 和 **链表的离散空间存储** 。物理结构从底层上决定了数据的访问、更新、增删等操作方法,在时间效率和空间效率方面呈现出此消彼长的特性。 ![classification_phisical_structure](classification_of_data_structure.assets/classification_phisical_structure.png)

Fig. 连续空间存储与离散空间存储

-**所有数据结构都是基于数组、或链表、或两者组合实现的。** 例如栈和队列,既可以使用数组实现、也可以使用链表实现,而例如哈希表,其实现同时包含了数组和链表。 +**所有数据结构都是基于数组、或链表、或两者组合实现的**。例如栈和队列,既可以使用数组实现、也可以使用链表实现,而例如哈希表,其实现同时包含了数组和链表。 - **基于数组可实现:** 栈、队列、堆、哈希表、矩阵、张量(维度 $\geq 3$ 的数组)等; - **基于链表可实现:** 栈、队列、堆、哈希表、树、图等; diff --git a/docs/chapter_data_structure/data_and_memory.md b/docs/chapter_data_structure/data_and_memory.md index c620032aa..3a11fdf36 100644 --- a/docs/chapter_data_structure/data_and_memory.md +++ b/docs/chapter_data_structure/data_and_memory.md @@ -128,12 +128,12 @@ comments: true 在计算机中,内存和硬盘是两种主要的存储硬件设备。「硬盘」主要用于长期存储数据,容量较大(通常可达到 TB 级别)、速度较慢。「内存」用于运行程序时暂存数据,速度更快,但容量较小(通常为 GB 级别)。 -**算法运行中,相关数据都被存储在内存中。** 下图展示了一个计算机内存条,其中每个黑色方块都包含一块内存空间。我们可以将内存想象成一个巨大的 Excel 表格,其中每个单元格都可以存储 1 byte 的数据,在算法运行时,所有数据都被存储在这些单元格中。 +**算法运行中,相关数据都被存储在内存中**。下图展示了一个计算机内存条,其中每个黑色方块都包含一块内存空间。我们可以将内存想象成一个巨大的 Excel 表格,其中每个单元格都可以存储 1 byte 的数据,在算法运行时,所有数据都被存储在这些单元格中。 -**系统通过「内存地址 Memory Location」来访问目标内存位置的数据。** 计算机根据特定规则给表格中每个单元格编号,保证每块内存空间都有独立的内存地址。自此,程序便通过这些地址,访问内存中的数据。 +**系统通过「内存地址 Memory Location」来访问目标内存位置的数据**。计算机根据特定规则给表格中每个单元格编号,保证每块内存空间都有独立的内存地址。自此,程序便通过这些地址,访问内存中的数据。 ![computer_memory_location](data_and_memory.assets/computer_memory_location.png)

Fig. 内存条、内存空间、内存地址

-**内存资源是设计数据结构与算法的重要考虑因素。** 内存是所有程序的公共资源,当内存被某程序占用时,不能被其它程序同时使用。我们需要根据剩余内存资源的情况来设计算法。例如,若剩余内存空间有限,则要求算法占用的峰值内存不能超过系统剩余内存;若运行的程序很多、缺少大块连续的内存空间,则要求选取的数据结构必须能够存储在离散的内存空间内。 +**内存资源是设计数据结构与算法的重要考虑因素**。内存是所有程序的公共资源,当内存被某程序占用时,不能被其它程序同时使用。我们需要根据剩余内存资源的情况来设计算法。例如,若剩余内存空间有限,则要求算法占用的峰值内存不能超过系统剩余内存;若运行的程序很多、缺少大块连续的内存空间,则要求选取的数据结构必须能够存储在离散的内存空间内。 diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md new file mode 100644 index 000000000..def4cde5b --- /dev/null +++ b/docs/chapter_heap/heap.md @@ -0,0 +1,32 @@ +# 堆 + +「堆 Heap」是一种特殊的树状数据结构,并且是一颗「完全二叉树」。堆主要分为两种: + +- 「大顶堆 Max Heap」,任意父结点的值 > 其子结点的值,因此根结点的值最大; +- 「小顶堆 Min Heap」,任意父结点的值 < 其子结点的值,因此根结点的值最小; + +(图) + +!!! tip "" + + 大顶堆和小顶堆的定义、性质、操作本质上是一样的。区别只是大顶堆在求最大值,小顶堆在求最小值。在下文中,我们将统一用「大顶堆」来举例,「小顶堆」的用法与实现可以简单地将所有 $>$ ($<$) 替换为 $<$ ($>$) 即可。 + +## 堆常用操作 + +堆的初始化。 + +获取堆顶元素。 + +添加与删除元素。 + +## 堆的实现 + +在二叉树章节中,我们讲过二叉树的数组表示方法,并且提到完全二叉树非常适合用数组来表示,因此我们一般使用「数组」来存储「堆」。 + + + +## 堆常见应用 + +- 优先队列。 +- 堆排序。 +- 获取数据 Top K 大(小)元素。 diff --git a/docs/chapter_introduction/algorithms_are_everywhere.md b/docs/chapter_introduction/algorithms_are_everywhere.md index b80a525b0..5d80f9c92 100644 --- a/docs/chapter_introduction/algorithms_are_everywhere.md +++ b/docs/chapter_introduction/algorithms_are_everywhere.md @@ -6,13 +6,13 @@ comments: true 听到“算法”这个词,我们一般会联想到数学。但实际上,大多数算法并不包含复杂的数学,而更像是在考察基本逻辑,而这些逻辑在我们日常生活中处处可见。 -在正式介绍算法之前,我想告诉你一件有趣的事:**其实,你在过去已经学会了很多算法,并且已经习惯将它们应用到日常生活中。** 接下来,我将介绍两个具体例子来佐证。 +在正式介绍算法之前,我想告诉你一件有趣的事:**其实,你在过去已经学会了很多算法,并且已经习惯将它们应用到日常生活中**。接下来,我将介绍两个具体例子来佐证。 -**例一:拼积木。** 一套积木,除了有许多部件之外,还会附送详细的拼装说明书。我们按照说明书上一步步操作,即可拼出复杂的积木模型。 +**例一:拼积木**。一套积木,除了有许多部件之外,还会附送详细的拼装说明书。我们按照说明书上一步步操作,即可拼出复杂的积木模型。 如果从数据结构与算法的角度看,大大小小的「积木」就是数据结构,而「拼装说明书」上的一系列步骤就是算法。 -**例二:查字典。** 在字典中,每个汉字都有一个对应的拼音,而字典是按照拼音的英文字母表顺序排列的。假设需要在字典中查询任意一个拼音首字母为 $r$ 的字,一般我们会这样做: +**例二:查字典**。在字典中,每个汉字都有一个对应的拼音,而字典是按照拼音的英文字母表顺序排列的。假设需要在字典中查询任意一个拼音首字母为 $r$ 的字,一般我们会这样做: 1. 打开字典大致一半页数的位置,查看此页的首字母是什么(假设为 $m$ ); 2. 由于在英文字母表中 $r$ 在 $m$ 的后面,因此应排除字典前半部分,查找范围仅剩后半部分; diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index 54cd2844c..cb926b941 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -199,19 +199,19 @@ comments: true ??? abstract "默认折叠,可以跳过" - **以实践为主。** 我们知道,学习英语期间光啃书本是远远不够的,需要多听、多说、多写,在实践中培养语感、积累经验。编程语言也是一门语言,因此学习方法也应是类似的,需要多看优秀代码、多敲键盘、多思考代码逻辑。 + **以实践为主**。我们知道,学习英语期间光啃书本是远远不够的,需要多听、多说、多写,在实践中培养语感、积累经验。编程语言也是一门语言,因此学习方法也应是类似的,需要多看优秀代码、多敲键盘、多思考代码逻辑。 本书的理论部分占少量篇幅,主要分为两类:一是基础且必要的概念知识,以培养读者对于算法的感性认识;二是重要的分类、对比或总结,这是为了帮助你站在更高视角俯瞰各个知识点,形成连点成面的效果。 实践部分主要由示例和代码组成。代码配有简要注释,复杂示例会尽可能地使用视觉化的形式呈现。我强烈建议读者对照着代码自己敲一遍,如果时间有限,也至少逐行读、复制并运行一遍,配合着讲解将代码吃透。 - **视觉化学习。** 信息时代以来,视觉化的脚步从未停止。媒体形式经历了文字短信、图文 Email 、动图、短(长)视频、交互式 Web 、3D 游戏等演变过程,信息的视觉化程度越来越高、愈加符合人类感官、信息传播效率大大提升。科技界也在向视觉化迈进,iPhone 就是一个典型例子,其相对于传统手机是高度视觉化的,包含精心设计的字体、主题配色、交互动画等。 + **视觉化学习**。信息时代以来,视觉化的脚步从未停止。媒体形式经历了文字短信、图文 Email 、动图、短(长)视频、交互式 Web 、3D 游戏等演变过程,信息的视觉化程度越来越高、愈加符合人类感官、信息传播效率大大提升。科技界也在向视觉化迈进,iPhone 就是一个典型例子,其相对于传统手机是高度视觉化的,包含精心设计的字体、主题配色、交互动画等。 近两年,短视频成为最受欢迎的信息媒介,可以在短时间内将高密度的信息“灌”给我们,有着极其舒适的观看体验。阅读则不然,读者与书本之间天然存在一种“疏离感”,我们看书会累、会走神、会停下来想其他事、会划下喜欢的句子、会思考某一片段的含义,这种疏离感给了读者与书本之间对话的可能,拓宽了想象空间。 本书作为一本入门教材,希望可以保有书本的“慢节奏”,但也会避免与读者产生过多“疏离感”,而是努力将知识完整清晰地推送到你聪明的小脑袋瓜中。我将采用视觉化的方式(例如配图、动画),尽我可能清晰易懂地讲解复杂概念和抽象示例。 - **内容精简化。** 大多数的经典教科书,会把每个主题都讲的很透彻。虽然透彻性正是其获得读者青睐的原因,但对于想要快速入门的初学者来说,这些教材的实用性不足。本书会避免引入非必要的概念、名词、定义等,也避免展开不必要的理论分析,毕竟这不是一本真正意义上的教材,主要任务是尽快地带领读者入门。 + **内容精简化**。大多数的经典教科书,会把每个主题都讲的很透彻。虽然透彻性正是其获得读者青睐的原因,但对于想要快速入门的初学者来说,这些教材的实用性不足。本书会避免引入非必要的概念、名词、定义等,也避免展开不必要的理论分析,毕竟这不是一本真正意义上的教材,主要任务是尽快地带领读者入门。 引入一些生活案例或趣味内容,非常适合作为知识点的引子或者解释的补充,但当融入过多额外元素时,内容会稍显冗长,也许反而使读者容易迷失、抓不住重点,这也是本书需要避免的。 diff --git a/docs/chapter_preface/suggestions.md b/docs/chapter_preface/suggestions.md index c0377ee34..d4dbd24bc 100644 --- a/docs/chapter_preface/suggestions.md +++ b/docs/chapter_preface/suggestions.md @@ -54,10 +54,10 @@ git clone https://github.com/krahets/hello-algo.git ## 算法学习“三步走” -**第一阶段,算法入门,也正是本书的定位。** 熟悉各种数据结构的特点、用法,学习各种算法的工作原理、用途、效率等。 +**第一阶段,算法入门,也正是本书的定位**。熟悉各种数据结构的特点、用法,学习各种算法的工作原理、用途、效率等。 -**第二阶段,刷算法题。** 可以先从热门题单开刷,推荐 [剑指 Offer](https://leetcode.cn/problem-list/xb9nqhhg/)、[LeetCode 热题 HOT 100](https://leetcode.cn/problem-list/2cktkvj/) ,先积累至少 100 道题量,熟悉大多数的算法问题。刚开始刷题时,“遗忘”是最大的困扰点,但这是很正常的,请不要担心。学习中有一种概念叫“周期性回顾”,同一道题隔段时间做一次,当做了三遍以上,往往就能牢记于心了。 +**第二阶段,刷算法题**。可以先从热门题单开刷,推荐 [剑指 Offer](https://leetcode.cn/problem-list/xb9nqhhg/)、[LeetCode 热题 HOT 100](https://leetcode.cn/problem-list/2cktkvj/) ,先积累至少 100 道题量,熟悉大多数的算法问题。刚开始刷题时,“遗忘”是最大的困扰点,但这是很正常的,请不要担心。学习中有一种概念叫“周期性回顾”,同一道题隔段时间做一次,当做了三遍以上,往往就能牢记于心了。 -**第三阶段,搭建知识体系。** 在学习方面,可以阅读算法专栏文章、解题框架、算法教材,不断地丰富知识体系。在刷题方面,可以开始采用进阶刷题方案,例如按专题分类、一题多解、一解多题等,刷题方案在社区中可以找到一些讲解,在此不做赘述。 +**第三阶段,搭建知识体系**。在学习方面,可以阅读算法专栏文章、解题框架、算法教材,不断地丰富知识体系。在刷题方面,可以开始采用进阶刷题方案,例如按专题分类、一题多解、一解多题等,刷题方案在社区中可以找到一些讲解,在此不做赘述。 ![learning_route](suggestions.assets/learning_route.png) diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index 6bc53fe5a..8827588ef 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -470,11 +470,11 @@ $$ 二分查找效率很高,体现在: -- **二分查找时间复杂度低。** 对数阶在数据量很大时具有巨大优势,例如,当数据大小 $n = 2^{20}$ 时,线性查找需要 $2^{20} = 1048576$ 轮循环,而二分查找仅需要 $\log_2 2^{20} = 20$ 轮循环。 -- **二分查找不需要额外空间。** 相对于借助额外数据结构来实现查找的算法来说,其更加节约空间使用。 +- **二分查找时间复杂度低**。对数阶在数据量很大时具有巨大优势,例如,当数据大小 $n = 2^{20}$ 时,线性查找需要 $2^{20} = 1048576$ 轮循环,而二分查找仅需要 $\log_2 2^{20} = 20$ 轮循环。 +- **二分查找不需要额外空间**。相对于借助额外数据结构来实现查找的算法来说,其更加节约空间使用。 但并不意味着所有情况下都应使用二分查找,这是因为: -- **二分查找仅适用于有序数据。** 如果输入数据是无序的,为了使用二分查找而专门执行数据排序,那么是得不偿失的,因为排序算法的时间复杂度一般为 $O(n \log n)$ ,比线性查找和二分查找都更差。再例如,对于频繁插入元素的场景,为了保持数组的有序性,需要将元素插入到特定位置,时间复杂度为 $O(n)$ ,也是非常昂贵的。 -- **二分查找仅适用于数组。** 由于在二分查找中,访问索引是 ”非连续“ 的,因此链表或者基于链表实现的数据结构都无法使用。 -- **在小数据量下,线性查找的性能更好。** 在线性查找中,每轮只需要 1 次判断操作;而在二分查找中,需要 1 次加法、1 次除法、1 ~ 3 次判断操作、1 次加法(减法),共 4 ~ 6 个单元操作;因此,在数据量 $n$ 较小时,线性查找反而比二分查找更快。 +- **二分查找仅适用于有序数据**。如果输入数据是无序的,为了使用二分查找而专门执行数据排序,那么是得不偿失的,因为排序算法的时间复杂度一般为 $O(n \log n)$ ,比线性查找和二分查找都更差。再例如,对于频繁插入元素的场景,为了保持数组的有序性,需要将元素插入到特定位置,时间复杂度为 $O(n)$ ,也是非常昂贵的。 +- **二分查找仅适用于数组**。由于在二分查找中,访问索引是 ”非连续“ 的,因此链表或者基于链表实现的数据结构都无法使用。 +- **在小数据量下,线性查找的性能更好**。在线性查找中,每轮只需要 1 次判断操作;而在二分查找中,需要 1 次加法、1 次除法、1 ~ 3 次判断操作、1 次加法(减法),共 4 ~ 6 个单元操作;因此,在数据量 $n$ 较小时,线性查找反而比二分查找更快。 diff --git a/docs/chapter_searching/linear_search.md b/docs/chapter_searching/linear_search.md index 3d592962e..1c07027e6 100644 --- a/docs/chapter_searching/linear_search.md +++ b/docs/chapter_searching/linear_search.md @@ -246,6 +246,6 @@ comments: true ## 优点与缺点 -**线性查找的通用性极佳。** 由于线性查找是依次访问元素的,即没有跳跃访问元素,因此数组或链表皆适用。 +**线性查找的通用性极佳**。由于线性查找是依次访问元素的,即没有跳跃访问元素,因此数组或链表皆适用。 -**线性查找的时间复杂度太高。** 在数据量 $n$ 很大时,查找效率很低。 +**线性查找的时间复杂度太高**。在数据量 $n$ 很大时,查找效率很低。 diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index b7b607f27..ebc161caa 100644 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -383,7 +383,7 @@ comments: true ## 基准数优化 -**普通快速排序在某些输入下的时间效率变差。** 举个极端例子,假设输入数组是完全倒序的,由于我们选取最左端元素为基准数,那么在哨兵划分完成后,基准数被交换至数组最右端,从而 **左子数组长度为 $n - 1$ 、右子数组长度为 $0$** 。这样进一步递归下去,**每轮哨兵划分后的右子数组长度都为 $0$** ,分治策略失效,快速排序退化为「冒泡排序」了。 +**普通快速排序在某些输入下的时间效率变差**。举个极端例子,假设输入数组是完全倒序的,由于我们选取最左端元素为基准数,那么在哨兵划分完成后,基准数被交换至数组最右端,从而 **左子数组长度为 $n - 1$ 、右子数组长度为 $0$** 。这样进一步递归下去,**每轮哨兵划分后的右子数组长度都为 $0$** ,分治策略失效,快速排序退化为「冒泡排序」了。 为了尽量避免这种情况发生,我们可以优化一下基准数的选取策略。首先,在哨兵划分中,我们可以 **随机选取一个元素作为基准数** 。但如果运气很差,每次都选择到比较差的基准数,那么效率依然不好。 @@ -576,7 +576,7 @@ comments: true ## 尾递归优化 -**普通快速排序在某些输入下的空间效率变差。** 仍然以完全倒序的输入数组为例,由于每轮哨兵划分后右子数组长度为 0 ,那么将形成一个高度为 $n - 1$ 的递归树,此时使用的栈帧空间大小劣化至 $O(n)$ 。 +**普通快速排序在某些输入下的空间效率变差**。仍然以完全倒序的输入数组为例,由于每轮哨兵划分后右子数组长度为 0 ,那么将形成一个高度为 $n - 1$ 的递归树,此时使用的栈帧空间大小劣化至 $O(n)$ 。 为了避免栈帧空间的累积,我们可以在每轮哨兵排序完成后,判断两个子数组的长度大小,仅递归排序较短的子数组。由于较短的子数组长度不会超过 $\frac{n}{2}$ ,因此这样做能保证递归深度不超过 $\log n$ ,即最差空间复杂度被优化至 $O(\log n)$ 。 diff --git a/docs/chapter_stack_and_queue/deque.md b/docs/chapter_stack_and_queue/deque.md index 72106360f..c178c0e0a 100644 --- a/docs/chapter_stack_and_queue/deque.md +++ b/docs/chapter_stack_and_queue/deque.md @@ -12,7 +12,7 @@ comments: true ## 双向队列常用操作 -双向队列的常用操作见下表,方法名需根据编程语言设定来具体确定。 +双向队列的常用操作见下表(方法命名以 Java 为例)。

Table. 双向队列的常用操作

@@ -38,25 +38,25 @@ comments: true ```java title="deque.java" /* 初始化双向队列 */ Deque deque = new LinkedList<>(); - + /* 元素入队 */ deque.offerLast(2); // 添加至队尾 deque.offerLast(5); deque.offerLast(4); deque.offerFirst(3); // 添加至队首 deque.offerFirst(1); - + /* 访问元素 */ int peekFirst = deque.peekFirst(); // 队首元素 int peekLast = deque.peekLast(); // 队尾元素 - + /* 元素出队 */ int pollFirst = deque.pollFirst(); // 队首元素出队 int pollLast = deque.pollLast(); // 队尾元素出队 - + /* 获取双向队列的长度 */ int size = deque.size(); - + /* 判断双向队列是否为空 */ boolean isEmpty = deque.isEmpty(); ``` @@ -66,25 +66,25 @@ comments: true ```cpp title="deque.cpp" /* 初始化双向队列 */ deque deque; - + /* 元素入队 */ deque.push_back(2); // 添加至队尾 deque.push_back(5); deque.push_back(4); deque.push_front(3); // 添加至队首 deque.push_front(1); - + /* 访问元素 */ int front = deque.front(); // 队首元素 int back = deque.back(); // 队尾元素 - + /* 元素出队 */ deque.pop_front(); // 队首元素出队 deque.pop_back(); // 队尾元素出队 - + /* 获取双向队列的长度 */ int size = deque.size(); - + /* 判断双向队列是否为空 */ bool empty = deque.empty(); ``` @@ -94,25 +94,25 @@ comments: true ```python title="deque.py" """ 初始化双向队列 """ duque = deque() - + """ 元素入队 """ duque.append(2) # 添加至队尾 duque.append(5) duque.append(4) duque.appendleft(3) # 添加至队首 duque.appendleft(1) - + """ 访问元素 """ front = duque[0] # 队首元素 rear = duque[-1] # 队尾元素 - + """ 元素出队 """ pop_front = duque.popleft() # 队首元素出队 pop_rear = duque.pop() # 队尾元素出队 - + """ 获取双向队列的长度 """ size = len(duque) - + """ 判断双向队列是否为空 """ is_empty = len(duque) == 0 ``` @@ -123,25 +123,25 @@ comments: true /* 初始化双向队列 */ // 在 Go 中,将 list 作为双向队列使用 deque := list.New() - + /* 元素入队 */ deque.PushBack(2) // 添加至队尾 deque.PushBack(5) deque.PushBack(4) deque.PushFront(3) // 添加至队首 deque.PushFront(1) - + /* 访问元素 */ front := deque.Front() // 队首元素 rear := deque.Back() // 队尾元素 - + /* 元素出队 */ deque.Remove(front) // 队首元素出队 deque.Remove(rear) // 队尾元素出队 - + /* 获取双向队列的长度 */ size := deque.Len() - + /* 判断双向队列是否为空 */ isEmpty := deque.Len() == 0 ``` @@ -149,19 +149,19 @@ comments: true === "JavaScript" ```js title="deque.js" - + ``` === "TypeScript" ```typescript title="deque.ts" - + ``` === "C" ```c title="deque.c" - + ``` === "C#" @@ -170,25 +170,25 @@ comments: true /* 初始化双向队列 */ // 在 C# 中,将链表 LinkedList 看作双向队列来使用 LinkedList deque = new LinkedList(); - + /* 元素入队 */ deque.AddLast(2); // 添加至队尾 deque.AddLast(5); deque.AddLast(4); deque.AddFirst(3); // 添加至队首 deque.AddFirst(1); - + /* 访问元素 */ int peekFirst = deque.First.Value; // 队首元素 int peekLast = deque.Last.Value; // 队尾元素 - + /* 元素出队 */ deque.RemoveFirst(); // 队首元素出队 deque.RemoveLast(); // 队尾元素出队 - + /* 获取双向队列的长度 */ int size = deque.Count; - + /* 判断双向队列是否为空 */ bool isEmpty = deque.Count == 0; ``` diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index cbf854f9c..8a964d3b7 100644 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -14,7 +14,7 @@ comments: true ## 队列常用操作 -队列的常用操作见下表,方法命名需根据编程语言的设定来具体确定。 +队列的常用操作见下表(方法命名以 Java 为例)。

Table. 队列的常用操作

@@ -37,23 +37,23 @@ comments: true ```java title="queue.java" /* 初始化队列 */ Queue queue = new LinkedList<>(); - + /* 元素入队 */ queue.offer(1); queue.offer(3); queue.offer(2); queue.offer(5); queue.offer(4); - + /* 访问队首元素 */ int peek = queue.peek(); - + /* 元素出队 */ int poll = queue.poll(); - + /* 获取队列的长度 */ int size = queue.size(); - + /* 判断队列是否为空 */ boolean isEmpty = queue.isEmpty(); ``` @@ -91,23 +91,23 @@ comments: true # 在 Python 中,我们一般将双向队列类 deque 看作队列使用 # 虽然 queue.Queue() 是纯正的队列类,但不太好用,因此不建议 que = collections.deque() - + """ 元素入队 """ que.append(1) que.append(3) que.append(2) que.append(5) que.append(4) - + """ 访问队首元素 """ front = que[0]; - + """ 元素出队 """ pop = que.popleft() - + """ 获取队列的长度 """ size = len(que) - + """ 判断队列是否为空 """ is_empty = len(que) == 0 ``` @@ -118,24 +118,24 @@ comments: true /* 初始化队列 */ // 在 Go 中,将 list 作为队列来使用 queue := list.New() - + /* 元素入队 */ queue.PushBack(1) queue.PushBack(3) queue.PushBack(2) queue.PushBack(5) queue.PushBack(4) - + /* 访问队首元素 */ peek := queue.Front() - + /* 元素出队 */ poll := queue.Front() queue.Remove(poll) - + /* 获取队列的长度 */ size := queue.Len() - + /* 判断队列是否为空 */ isEmpty := queue.Len() == 0 ``` @@ -146,24 +146,24 @@ comments: true /* 初始化队列 */ // JavaScript 没有内置的队列,可以把 Array 当作队列来使用 const queue = []; - + /* 元素入队 */ queue.push(1); queue.push(3); queue.push(2); queue.push(5); queue.push(4); - + /* 访问队首元素 */ const peek = queue[0]; - + /* 元素出队 */ // 底层是数组,因此 shift() 方法的时间复杂度为 O(n) const poll = queue.shift(); - + /* 获取队列的长度 */ const size = queue.length; - + /* 判断队列是否为空 */ const empty = queue.length === 0; ``` @@ -174,24 +174,24 @@ comments: true /* 初始化队列 */ // TypeScript 没有内置的队列,可以把 Array 当作队列来使用 const queue: number[] = []; - + /* 元素入队 */ queue.push(1); queue.push(3); queue.push(2); queue.push(5); queue.push(4); - + /* 访问队首元素 */ const peek = queue[0]; - + /* 元素出队 */ // 底层是数组,因此 shift() 方法的时间复杂度为 O(n) const poll = queue.shift(); - + /* 获取队列的长度 */ const size = queue.length; - + /* 判断队列是否为空 */ const empty = queue.length === 0; ``` @@ -199,7 +199,7 @@ comments: true === "C" ```c title="queue.c" - + ``` === "C#" @@ -207,23 +207,23 @@ comments: true ```csharp title="queue.cs" /* 初始化队列 */ Queue queue = new(); - + /* 元素入队 */ queue.Enqueue(1); queue.Enqueue(3); queue.Enqueue(2); queue.Enqueue(5); queue.Enqueue(4); - + /* 访问队首元素 */ int peek = queue.Peek(); - + /* 元素出队 */ int poll = queue.Dequeue(); - + /* 获取队列的长度 */ int size = queue.Count(); - + /* 判断队列是否为空 */ bool isEmpty = queue.Count() == 0; ``` @@ -243,7 +243,7 @@ comments: true class LinkedListQueue { private ListNode front, rear; // 头结点 front ,尾结点 rear private int queSize = 0; - + public LinkedListQueue() { front = null; rear = null; @@ -296,7 +296,7 @@ comments: true private: ListNode *front, *rear; // 头结点 front ,尾结点 rear int queSize; - + public: LinkedListQueue() { front = nullptr; @@ -355,15 +355,15 @@ comments: true self.__front = None # 头结点 front self.__rear = None # 尾结点 rear self.__size = 0 - + """ 获取队列的长度 """ def size(self): return self.__size - + """ 判断队列是否为空 """ def is_empty(self): return not self.__front - + """ 入队 """ def push(self, num): # 尾结点后添加 num @@ -377,7 +377,7 @@ comments: true self.__rear.next = node self.__rear = node self.__size += 1 - + """ 出队 """ def poll(self): num = self.peek() @@ -385,7 +385,7 @@ comments: true self.__front = self.__front.next self.__size -= 1 return num - + """ 访问队首元素 """ def peek(self): if self.size() == 0: @@ -548,7 +548,7 @@ comments: true === "C" ```c title="linkedlist_queue.c" - + ``` === "C#" @@ -630,7 +630,7 @@ comments: true private int[] nums; // 用于存储队列元素的数组 private int front = 0; // 头指针,指向队首 private int rear = 0; // 尾指针,指向队尾 + 1 - + public ArrayQueue(int capacity) { // 初始化数组 nums = new int[capacity]; @@ -686,7 +686,7 @@ comments: true int cap; // 队列容量 int front = 0; // 头指针,指向队首 int rear = 0; // 尾指针,指向队尾 + 1 - + public: ArrayQueue(int capacity) { // 初始化数组 @@ -741,20 +741,20 @@ comments: true self.__nums = [0] * size # 用于存储队列元素的数组 self.__front = 0 # 头指针,指向队首 self.__rear = 0 # 尾指针,指向队尾 + 1 - + """ 获取队列的容量 """ def capacity(self): return len(self.__nums) - + """ 获取队列的长度 """ def size(self): # 由于将数组看作为环形,可能 rear < front ,因此需要取余数 return (self.capacity() + self.__rear - self.__front) % self.capacity() - + """ 判断队列是否为空 """ def is_empty(self): return (self.__rear - self.__front) == 0 - + """ 入队 """ def push(self, val): if self.size() == self.capacity(): @@ -764,21 +764,21 @@ comments: true self.__nums[self.__rear] = val # 尾指针向后移动一位,越过尾部后返回到数组头部 self.__rear = (self.__rear + 1) % self.capacity() - + """ 出队 """ def poll(self): num = self.peek() # 队头指针向后移动一位,若越过尾部则返回到数组头部 self.__front = (self.__front + 1) % self.capacity() return num - + """ 访问队首元素 """ def peek(self): if self.is_empty(): print("队列为空") return False return self.__nums[self.__front] - + """ 返回列表用于打印 """ def to_list(self): res = [0] * self.size() @@ -950,7 +950,7 @@ comments: true === "C" ```c title="array_queue.c" - + ``` === "C#" @@ -1017,5 +1017,5 @@ comments: true ## 队列典型应用 -- **淘宝订单。** 购物者下单后,订单就被加入到队列之中,随后系统再根据顺序依次处理队列中的订单。在双十一时,在短时间内会产生海量的订单,如何处理「高并发」则是工程师们需要重点思考的问题。 -- **各种待办事项。** 例如打印机的任务队列、餐厅的出餐队列等等。 +- **淘宝订单**。购物者下单后,订单就被加入到队列之中,随后系统再根据顺序依次处理队列中的订单。在双十一时,在短时间内会产生海量的订单,如何处理「高并发」则是工程师们需要重点思考的问题。 +- **各种待办事项**。例如打印机的任务队列、餐厅的出餐队列等等。 diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index 825cda9f9..985a4cf66 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -16,7 +16,7 @@ comments: true ## 栈常用操作 -栈的常用操作见下表,方法名需根据编程语言设定来具体确定。 +栈的常用操作见下表(方法命名以 Java 为例)。

Table. 栈的常用操作

@@ -40,23 +40,23 @@ comments: true /* 初始化栈 */ // 在 Java 中,推荐将 LinkedList 当作栈来使用 LinkedList stack = new LinkedList<>(); - + /* 元素入栈 */ stack.addLast(1); stack.addLast(3); stack.addLast(2); stack.addLast(5); stack.addLast(4); - + /* 访问栈顶元素 */ int peek = stack.peekLast(); - + /* 元素出栈 */ int pop = stack.removeLast(); - + /* 获取栈的长度 */ int size = stack.size(); - + /* 判断是否为空 */ boolean isEmpty = stack.isEmpty(); ``` @@ -66,23 +66,23 @@ comments: true ```cpp title="stack.cpp" /* 初始化栈 */ stack stack; - + /* 元素入栈 */ stack.push(1); stack.push(3); stack.push(2); stack.push(5); stack.push(4); - + /* 访问栈顶元素 */ int top = stack.top(); - + /* 元素出栈 */ stack.pop(); - + /* 获取栈的长度 */ int size = stack.size(); - + /* 判断是否为空 */ bool empty = stack.empty(); ``` @@ -93,23 +93,23 @@ comments: true """ 初始化栈 """ # Python 没有内置的栈类,可以把 List 当作栈来使用 stack = [] - + """ 元素入栈 """ stack.append(1) stack.append(3) stack.append(2) stack.append(5) stack.append(4) - + """ 访问栈顶元素 """ peek = stack[-1] - + """ 元素出栈 """ pop = stack.pop() - + """ 获取栈的长度 """ size = len(stack) - + """ 判断是否为空 """ is_empty = len(stack) == 0 ``` @@ -120,24 +120,24 @@ comments: true /* 初始化栈 */ // 在 Go 中,推荐将 Slice 当作栈来使用 var stack []int - + /* 元素入栈 */ stack = append(stack, 1) stack = append(stack, 3) stack = append(stack, 2) stack = append(stack, 5) stack = append(stack, 4) - + /* 访问栈顶元素 */ peek := stack[len(stack)-1] - + /* 元素出栈 */ pop := stack[len(stack)-1] stack = stack[:len(stack)-1] - + /* 获取栈的长度 */ size := len(stack) - + /* 判断是否为空 */ isEmpty := len(stack) == 0 ``` @@ -148,23 +148,23 @@ comments: true /* 初始化栈 */ // Javascript 没有内置的栈类,可以把 Array 当作栈来使用 const stack = []; - + /* 元素入栈 */ stack.push(1); stack.push(3); stack.push(2); stack.push(5); stack.push(4); - + /* 访问栈顶元素 */ const peek = stack[stack.length-1]; - + /* 元素出栈 */ const pop = stack.pop(); - + /* 获取栈的长度 */ const size = stack.length; - + /* 判断是否为空 */ const is_empty = stack.length === 0; ``` @@ -175,23 +175,23 @@ comments: true /* 初始化栈 */ // Typescript 没有内置的栈类,可以把 Array 当作栈来使用 const stack: number[] = []; - + /* 元素入栈 */ stack.push(1); stack.push(3); stack.push(2); stack.push(5); stack.push(4); - + /* 访问栈顶元素 */ const peek = stack[stack.length - 1]; - + /* 元素出栈 */ const pop = stack.pop(); - + /* 获取栈的长度 */ const size = stack.length; - + /* 判断是否为空 */ const is_empty = stack.length === 0; ``` @@ -199,7 +199,7 @@ comments: true === "C" ```c title="stack.c" - + ``` === "C#" @@ -207,23 +207,23 @@ comments: true ```csharp title="stack.cs" /* 初始化栈 */ Stack stack = new (); - + /* 元素入栈 */ stack.Push(1); stack.Push(3); stack.Push(2); stack.Push(5); stack.Push(4); - + /* 访问栈顶元素 */ int peek = stack.Peek(); - + /* 元素出栈 */ int pop = stack.Pop(); - + /* 获取栈的长度 */ int size = stack.Count(); - + /* 判断是否为空 */ bool isEmpty = stack.Count()==0; ``` @@ -291,7 +291,7 @@ comments: true private: ListNode* stackTop; // 将头结点作为栈顶 int stkSize; // 栈的长度 - + public: LinkedListStack() { stackTop = nullptr; @@ -338,29 +338,29 @@ comments: true def __init__(self): self.__peek = None self.__size = 0 - + """ 获取栈的长度 """ def size(self): return self.__size - + """ 判断栈是否为空 """ def is_empty(self): return not self.__peek - + """ 入栈 """ def push(self, val): node = ListNode(val) node.next = self.__peek self.__peek = node self.__size += 1 - + """ 出栈 """ def pop(self): num = self.peek() self.__peek = self.__peek.next self.__size -= 1 return num - + """ 访问栈顶元素 """ def peek(self): # 判空处理 @@ -420,21 +420,21 @@ comments: true class LinkedListStack { #stackPeek; // 将头结点作为栈顶 #stkSize = 0; // 栈的长度 - + constructor() { this.#stackPeek = null; } - + /* 获取栈的长度 */ get size() { return this.#stkSize; } - + /* 判断栈是否为空 */ isEmpty() { return this.size == 0; } - + /* 入栈 */ push(num) { const node = new ListNode(num); @@ -442,7 +442,7 @@ comments: true this.#stackPeek = node; this.#stkSize++; } - + /* 出栈 */ pop() { const num = this.peek(); @@ -453,7 +453,7 @@ comments: true this.#stkSize--; return num; } - + /* 访问栈顶元素 */ peek() { if (!this.#stackPeek) { @@ -461,7 +461,7 @@ comments: true } return this.#stackPeek.val; } - + /* 将链表转化为 Array 并返回 */ toArray() { let node = this.#stackPeek; @@ -482,21 +482,21 @@ comments: true class LinkedListStack { private stackPeek: ListNode | null; // 将头结点作为栈顶 private stkSize: number = 0; // 栈的长度 - + constructor() { this.stackPeek = null; } - + /* 获取栈的长度 */ get size(): number { return this.stkSize; } - + /* 判断栈是否为空 */ isEmpty(): boolean { return this.size == 0; } - + /* 入栈 */ push(num: number): void { const node = new ListNode(num); @@ -504,7 +504,7 @@ comments: true this.stackPeek = node; this.stkSize++; } - + /* 出栈 */ pop(): number { const num = this.peek(); @@ -515,7 +515,7 @@ comments: true this.stkSize--; return num; } - + /* 访问栈顶元素 */ peek(): number { if (!this.stackPeek) { @@ -523,7 +523,7 @@ comments: true } return this.stackPeek.val; } - + /* 将链表转化为 Array 并返回 */ toArray(): number[] { let node = this.stackPeek; @@ -540,7 +540,7 @@ comments: true === "C" ```c title="linkedlist_stack.c" - + ``` === "C#" @@ -676,24 +676,24 @@ comments: true class ArrayStack: def __init__(self): self.__stack = [] - + """ 获取栈的长度 """ def size(self): return len(self.__stack) - + """ 判断栈是否为空 """ def is_empty(self): return self.__stack == [] - + """ 入栈 """ def push(self, item): self.__stack.append(item) - + """ 出栈 """ def pop(self): assert not self.is_empty(), "栈为空" return self.__stack.pop() - + """ 访问栈顶元素 """ def peek(self): assert not self.is_empty(), "栈为空" @@ -821,7 +821,7 @@ comments: true === "C" ```c title="array_stack.c" - + ``` === "C#" @@ -876,5 +876,5 @@ comments: true ## 栈典型应用 -- **浏览器中的后退与前进、软件中的撤销与反撤销。** 每当我们打开新的网页,浏览器就讲上一个网页执行入栈,这样我们就可以通过「后退」操作来回到上一页面,后退操作实际上是在执行出栈。如果要同时支持后退和前进,那么则需要两个栈来配合实现。 -- **程序内存管理。** 每当调用函数时,系统就会在栈顶添加一个栈帧,用来记录函数的上下文信息。在递归函数中,向下递推会不断执行入栈,向上回溯阶段时出栈。 +- **浏览器中的后退与前进、软件中的撤销与反撤销**。每当我们打开新的网页,浏览器就讲上一个网页执行入栈,这样我们就可以通过「后退」操作来回到上一页面,后退操作实际上是在执行出栈。如果要同时支持后退和前进,那么则需要两个栈来配合实现。 +- **程序内存管理**。每当调用函数时,系统就会在栈顶添加一个栈帧,用来记录函数的上下文信息。在递归函数中,向下递推会不断执行入栈,向上回溯阶段时出栈。 diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index d969c66d7..fe08828c1 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -253,7 +253,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit ## AVL 树旋转 -AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影响二叉树中序遍历序列的前提下,使失衡结点重新恢复平衡。** 换言之,旋转操作既可以使树保持为「二叉搜索树」,也可以使树重新恢复为「平衡二叉树」。 +AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影响二叉树中序遍历序列的前提下,使失衡结点重新恢复平衡**。换言之,旋转操作既可以使树保持为「二叉搜索树」,也可以使树重新恢复为「平衡二叉树」。 我们将平衡因子的绝对值 $> 1$ 的结点称为「失衡结点」。根据结点的失衡情况,旋转操作分为 **右旋、左旋、先右旋后左旋、先左旋后右旋**,接下来我们来一起来看看它们是如何操作的。 diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index cc7c2f51c..60a5598bd 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -430,15 +430,15 @@ comments: true 与插入结点一样,我们需要在删除操作后维持二叉搜索树的“左子树 < 根结点 < 右子树”的性质。首先,我们需要在二叉树中执行查找操作,获取待删除结点。接下来,根据待删除结点的子结点数量,删除操作需要分为三种情况: -**待删除结点的子结点数量 $= 0$ 。** 表明待删除结点是叶结点,直接删除即可。 +**待删除结点的子结点数量 $= 0$ **。表明待删除结点是叶结点,直接删除即可。 ![bst_remove_case1](binary_search_tree.assets/bst_remove_case1.png) -**待删除结点的子结点数量 $= 1$ 。** 将待删除结点替换为其子结点。 +**待删除结点的子结点数量 $= 1$ **。将待删除结点替换为其子结点。 ![bst_remove_case2](binary_search_tree.assets/bst_remove_case2.png) -**待删除结点的子结点数量 $= 2$ 。** 删除操作分为三步: +**待删除结点的子结点数量 $= 2$ **。删除操作分为三步: 1. 找到待删除结点在 **中序遍历序列** 中的下一个结点,记为 `nex` ; 2. 在树中递归删除结点 `nex` ; diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 3e3222222..7977ab6a2 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -137,7 +137,7 @@ comments: true ## 二叉树基本操作 -**初始化二叉树。** 与链表类似,先初始化结点,再构建引用指向(即指针)。 +**初始化二叉树**。与链表类似,先初始化结点,再构建引用指向(即指针)。 === "Java" @@ -263,7 +263,7 @@ comments: true n2.right = n5; ``` -**插入与删除结点。** 与链表类似,插入与删除结点都可以通过修改指针实现。 +**插入与删除结点**。与链表类似,插入与删除结点都可以通过修改指针实现。 ![binary_tree_add_remove](binary_tree.assets/binary_tree_add_remove.png) @@ -497,7 +497,7 @@ comments: true ![array_representation_with_empty](binary_tree.assets/array_representation_with_empty.png) -回顾「完全二叉树」的满足条件,其只有最底层有空结点,并且最底层的结点尽量靠左,因而所有空结点都一定出现在层序遍历序列的末尾。**因为我们先验地确定了空位的位置,所以在使用数组表示完全二叉树时,可以省略存储“空位”**。“便于使用数组表示”也是完全二叉树受欢迎的原因之一。 +回顾「完全二叉树」的定义,其只有最底层有空结点,并且最底层的结点尽量靠左,因而所有空结点都一定出现在层序遍历序列的末尾。**因为我们先验地确定了空位的位置,所以在使用数组表示完全二叉树时,可以省略存储“空位”**。因此,完全二叉树非常适合使用数组来表示。 ![array_representation_complete_binary_tree](binary_tree.assets/array_representation_complete_binary_tree.png) From f49c6740299079d60196c106c59c5d05b72d3796 Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Thu, 5 Jan 2023 21:16:05 +0800 Subject: [PATCH 119/158] feat: add Swift codes for array article --- codes/swift/Package.swift | 2 + .../chapter_array_and_linkedlist/array.swift | 103 ++++++++++++++++++ docs/chapter_array_and_linkedlist/array.md | 91 ++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 codes/swift/chapter_array_and_linkedlist/array.swift diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index a292d9fd3..d9368354b 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -9,6 +9,7 @@ let package = Package( .executable(name: "worst_best_time_complexity", targets: ["worst_best_time_complexity"]), .executable(name: "space_complexity", targets: ["space_complexity"]), .executable(name: "leetcode_two_sum", targets: ["leetcode_two_sum"]), + .executable(name: "array", targets: ["array"]), ], targets: [ .target(name: "utils", path: "utils"), @@ -16,5 +17,6 @@ let package = Package( .executableTarget(name: "worst_best_time_complexity", path: "chapter_computational_complexity", sources: ["worst_best_time_complexity.swift"]), .executableTarget(name: "space_complexity", dependencies: ["utils"], path: "chapter_computational_complexity", sources: ["space_complexity.swift"]), .executableTarget(name: "leetcode_two_sum", path: "chapter_computational_complexity", sources: ["leetcode_two_sum.swift"]), + .executableTarget(name: "array", path: "chapter_array_and_linkedlist", sources: ["array.swift"]), ] ) diff --git a/codes/swift/chapter_array_and_linkedlist/array.swift b/codes/swift/chapter_array_and_linkedlist/array.swift new file mode 100644 index 000000000..c0c7ab9b9 --- /dev/null +++ b/codes/swift/chapter_array_and_linkedlist/array.swift @@ -0,0 +1,103 @@ +/** + * File: array.swift + * Created Time: 2023-01-05 + * Author: nuomi1 (nuomi1@qq.com) + */ + +// 随机返回一个数组元素 +func randomAccess(nums: [Int]) -> Int { + // 在区间 [0, nums.count) 中随机抽取一个数字 + let randomIndex = nums.indices.randomElement()! + // 获取并返回随机元素 + let randomNum = nums[randomIndex] + return randomNum +} + +// 扩展数组长度 +func extend(nums: [Int], enlarge: Int) -> [Int] { + // 初始化一个扩展长度后的数组 + var res = Array(repeating: 0, count: nums.count + enlarge) + // 将原数组中的所有元素复制到新数组 + for i in nums.indices { + res[i] = nums[i] + } + // 返回扩展后的新数组 + return res +} + +// 在数组的索引 index 处插入元素 num +func insert(nums: inout [Int], num: Int, index: Int) { + // 把索引 index 以及之后的所有元素向后移动一位 + for i in sequence(first: nums.count - 1, next: { $0 > index + 1 ? $0 - 1 : nil }) { + nums[i] = nums[i - 1] + } + // 将 num 赋给 index 处元素 + nums[index] = num +} + +// 删除索引 index 处元素 +func remove(nums: inout [Int], index: Int) { + let count = nums.count + // 把索引 index 之后的所有元素向前移动一位 + for i in sequence(first: index, next: { $0 < count - 1 - 1 ? $0 + 1 : nil }) { + nums[i] = nums[i + 1] + } +} + +// 遍历数组 +func traverse(nums: [Int]) { + var count = 0 + // 通过索引遍历数组 + for _ in nums.indices { + count += 1 + } + // 直接遍历数组 + for _ in nums { + count += 1 + } +} + +// 在数组中查找指定元素 +func find(nums: [Int], target: Int) -> Int { + for i in nums.indices { + if nums[i] == target { + return i + } + } + return -1 +} + +@main +enum _Array { + // Driver Code + static func main() { + // 初始化数组 + let arr = Array(repeating: 0, count: 5) + print("数组 arr = \(arr)") + var nums = [1, 3, 2, 5, 4] + print("数组 nums = \(nums)") + + // 随机访问 + let randomNum = randomAccess(nums: nums) + print("在 nums 中获取随机元素 \(randomNum)") + + // 长度扩展 + nums = extend(nums: nums, enlarge: 3) + print("将数组长度扩展至 8 ,得到 nums = \(nums)") + + // 插入元素 + insert(nums: &nums, num: 6, index: 3) + print("在索引 3 处插入数字 6 ,得到 nums = \(nums)") + + // 删除元素 + remove(nums: &nums, index: 2) + print("删除索引 2 处的元素,得到 nums = \(nums)") + + // 遍历数组 + traverse(nums: nums) + + // 查找元素 + let index = find(nums: nums, target: 3) + print("在 nums 中查找元素 3 ,得到索引 = \(index)") + } +} diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 648a9c753..5e814ea24 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -81,6 +81,14 @@ comments: true int[] nums = { 1, 3, 2, 5, 4 }; ``` +=== "Swift" + + ```swift title="array.swift" + // 初始化数组 + let arr = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0] + let nums = [1, 3, 2, 5, 4] + ``` + ## 数组优点 **在数组中访问元素非常高效。** 这是因为在数组中,计算元素的内存地址非常容易。给定数组首个元素的地址、和一个元素的索引,利用以下公式可以直接计算得到该元素的内存地址,从而直接访问此元素。 @@ -193,6 +201,19 @@ elementAddr = firtstElementAddr + elementLength * elementIndex } ``` +=== "Swift" + + ```swift title="array.swift" + // 随机返回一个数组元素 + func randomAccess(nums: [Int]) -> Int { + // 在区间 [0, nums.count) 中随机抽取一个数字 + let randomIndex = nums.indices.randomElement()! + // 获取并返回随机元素 + let randomNum = nums[randomIndex] + return randomNum + } + ``` + ## 数组缺点 **数组在初始化后长度不可变。** 由于系统无法保证数组之后的内存空间是可用的,因此数组长度无法扩展。而若希望扩容数组,则需新建一个数组,然后把原数组元素依次拷贝到新数组,在数组很大的情况下,这是非常耗时的。 @@ -317,6 +338,22 @@ elementAddr = firtstElementAddr + elementLength * elementIndex } ``` +=== "Swift" + + ```swift title="array.swift" + // 扩展数组长度 + func extend(nums: [Int], enlarge: Int) -> [Int] { + // 初始化一个扩展长度后的数组 + var res = Array(repeating: 0, count: nums.count + enlarge) + // 将原数组中的所有元素复制到新数组 + for i in nums.indices { + res[i] = nums[i] + } + // 返回扩展后的新数组 + return res + } + ``` + **数组中插入或删除元素效率低下。** 假设我们想要在数组中间某位置插入一个元素,由于数组元素在内存中是“紧挨着的”,它们之间没有空间再放任何数据。因此,我们不得不将此索引之后的所有元素都向后移动一位,然后再把元素赋值给该索引。删除元素也是类似,需要把此索引之后的元素都向前移动一位。总体看有以下缺点: - **时间复杂度高:** 数组的插入和删除的平均时间复杂度均为 $O(N)$ ,其中 $N$ 为数组长度。 @@ -486,6 +523,29 @@ elementAddr = firtstElementAddr + elementLength * elementIndex } ``` +=== "Swift" + + ```swift title="array.swift" + // 在数组的索引 index 处插入元素 num + func insert(nums: inout [Int], num: Int, index: Int) { + // 把索引 index 以及之后的所有元素向后移动一位 + for i in sequence(first: nums.count - 1, next: { $0 > index + 1 ? $0 - 1 : nil }) { + nums[i] = nums[i - 1] + } + // 将 num 赋给 index 处元素 + nums[index] = num + } + + // 删除索引 index 处元素 + func remove(nums: inout [Int], index: Int) { + let count = nums.count + // 把索引 index 之后的所有元素向前移动一位 + for i in sequence(first: index, next: { $0 < count - 1 - 1 ? $0 + 1 : nil }) { + nums[i] = nums[i + 1] + } + } + ``` + ## 数组常用操作 **数组遍历。** 以下介绍两种常用的遍历方法。 @@ -611,6 +671,23 @@ elementAddr = firtstElementAddr + elementLength * elementIndex } ``` +=== "Swift" + + ```swift title="array.swift" + // 遍历数组 + func traverse(nums: [Int]) { + var count = 0 + // 通过索引遍历数组 + for _ in nums.indices { + count += 1 + } + // 直接遍历数组 + for _ in nums { + count += 1 + } + } + ``` + **数组查找。** 通过遍历数组,查找数组内的指定元素,并输出对应索引。 === "Java" @@ -713,6 +790,20 @@ elementAddr = firtstElementAddr + elementLength * elementIndex } ``` +=== "Swift" + + ```swift title="array.swift" + // 在数组中查找指定元素 + func find(nums: [Int], target: Int) -> Int { + for i in nums.indices { + if nums[i] == target { + return i + } + } + return -1 + } + ``` + ## 数组典型应用 **随机访问。** 如果我们想要随机抽取一些样本,那么可以用数组存储,并生成一个随机序列,根据索引实现样本的随机抽取。 From dcc3b2e35b1d2fd8239e9904fea81e40ea83ad45 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Sun, 8 Jan 2023 19:03:22 +0800 Subject: [PATCH 120/158] Optimize arrToTree function in java, cpp, py, go, js, ts. --- .gitignore | 4 +- codes/cpp/chapter_tree/binary_tree_bfs.cpp | 3 +- codes/cpp/chapter_tree/binary_tree_dfs.cpp | 3 +- codes/cpp/include/TreeNode.hpp | 15 +-- codes/csharp/chapter_tree/binary_tree_bfs.cs | 3 +- codes/csharp/chapter_tree/binary_tree_dfs.cs | 3 +- codes/csharp/include/TreeNode.cs | 10 +- codes/go/chapter_tree/binary_tree_bfs_test.go | 6 +- codes/go/chapter_tree/binary_tree_dfs_test.go | 10 +- codes/go/pkg/tree_node.go | 10 +- codes/go/pkg/tree_node_test.go | 2 +- codes/java/chapter_heap/my_heap.java | 115 ++++++++++++++++++ codes/java/chapter_tree/binary_tree_bfs.java | 3 +- codes/java/chapter_tree/binary_tree_dfs.java | 3 +- codes/java/include/PrintUtil.java | 8 ++ codes/java/include/TreeNode.java | 24 +--- .../chapter_tree/binary_tree_bfs.js | 4 +- .../chapter_tree/binary_tree_dfs.js | 3 +- codes/javascript/include/TreeNode.js | 15 +-- .../python/chapter_tree/binary_search_tree.py | 2 +- codes/python/chapter_tree/binary_tree.py | 2 +- codes/python/chapter_tree/binary_tree_bfs.py | 2 +- codes/python/chapter_tree/binary_tree_dfs.py | 2 +- codes/python/include/binary_tree.py | 36 ++---- .../chapter_tree/binary_tree_bfs.ts | 2 +- .../chapter_tree/binary_tree_dfs.ts | 2 +- codes/typescript/module/TreeNode.ts | 8 +- docs/chapter_heap/heap.md | 36 ++++-- .../binary_tree_add_remove.png | Bin 77839 -> 79400 bytes 29 files changed, 222 insertions(+), 114 deletions(-) create mode 100644 codes/java/chapter_heap/my_heap.java diff --git a/.gitignore b/.gitignore index 3ff305496..e93873c95 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,5 @@ docs/overrides/ # python files __pycache__ -# iml -hello-algo.iml +# in-progress articles +docs/chapter_heap diff --git a/codes/cpp/chapter_tree/binary_tree_bfs.cpp b/codes/cpp/chapter_tree/binary_tree_bfs.cpp index 236f62449..ffc2e372c 100644 --- a/codes/cpp/chapter_tree/binary_tree_bfs.cpp +++ b/codes/cpp/chapter_tree/binary_tree_bfs.cpp @@ -30,8 +30,7 @@ vector hierOrder(TreeNode* root) { int main() { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - TreeNode* root = vecToTree(vector - { 1, 2, 3, 4, 5, 6, 7, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX }); + TreeNode* root = vecToTree(vector { 1, 2, 3, 4, 5, 6, 7 }); cout << endl << "初始化二叉树\n" << endl; PrintUtil::printTree(root); diff --git a/codes/cpp/chapter_tree/binary_tree_dfs.cpp b/codes/cpp/chapter_tree/binary_tree_dfs.cpp index 51287b736..5ed5b78fe 100644 --- a/codes/cpp/chapter_tree/binary_tree_dfs.cpp +++ b/codes/cpp/chapter_tree/binary_tree_dfs.cpp @@ -41,8 +41,7 @@ void postOrder(TreeNode* root) { int main() { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - TreeNode* root = vecToTree(vector - { 1, 2, 3, 4, 5, 6, 7, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX}); + TreeNode* root = vecToTree(vector { 1, 2, 3, 4, 5, 6, 7 }); cout << endl << "初始化二叉树\n" << endl; PrintUtil::printTree(root); diff --git a/codes/cpp/include/TreeNode.hpp b/codes/cpp/include/TreeNode.hpp index ab54c7746..82f2ce981 100644 --- a/codes/cpp/include/TreeNode.hpp +++ b/codes/cpp/include/TreeNode.hpp @@ -27,23 +27,24 @@ struct TreeNode { * @return TreeNode* */ TreeNode *vecToTree(vector list) { - if (list.empty()) { + if (list.empty()) return nullptr; - } auto *root = new TreeNode(list[0]); queue que; - size_t n = list.size(), index = 1; - while (index < n) { + que.emplace(root); + size_t n = list.size(), index = 0; + while (!que.empty()) { auto node = que.front(); que.pop(); - + if (++index >= n) break; if (index < n) { - node->left = new TreeNode(list[index++]); + node->left = new TreeNode(list[index]); que.emplace(node->left); } + if (++index >= n) break; if (index < n) { - node->right = new TreeNode(list[index++]); + node->right = new TreeNode(list[index]); que.emplace(node->right); } } diff --git a/codes/csharp/chapter_tree/binary_tree_bfs.cs b/codes/csharp/chapter_tree/binary_tree_bfs.cs index 31f707246..f0c914acb 100644 --- a/codes/csharp/chapter_tree/binary_tree_bfs.cs +++ b/codes/csharp/chapter_tree/binary_tree_bfs.cs @@ -41,8 +41,7 @@ namespace hello_algo.chapter_tree { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - TreeNode? root = TreeNode.ArrToTree(new int?[] { - 1, 2, 3, 4, 5, 6, 7, null, null, null, null, null, null, null, null}); + TreeNode? root = TreeNode.ArrToTree(new int?[] { 1, 2, 3, 4, 5, 6, 7 }); Console.WriteLine("\n初始化二叉树\n"); PrintUtil.PrintTree(root); diff --git a/codes/csharp/chapter_tree/binary_tree_dfs.cs b/codes/csharp/chapter_tree/binary_tree_dfs.cs index 6e38ebe4c..0f89cb3b2 100644 --- a/codes/csharp/chapter_tree/binary_tree_dfs.cs +++ b/codes/csharp/chapter_tree/binary_tree_dfs.cs @@ -57,8 +57,7 @@ namespace hello_algo.chapter_tree { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - TreeNode? root = TreeNode.ArrToTree(new int?[] { - 1, 2, 3, 4, 5, 6, 7, null, null, null, null, null, null, null, null}); + TreeNode? root = TreeNode.ArrToTree(new int?[] { 1, 2, 3, 4, 5, 6, 7 }); Console.WriteLine("\n初始化二叉树\n"); PrintUtil.PrintTree(root); diff --git a/codes/csharp/include/TreeNode.cs b/codes/csharp/include/TreeNode.cs index e5cacd59e..9cd0ac92c 100644 --- a/codes/csharp/include/TreeNode.cs +++ b/codes/csharp/include/TreeNode.cs @@ -19,7 +19,7 @@ namespace hello_algo.include } /** - * Generate a binary tree with an array + * Generate a binary tree given an array * @param arr * @return */ @@ -31,22 +31,22 @@ namespace hello_algo.include TreeNode root = new TreeNode((int) arr[0]); Queue queue = new Queue(); queue.Enqueue(root); - int i = 1; - while (queue.Count!=0) + int i = 0; + while (queue.Count != 0) { TreeNode node = queue.Dequeue(); + if (++i >= arr.Length) break; if (arr[i] != null) { node.left = new TreeNode((int) arr[i]); queue.Enqueue(node.left); } - i++; + if (++i >= arr.Length) break; if (arr[i] != null) { node.right = new TreeNode((int) arr[i]); queue.Enqueue(node.right); } - i++; } return root; } diff --git a/codes/go/chapter_tree/binary_tree_bfs_test.go b/codes/go/chapter_tree/binary_tree_bfs_test.go index 8d5c0aaff..d32dbdca9 100644 --- a/codes/go/chapter_tree/binary_tree_bfs_test.go +++ b/codes/go/chapter_tree/binary_tree_bfs_test.go @@ -14,11 +14,11 @@ import ( func TestLevelOrder(t *testing.T) { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - root := ArrayToTree([]int{1, 2, 3, 4, 5, 6, 7}) - fmt.Println("初始化二叉树: ") + root := ArrToTree([]int{1, 2, 3, 4, 5, 6, 7}) + fmt.Println("\n初始化二叉树: ") PrintTree(root) // 层序遍历 nums := levelOrder(root) - fmt.Println("层序遍历的结点打印序列 =", nums) + fmt.Println("\n层序遍历的结点打印序列 =", nums) } diff --git a/codes/go/chapter_tree/binary_tree_dfs_test.go b/codes/go/chapter_tree/binary_tree_dfs_test.go index 67a3e1f3a..b0db8086c 100644 --- a/codes/go/chapter_tree/binary_tree_dfs_test.go +++ b/codes/go/chapter_tree/binary_tree_dfs_test.go @@ -14,22 +14,22 @@ import ( func TestPreInPostOrderTraversal(t *testing.T) { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - root := ArrayToTree([]int{1, 2, 3, 4, 5, 6, 7}) - fmt.Println("初始化二叉树: ") + root := ArrToTree([]int{1, 2, 3, 4, 5, 6, 7}) + fmt.Println("\n初始化二叉树: ") PrintTree(root) // 前序遍历 nums = nil preOrder(root) - fmt.Println("前序遍历的结点打印序列 =", nums) + fmt.Println("\n前序遍历的结点打印序列 =", nums) // 中序遍历 nums = nil inOrder(root) - fmt.Println("中序遍历的结点打印序列 =", nums) + fmt.Println("\n中序遍历的结点打印序列 =", nums) // 后序遍历 nums = nil postOrder(root) - fmt.Println("后序遍历的结点打印序列 =", nums) + fmt.Println("\n后序遍历的结点打印序列 =", nums) } diff --git a/codes/go/pkg/tree_node.go b/codes/go/pkg/tree_node.go index 2511a7655..b1e630e67 100644 --- a/codes/go/pkg/tree_node.go +++ b/codes/go/pkg/tree_node.go @@ -22,8 +22,8 @@ func NewTreeNode(v int) *TreeNode { } } -// ArrayToTree Generate a binary tree with an array -func ArrayToTree(arr []int) *TreeNode { +// ArrToTree Generate a binary tree given an array +func ArrToTree(arr []int) *TreeNode { if len(arr) <= 0 { return nil } @@ -31,19 +31,19 @@ func ArrayToTree(arr []int) *TreeNode { // Let container.list as queue queue := list.New() queue.PushBack(root) - i := 1 + i := 0 for queue.Len() > 0 { // poll node := queue.Remove(queue.Front()).(*TreeNode) + i++ if i < len(arr) { node.Left = NewTreeNode(arr[i]) queue.PushBack(node.Left) - i++ } + i++ if i < len(arr) { node.Right = NewTreeNode(arr[i]) queue.PushBack(node.Right) - i++ } } return root diff --git a/codes/go/pkg/tree_node_test.go b/codes/go/pkg/tree_node_test.go index b4c8e0773..bb1885ee1 100644 --- a/codes/go/pkg/tree_node_test.go +++ b/codes/go/pkg/tree_node_test.go @@ -11,7 +11,7 @@ import ( func TestTreeNode(t *testing.T) { arr := []int{2, 3, 5, 6, 7} - node := ArrayToTree(arr) + node := ArrToTree(arr) // print tree PrintTree(node) diff --git a/codes/java/chapter_heap/my_heap.java b/codes/java/chapter_heap/my_heap.java new file mode 100644 index 000000000..da2dffc0a --- /dev/null +++ b/codes/java/chapter_heap/my_heap.java @@ -0,0 +1,115 @@ +/** + * File: my_heap.java + * Created Time: 2023-01-07 + * Author: Krahets (krahets@163.com) + */ + +package chapter_heap; + +import include.*; +import java.util.*; + +class MaxHeap { + private List heap; + + public MaxHeap() { + heap = new ArrayList<>(); + } + + public MaxHeap(List nums) { + // 将元素拷贝至堆中 + heap = new ArrayList<>(nums); + // 堆化除叶结点外的其他所有结点 + for (int i = parent(size() - 1); i >= 0; i--) { + heapify(i); + } + } + + /* 获取左子结点 */ + private int left(int i) { + return 2 * i + 1; + } + + /* 获取右子结点 */ + private int right(int i) { + return 2 * i + 2; + } + + /* 获取父结点 */ + private int parent(int i) { + return (i - 1) / 2; + } + + /* 交换元素 */ + private void swap(int i, int j) { + int tmp = heap.get(i); + heap.set(i, j); + heap.set(j, tmp); + } + + public int size() { + return heap.size(); + } + + public boolean isEmpty() { + return size() == 0; + } + + /* 获取堆顶元素 */ + public int peek() { + return heap.get(0); + } + + /* 元素入堆 */ + public void push(int val) { + heap.add(val); + + // 从底至顶堆化 + int i = size(); + while (true) { + int p = parent(i); + if (p < 0 || heap.get(i) > heap.get(p)) + break; + swap(i, p); + i = p; + } + } + + /* 元素出堆 */ + public int poll() { + // 判空处理 + if (isEmpty()) + throw new EmptyStackException(); + // 交换根结点与右下角(即最后一个)结点 + swap(0, size() - 1); + // 删除结点 + int val = heap.remove(size() - 1); + // 从顶至底堆化 + heapify(0); + // 返回堆顶元素 + return val; + } + + /* 从结点 i 开始,从顶至底堆化 */ + private void heapify(int i) { + while (true) { + // 判断结点 i, l, r 中的最大结点,记为 ma ; + int l = left(i), r = right(i), ma = i; + if (heap.get(l) > heap.get(ma)) ma = l; + if (heap.get(r) > heap.get(ma)) ma = r; + // 若结点 i 最大,则无需继续堆化,跳出 + if (ma == i) break; + // 交换结点 i 与结点 max + swap(i, ma); + // 循环向下堆化 + i = ma; + } + } +} + + +public class my_heap { + public static void main(String[] args) { + + } +} diff --git a/codes/java/chapter_tree/binary_tree_bfs.java b/codes/java/chapter_tree/binary_tree_bfs.java index c000eacea..450311d08 100644 --- a/codes/java/chapter_tree/binary_tree_bfs.java +++ b/codes/java/chapter_tree/binary_tree_bfs.java @@ -30,8 +30,7 @@ public class binary_tree_bfs { public static void main(String[] args) { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - TreeNode root = TreeNode.arrToTree(new Integer[] { - 1, 2, 3, 4, 5, 6, 7, null, null, null, null, null, null, null, null }); + TreeNode root = TreeNode.arrToTree(new Integer[] { 1, 2, 3, 4, 5, 6, 7 }); System.out.println("\n初始化二叉树\n"); PrintUtil.printTree(root); diff --git a/codes/java/chapter_tree/binary_tree_dfs.java b/codes/java/chapter_tree/binary_tree_dfs.java index 1d3026dfb..d1ef063b0 100644 --- a/codes/java/chapter_tree/binary_tree_dfs.java +++ b/codes/java/chapter_tree/binary_tree_dfs.java @@ -43,8 +43,7 @@ public class binary_tree_dfs { public static void main(String[] args) { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - TreeNode root = TreeNode.arrToTree(new Integer[] { - 1, 2, 3, 4, 5, 6, 7, null, null, null, null, null, null, null, null}); + TreeNode root = TreeNode.arrToTree(new Integer[] { 1, null, 3, 4, 5 }); System.out.println("\n初始化二叉树\n"); PrintUtil.printTree(root); diff --git a/codes/java/include/PrintUtil.java b/codes/java/include/PrintUtil.java index f21bc17c9..b2593ac1f 100755 --- a/codes/java/include/PrintUtil.java +++ b/codes/java/include/PrintUtil.java @@ -8,6 +8,7 @@ package include; import java.util.*; + class Trunk { Trunk prev; String str; @@ -103,4 +104,11 @@ public class PrintUtil { System.out.println(kv.getKey() + " -> " + kv.getValue()); } } + + public static void printHeap(PriorityQueue queue) { + Integer[] nums = (Integer[])queue.toArray(); + TreeNode root = TreeNode.arrToTree(nums); + + printTree(root); + } } diff --git a/codes/java/include/TreeNode.java b/codes/java/include/TreeNode.java index b98252665..11d457a10 100644 --- a/codes/java/include/TreeNode.java +++ b/codes/java/include/TreeNode.java @@ -22,7 +22,7 @@ public class TreeNode { } /** - * Generate a binary tree with an array + * Generate a binary tree given an array * @param arr * @return */ @@ -32,19 +32,19 @@ public class TreeNode { TreeNode root = new TreeNode(arr[0]); Queue queue = new LinkedList<>() {{ add(root); }}; - int i = 1; + int i = 0; while(!queue.isEmpty()) { TreeNode node = queue.poll(); + if (++i >= arr.length) break; if(arr[i] != null) { node.left = new TreeNode(arr[i]); queue.add(node.left); } - i++; + if (++i >= arr.length) break; if(arr[i] != null) { node.right = new TreeNode(arr[i]); queue.add(node.right); } - i++; } return root; } @@ -71,20 +71,4 @@ public class TreeNode { } return list; } - - /** - * Get a tree node with specific value in a binary tree - * @param root - * @param val - * @return - */ - public static TreeNode getTreeNode(TreeNode root, int val) { - if (root == null) - return null; - if (root.val == val) - return root; - TreeNode left = getTreeNode(root.left, val); - TreeNode right = getTreeNode(root.right, val); - return left != null ? left : right; - } } diff --git a/codes/javascript/chapter_tree/binary_tree_bfs.js b/codes/javascript/chapter_tree/binary_tree_bfs.js index b52f52820..21daaa8dc 100644 --- a/codes/javascript/chapter_tree/binary_tree_bfs.js +++ b/codes/javascript/chapter_tree/binary_tree_bfs.js @@ -28,10 +28,10 @@ function hierOrder(root) { /* Driver Code */ /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 -var root = arrToTree([1, 2, 3, 4, 5, 6, 7, null, null, null, null, null, null, null, null]); +var root = arrToTree([1, 2, 3, 4, 5, 6, 7]); console.log("\n初始化二叉树\n"); printTree(root); /* 层序遍历 */ let list = hierOrder(root); -console.log("\n层序遍历的结点打印序列 = " + list); \ No newline at end of file +console.log("\n层序遍历的结点打印序列 = " + list); diff --git a/codes/javascript/chapter_tree/binary_tree_dfs.js b/codes/javascript/chapter_tree/binary_tree_dfs.js index 51e557336..d23b69b6d 100644 --- a/codes/javascript/chapter_tree/binary_tree_dfs.js +++ b/codes/javascript/chapter_tree/binary_tree_dfs.js @@ -40,7 +40,7 @@ function postOrder(root) { /* Driver Code */ /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 -var root = arrToTree([1, 2, 3, 4, 5, 6, 7, null, null, null, null, null, null, null, null]); +var root = arrToTree([1, 2, 3, 4, 5, 6, 7]); console.log("\n初始化二叉树\n"); printTree(root); @@ -58,4 +58,3 @@ console.log("\n中序遍历的结点打印序列 = " + list); list.length = 0; postOrder(root); console.log("\n后序遍历的结点打印序列 = " + list); - diff --git a/codes/javascript/include/TreeNode.js b/codes/javascript/include/TreeNode.js index 6add12f0c..ce6d667bf 100644 --- a/codes/javascript/include/TreeNode.js +++ b/codes/javascript/include/TreeNode.js @@ -14,7 +14,7 @@ function TreeNode(val, left, right) { } /** -* Generate a binary tree with an array +* Generate a binary tree given an array * @param arr * @return */ @@ -24,20 +24,21 @@ function arrToTree(arr) { let root = new TreeNode(arr[0]); let queue = [root] - let i = 1; - while(queue.length) { + let i = 0; + while (queue.length) { let node = queue.shift(); - if(arr[i] !== null) { + if (++i >= arr.length) break; + if (arr[i] !== null) { node.left = new TreeNode(arr[i]); queue.push(node.left); } - i++; - if(arr[i] !== null) { + if (++i >= arr.length) break; + if (arr[i] !== null) { node.right = new TreeNode(arr[i]); queue.push(node.right); } - i++; } + return root; } diff --git a/codes/python/chapter_tree/binary_search_tree.py b/codes/python/chapter_tree/binary_search_tree.py index db52d57bd..7633b6288 100644 --- a/codes/python/chapter_tree/binary_search_tree.py +++ b/codes/python/chapter_tree/binary_search_tree.py @@ -138,7 +138,7 @@ class BinarySearchTree: """ Driver Code """ if __name__ == "__main__": # 初始化二叉搜索树 - nums = list(range(1, 16)) + nums = list(range(1, 16)) # [1, 2, ..., 15] bst = BinarySearchTree(nums=nums) print("\n初始化的二叉树为\n") print_tree(bst.root) diff --git a/codes/python/chapter_tree/binary_tree.py b/codes/python/chapter_tree/binary_tree.py index d99026352..e00eeb4e5 100644 --- a/codes/python/chapter_tree/binary_tree.py +++ b/codes/python/chapter_tree/binary_tree.py @@ -36,5 +36,5 @@ if __name__ == "__main__": print_tree(n1) # 删除结点 n1.left = n2 - print("\n删除结点 P 后\n"); + print("\n删除结点 P 后\n") print_tree(n1) diff --git a/codes/python/chapter_tree/binary_tree_bfs.py b/codes/python/chapter_tree/binary_tree_bfs.py index 0320a08dd..225a8bd6e 100644 --- a/codes/python/chapter_tree/binary_tree_bfs.py +++ b/codes/python/chapter_tree/binary_tree_bfs.py @@ -32,7 +32,7 @@ def hier_order(root: TreeNode): if __name__ == "__main__": # 初始化二叉树 # 这里借助了一个从数组直接生成二叉树的函数 - root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7, None, None, None, None, None, None, None, None]) + root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7]) print("\n初始化二叉树\n") print_tree(root) diff --git a/codes/python/chapter_tree/binary_tree_dfs.py b/codes/python/chapter_tree/binary_tree_dfs.py index 11ee8339c..a1d46e697 100644 --- a/codes/python/chapter_tree/binary_tree_dfs.py +++ b/codes/python/chapter_tree/binary_tree_dfs.py @@ -45,7 +45,7 @@ def post_order(root: typing.Optional[TreeNode]): if __name__ == "__main__": # 初始化二叉树 # 这里借助了一个从数组直接生成二叉树的函数 - root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7, None, None, None, None, None, None, None, None]) + root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7]) print("\n初始化二叉树\n") print_tree(root) diff --git a/codes/python/include/binary_tree.py b/codes/python/include/binary_tree.py index 670fd18ce..9d612a7e2 100644 --- a/codes/python/include/binary_tree.py +++ b/codes/python/include/binary_tree.py @@ -26,39 +26,30 @@ class TreeNode: def list_to_tree(arr): """Generate a binary tree with a list - - Args: - arr ([type]): [description] - - Returns: - [type]: [description] """ if not arr: return None - i = 1 - root = TreeNode(int(arr[0])) - queue = collections.deque() - queue.append(root) + + i = 0 + root = TreeNode(arr[0]) + queue = collections.deque([root]) while queue: node = queue.popleft() + i += 1 + if i >= len(arr): break if arr[i] != None: - node.left = TreeNode(int(arr[i])) + node.left = TreeNode(arr[i]) queue.append(node.left) i += 1 + if i >= len(arr): break if arr[i] != None: - node.right = TreeNode(int(arr[i])) + node.right = TreeNode(arr[i]) queue.append(node.right) - i += 1 + return root def tree_to_list(root): """Serialize a tree into an array - - Args: - root ([type]): [description] - - Returns: - [type]: [description] """ if not root: return [] queue = collections.deque() @@ -75,13 +66,6 @@ def tree_to_list(root): def get_tree_node(root, val): """Get a tree node with specific value in a binary tree - - Args: - root ([type]): [description] - val ([type]): [description] - - Returns: - [type]: [description] """ if not root: return diff --git a/codes/typescript/chapter_tree/binary_tree_bfs.ts b/codes/typescript/chapter_tree/binary_tree_bfs.ts index 1e57ef62d..7ec4a2a5c 100644 --- a/codes/typescript/chapter_tree/binary_tree_bfs.ts +++ b/codes/typescript/chapter_tree/binary_tree_bfs.ts @@ -30,7 +30,7 @@ function hierOrder(root: TreeNode | null): number[] { /* Driver Code */ /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 -var root = arrToTree([1, 2, 3, 4, 5, 6, 7, null, null, null, null, null, null, null, null]); +var root = arrToTree([1, 2, 3, 4, 5, 6, 7]); console.log('\n初始化二叉树\n'); printTree(root); diff --git a/codes/typescript/chapter_tree/binary_tree_dfs.ts b/codes/typescript/chapter_tree/binary_tree_dfs.ts index d9e982df1..a61855af8 100644 --- a/codes/typescript/chapter_tree/binary_tree_dfs.ts +++ b/codes/typescript/chapter_tree/binary_tree_dfs.ts @@ -47,7 +47,7 @@ function postOrder(root: TreeNode | null): void { /* Driver Code */ /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 -const root = arrToTree([1, 2, 3, 4, 5, 6, 7, null, null, null, null, null, null, null, null]); +const root = arrToTree([1, 2, 3, 4, 5, 6, 7]); console.log('\n初始化二叉树\n'); printTree(root); diff --git a/codes/typescript/module/TreeNode.ts b/codes/typescript/module/TreeNode.ts index 5a886763e..0e3425006 100644 --- a/codes/typescript/module/TreeNode.ts +++ b/codes/typescript/module/TreeNode.ts @@ -20,7 +20,7 @@ class TreeNode { } /** - * Generate a binary tree with an array + * Generate a binary tree given an array * @param arr * @return */ @@ -31,19 +31,19 @@ function arrToTree(arr: (number | null)[]): TreeNode | null { const root = new TreeNode(arr[0] as number); const queue = [root]; - let i = 1; + let i = 0; while (queue.length) { let node = queue.shift() as TreeNode; + if (++i >= arr.length) break; if (arr[i] !== null) { node.left = new TreeNode(arr[i] as number); queue.push(node.left); } - i++; + if (++i >= arr.length) break; if (arr[i] !== null) { node.right = new TreeNode(arr[i] as number); queue.push(node.right); } - i++; } return root; } diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md index def4cde5b..b0151622f 100644 --- a/docs/chapter_heap/heap.md +++ b/docs/chapter_heap/heap.md @@ -2,26 +2,48 @@ 「堆 Heap」是一种特殊的树状数据结构,并且是一颗「完全二叉树」。堆主要分为两种: -- 「大顶堆 Max Heap」,任意父结点的值 > 其子结点的值,因此根结点的值最大; -- 「小顶堆 Min Heap」,任意父结点的值 < 其子结点的值,因此根结点的值最小; +- 「大顶堆 Max Heap」,任意结点的值 $\geq$ 其子结点的值,因此根结点的值最大; +- 「小顶堆 Min Heap」,任意结点的值 $\leq$ 其子结点的值,因此根结点的值最小; (图) !!! tip "" - 大顶堆和小顶堆的定义、性质、操作本质上是一样的。区别只是大顶堆在求最大值,小顶堆在求最小值。在下文中,我们将统一用「大顶堆」来举例,「小顶堆」的用法与实现可以简单地将所有 $>$ ($<$) 替换为 $<$ ($>$) 即可。 + 大顶堆和小顶堆的定义、性质、操作本质上是相同的,区别只是大顶堆在求最大值,小顶堆在求最小值。 ## 堆常用操作 -堆的初始化。 +值得说明的是,多数编程语言提供的是「优先队列 Priority Queue」,其是一种抽象数据结构,**定义为具有出队优先级的队列**。 -获取堆顶元素。 +而恰好,堆的定义与优先队列的操作逻辑完全吻合,大顶堆就是一个元素从大到小出队的优先队列。从使用角度看,我们可以将「优先队列」和「堆」理解为等价的数据结构,下文将统一使用 “堆” 这个名称。 -添加与删除元素。 +堆的常用操作见下表(方法命名以 Java 为例)。 + +

Table. 堆的常用操作

+ +
+ +| 方法 | 描述 | +| --------- | -------------------------------------------- | +| add() | 元素入堆 | +| poll() | 堆顶元素出堆 | +| peek() | 访问堆顶元素(大 / 小顶堆分别为最大 / 小值) | +| size() | 获取堆的元素数量 | +| isEmpty() | 判断堆是否为空 | + +
+ +```java + +``` ## 堆的实现 -在二叉树章节中,我们讲过二叉树的数组表示方法,并且提到完全二叉树非常适合用数组来表示,因此我们一般使用「数组」来存储「堆」。 +!!! tip + + 下文使用「大顶堆」来举例,「小顶堆」的用法与实现可以简单地将所有 $>$ ($<$) 替换为 $<$ ($>$) 即可。 + +我们一般使用「数组」来存储「堆」,这是因为完全二叉树非常适合用数组来表示(在二叉树章节有详细解释)。 diff --git a/docs/chapter_tree/binary_tree.assets/binary_tree_add_remove.png b/docs/chapter_tree/binary_tree.assets/binary_tree_add_remove.png index b7922fcb3e7a364f2ef795cdcc8108852c2abdce..d95c48f078fb4379531212f15945476bdd1aafd9 100644 GIT binary patch literal 79400 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 E1C(Ef#sB~S literal 77839 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 O_L$~y1wIpv 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%0Du>L0dEV7pFYgfQpxr;h<=Oz~lZJVKz8HnpS4bcm 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�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 bvv~h|)ZV2eITgJM1}-m;L;E;;LbLw|6_qN3 From 4839c234329c9365bd73668cb17b198e6345498d Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Sun, 8 Jan 2023 19:04:24 +0800 Subject: [PATCH 121/158] Remove heap.md for temporary. --- docs/chapter_heap/heap.md | 54 --------------------------------------- 1 file changed, 54 deletions(-) delete mode 100644 docs/chapter_heap/heap.md diff --git a/docs/chapter_heap/heap.md b/docs/chapter_heap/heap.md deleted file mode 100644 index b0151622f..000000000 --- a/docs/chapter_heap/heap.md +++ /dev/null @@ -1,54 +0,0 @@ -# 堆 - -「堆 Heap」是一种特殊的树状数据结构,并且是一颗「完全二叉树」。堆主要分为两种: - -- 「大顶堆 Max Heap」,任意结点的值 $\geq$ 其子结点的值,因此根结点的值最大; -- 「小顶堆 Min Heap」,任意结点的值 $\leq$ 其子结点的值,因此根结点的值最小; - -(图) - -!!! tip "" - - 大顶堆和小顶堆的定义、性质、操作本质上是相同的,区别只是大顶堆在求最大值,小顶堆在求最小值。 - -## 堆常用操作 - -值得说明的是,多数编程语言提供的是「优先队列 Priority Queue」,其是一种抽象数据结构,**定义为具有出队优先级的队列**。 - -而恰好,堆的定义与优先队列的操作逻辑完全吻合,大顶堆就是一个元素从大到小出队的优先队列。从使用角度看,我们可以将「优先队列」和「堆」理解为等价的数据结构,下文将统一使用 “堆” 这个名称。 - -堆的常用操作见下表(方法命名以 Java 为例)。 - -

Table. 堆的常用操作

- -
- -| 方法 | 描述 | -| --------- | -------------------------------------------- | -| add() | 元素入堆 | -| poll() | 堆顶元素出堆 | -| peek() | 访问堆顶元素(大 / 小顶堆分别为最大 / 小值) | -| size() | 获取堆的元素数量 | -| isEmpty() | 判断堆是否为空 | - -
- -```java - -``` - -## 堆的实现 - -!!! tip - - 下文使用「大顶堆」来举例,「小顶堆」的用法与实现可以简单地将所有 $>$ ($<$) 替换为 $<$ ($>$) 即可。 - -我们一般使用「数组」来存储「堆」,这是因为完全二叉树非常适合用数组来表示(在二叉树章节有详细解释)。 - - - -## 堆常见应用 - -- 优先队列。 -- 堆排序。 -- 获取数据 Top K 大(小)元素。 From 3ba37dba3a55f02b46484a41fcfeacc2881c6a47 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Sun, 8 Jan 2023 19:08:07 +0800 Subject: [PATCH 122/158] Fix the test case of the binary tree dfs in Java. --- codes/java/chapter_tree/binary_tree_dfs.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codes/java/chapter_tree/binary_tree_dfs.java b/codes/java/chapter_tree/binary_tree_dfs.java index d1ef063b0..ad5337f22 100644 --- a/codes/java/chapter_tree/binary_tree_dfs.java +++ b/codes/java/chapter_tree/binary_tree_dfs.java @@ -43,7 +43,7 @@ public class binary_tree_dfs { public static void main(String[] args) { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - TreeNode root = TreeNode.arrToTree(new Integer[] { 1, null, 3, 4, 5 }); + TreeNode root = TreeNode.arrToTree(new Integer[] { 1, 2, 3, 4, 5, 6, 7 }); System.out.println("\n初始化二叉树\n"); PrintUtil.printTree(root); From 73e345283841496b6ed37df8cf838434c9530ee8 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Sun, 8 Jan 2023 19:41:05 +0800 Subject: [PATCH 123/158] Add Swift language blocks to the docs. --- .../worst_best_time_complexity.c | 12 +-- codes/csharp/hello-algo.csproj | 4 +- .../chapter_array_and_linkedlist/array.swift | 12 +-- .../linked_list.md | 100 ++++++++++++------ docs/chapter_array_and_linkedlist/list.md | 43 ++++++++ .../time_complexity.md | 12 +-- docs/chapter_hashing/hash_map.md | 50 ++++++--- docs/chapter_preface/about_the_book.md | 13 +++ docs/chapter_searching/binary_search.md | 18 ++++ docs/chapter_searching/hashing_search.md | 12 +++ docs/chapter_searching/linear_search.md | 12 +++ docs/chapter_sorting/bubble_sort.md | 12 +++ docs/chapter_sorting/insertion_sort.md | 6 ++ docs/chapter_sorting/merge_sort.md | 6 ++ docs/chapter_sorting/quick_sort.md | 28 ++++- docs/chapter_stack_and_queue/deque.md | 6 ++ docs/chapter_stack_and_queue/queue.md | 18 ++++ docs/chapter_stack_and_queue/stack.md | 18 ++++ docs/chapter_tree/avl_tree.md | 48 +++++++++ docs/chapter_tree/binary_search_tree.md | 18 ++++ docs/chapter_tree/binary_tree.md | 24 +++++ docs/chapter_tree/binary_tree_traversal.md | 12 +++ 22 files changed, 414 insertions(+), 70 deletions(-) diff --git a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c index 9173f32d4..2570ff3c3 100644 --- a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c +++ b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c @@ -15,12 +15,12 @@ int *randomNumbers(int n) { nums[i] = i + 1; } // 随机打乱数组元素 - for (int i = n - 1; i > 0; i--) { - int j = rand() % (i + 1); - int temp = nums[i]; - nums[i] = nums[j]; - nums[j] = temp; - } + for (int i = n - 1; i > 0; i--) { + int j = rand() % (i + 1); + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } return nums; } diff --git a/codes/csharp/hello-algo.csproj b/codes/csharp/hello-algo.csproj index 2ef4da851..d96d728b1 100644 --- a/codes/csharp/hello-algo.csproj +++ b/codes/csharp/hello-algo.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/codes/swift/chapter_array_and_linkedlist/array.swift b/codes/swift/chapter_array_and_linkedlist/array.swift index c0c7ab9b9..08cd0707f 100644 --- a/codes/swift/chapter_array_and_linkedlist/array.swift +++ b/codes/swift/chapter_array_and_linkedlist/array.swift @@ -4,7 +4,7 @@ * Author: nuomi1 (nuomi1@qq.com) */ -// 随机返回一个数组元素 +/* 随机返回一个数组元素 */ func randomAccess(nums: [Int]) -> Int { // 在区间 [0, nums.count) 中随机抽取一个数字 let randomIndex = nums.indices.randomElement()! @@ -13,7 +13,7 @@ func randomAccess(nums: [Int]) -> Int { return randomNum } -// 扩展数组长度 +/* 扩展数组长度 */ func extend(nums: [Int], enlarge: Int) -> [Int] { // 初始化一个扩展长度后的数组 var res = Array(repeating: 0, count: nums.count + enlarge) @@ -25,7 +25,7 @@ func extend(nums: [Int], enlarge: Int) -> [Int] { return res } -// 在数组的索引 index 处插入元素 num +/* 在数组的索引 index 处插入元素 num */ func insert(nums: inout [Int], num: Int, index: Int) { // 把索引 index 以及之后的所有元素向后移动一位 for i in sequence(first: nums.count - 1, next: { $0 > index + 1 ? $0 - 1 : nil }) { @@ -35,7 +35,7 @@ func insert(nums: inout [Int], num: Int, index: Int) { nums[index] = num } -// 删除索引 index 处元素 +/* 删除索引 index 处元素 */ func remove(nums: inout [Int], index: Int) { let count = nums.count // 把索引 index 之后的所有元素向前移动一位 @@ -44,7 +44,7 @@ func remove(nums: inout [Int], index: Int) { } } -// 遍历数组 +/* 遍历数组 */ func traverse(nums: [Int]) { var count = 0 // 通过索引遍历数组 @@ -57,7 +57,7 @@ func traverse(nums: [Int]) { } } -// 在数组中查找指定元素 +/* 在数组中查找指定元素 */ func find(nums: [Int], target: Int) -> Int { for i in nums.indices { if nums[i] == target { diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index f054802d4..aa9c93ec7 100644 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -112,6 +112,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="" + + ``` + **尾结点指向什么?** 我们一般将链表的最后一个结点称为「尾结点」,其指向的是「空」,在 Java / C++ / Python 中分别记为 `null` / `nullptr` / `None` 。在不引起歧义下,本书都使用 `null` 来表示空。 **链表初始化方法**。建立链表分为两步,第一步是初始化各个结点对象,第二步是构建引用指向关系。完成后,即可以从链表的首个结点(即头结点)出发,访问其余所有的结点。 @@ -122,7 +128,7 @@ comments: true === "Java" - ```java title="" + ```java title="linked_list.java" /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ // 初始化各个结点 ListNode n0 = new ListNode(1); @@ -139,7 +145,7 @@ comments: true === "C++" - ```cpp title="" + ```cpp title="linked_list.cpp" /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ // 初始化各个结点 ListNode* n0 = new ListNode(1); @@ -156,7 +162,7 @@ comments: true === "Python" - ```python title="" + ```python title="linked_list.py" """ 初始化链表 1 -> 3 -> 2 -> 5 -> 4 """ # 初始化各个结点 n0 = ListNode(1) @@ -173,7 +179,7 @@ comments: true === "Go" - ```go title="" + ```go title="linked_list.go" /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ // 初始化各个结点 n0 := NewListNode(1) @@ -191,7 +197,7 @@ comments: true === "JavaScript" - ```js title="" + ```js title="linked_list.js" /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ // 初始化各个结点 const n0 = new ListNode(1); @@ -208,7 +214,7 @@ comments: true === "TypeScript" - ```typescript title="" + ```typescript title="linked_list.ts" /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ // 初始化各个结点 const n0 = new ListNode(1); @@ -225,13 +231,13 @@ comments: true === "C" - ```c title="" + ```c title="linked_list.c" ``` === "C#" - ```csharp title="" + ```csharp title="linked_list.cs" /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ // 初始化各个结点 ListNode n0 = new ListNode(1); @@ -246,6 +252,12 @@ comments: true n3.next = n4; ``` +=== "Swift" + + ```swift title="linked_list.swift" + + ``` + ## 链表优点 **在链表中,插入与删除结点的操作效率高**。例如,如果想在链表中间的两个结点 `A` , `B` 之间插入一个新结点 `P` ,我们只需要改变两个结点指针即可,时间复杂度为 $O(1)$ ,相比数组的插入操作高效很多。在链表中删除某个结点也很方便,只需要改变一个结点指针即可。 @@ -256,7 +268,7 @@ comments: true === "Java" - ```java title="" + ```java title="linked_list.java" /* 在链表的结点 n0 之后插入结点 P */ void insert(ListNode n0, ListNode P) { ListNode n1 = n0.next; @@ -277,7 +289,7 @@ comments: true === "C++" - ```cpp title="" + ```cpp title="linked_list.cpp" /* 在链表的结点 n0 之后插入结点 P */ void insert(ListNode* n0, ListNode* P) { ListNode* n1 = n0->next; @@ -300,7 +312,7 @@ comments: true === "Python" - ```python title="" + ```python title="linked_list.py" """ 在链表的结点 n0 之后插入结点 P """ def insert(n0, P): n1 = n0.next @@ -319,7 +331,7 @@ comments: true === "Go" - ```go title="" + ```go title="linked_list.go" /* 在链表的结点 n0 之后插入结点 P */ func insert(n0 *ListNode, P *ListNode) { n1 := n0.Next @@ -341,7 +353,7 @@ comments: true === "JavaScript" - ```js title="" + ```js title="linked_list.js" /* 在链表的结点 n0 之后插入结点 P */ function insert(n0, P) { let n1 = n0.next; @@ -362,7 +374,7 @@ comments: true === "TypeScript" - ```typescript title="" + ```typescript title="linked_list.ts" /* 在链表的结点 n0 之后插入结点 P */ function insert(n0: ListNode, P: ListNode): void { const n1 = n0.next; @@ -383,13 +395,13 @@ comments: true === "C" - ```c title="" + ```c title="linked_list.c" ``` === "C#" - ```csharp title="" + ```csharp title="linked_list.cs" // 在链表的结点 n0 之后插入结点 P void Insert(ListNode n0, ListNode P) { @@ -410,13 +422,19 @@ comments: true } ``` +=== "Swift" + + ```swift title="linked_list.swift" + + ``` + ## 链表缺点 **链表访问结点效率低**。上节提到,数组可以在 $O(1)$ 时间下访问任意元素,但链表无法直接访问任意结点。这是因为计算机需要从头结点出发,一个一个地向后遍历到目标结点。例如,倘若想要访问链表索引为 `index` (即第 `index + 1` 个)的结点,那么需要 `index` 次访问操作。 === "Java" - ```java title="" + ```java title="linked_list.java" /* 访问链表中索引为 index 的结点 */ ListNode access(ListNode head, int index) { for (int i = 0; i < index; i++) { @@ -430,7 +448,7 @@ comments: true === "C++" - ```cpp title="" + ```cpp title="linked_list.cpp" /* 访问链表中索引为 index 的结点 */ ListNode* access(ListNode* head, int index) { for (int i = 0; i < index; i++) { @@ -444,7 +462,7 @@ comments: true === "Python" - ```python title="" + ```python title="linked_list.py" """ 访问链表中索引为 index 的结点 """ def access(head, index): for _ in range(index): @@ -456,7 +474,7 @@ comments: true === "Go" - ```go title="" + ```go title="linked_list.go" /* 访问链表中索引为 index 的结点 */ func access(head *ListNode, index int) *ListNode { for i := 0; i < index; i++ { @@ -471,7 +489,7 @@ comments: true === "JavaScript" - ```js title="" + ```js title="linked_list.js" /* 访问链表中索引为 index 的结点 */ function access(head, index) { for (let i = 0; i < index; i++) { @@ -485,7 +503,7 @@ comments: true === "TypeScript" - ```typescript title="" + ```typescript title="linked_list.ts" /* 访问链表中索引为 index 的结点 */ function access(head: ListNode | null, index: number): ListNode | null { for (let i = 0; i < index; i++) { @@ -500,13 +518,13 @@ comments: true === "C" - ```c title="" + ```c title="linked_list.c" ``` === "C#" - ```csharp title="" + ```csharp title="linked_list.cs" // 访问链表中索引为 index 的结点 ListNode Access(ListNode head, int index) { @@ -520,6 +538,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="linked_list.swift" + + ``` + **链表的内存占用多**。链表以结点为单位,每个结点除了保存值外,还需额外保存指针(引用)。这意味着同样数据量下,链表比数组需要占用更多内存空间。 ## 链表常用操作 @@ -528,7 +552,7 @@ comments: true === "Java" - ```java title="" + ```java title="linked_list.java" /* 在链表中查找值为 target 的首个结点 */ int find(ListNode head, int target) { int index = 0; @@ -544,7 +568,7 @@ comments: true === "C++" - ```cpp title="" + ```cpp title="linked_list.cpp" /* 在链表中查找值为 target 的首个结点 */ int find(ListNode* head, int target) { int index = 0; @@ -560,7 +584,7 @@ comments: true === "Python" - ```python title="" + ```python title="linked_list.py" """ 在链表中查找值为 target 的首个结点 """ def find(head, target): index = 0 @@ -574,7 +598,7 @@ comments: true === "Go" - ```go title="" + ```go title="linked_list.go" /* 在链表中查找值为 target 的首个结点 */ func find(head *ListNode, target int) int { index := 0 @@ -591,7 +615,7 @@ comments: true === "JavaScript" - ```js title="" + ```js title="linked_list.js" /* 在链表中查找值为 target 的首个结点 */ function find(head, target) { let index = 0; @@ -608,7 +632,7 @@ comments: true === "TypeScript" - ```typescript title="" + ```typescript title="linked_list.ts" /* 在链表中查找值为 target 的首个结点 */ function find(head: ListNode | null, target: number): number { let index = 0; @@ -625,13 +649,13 @@ comments: true === "C" - ```c title="" + ```c title="linked_list.c" ``` === "C#" - ```csharp title="" + ```csharp title="linked_list.cs" // 在链表中查找值为 target 的首个结点 int Find(ListNode head, int target) { @@ -647,6 +671,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="linked_list.swift" + + ``` + ## 常见链表类型 **单向链表**。即上述介绍的普通链表。单向链表的结点有「值」和指向下一结点的「指针(引用)」两项数据。我们将首个结点称为头结点,尾结点指向 `null` 。 @@ -760,6 +790,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="" + + ``` + ![linkedlist_common_types](linked_list.assets/linkedlist_common_types.png)

Fig. 常见链表类型

diff --git a/docs/chapter_array_and_linkedlist/list.md b/docs/chapter_array_and_linkedlist/list.md index 19de6d778..d1109d65b 100644 --- a/docs/chapter_array_and_linkedlist/list.md +++ b/docs/chapter_array_and_linkedlist/list.md @@ -91,6 +91,12 @@ comments: true List list = numbers.ToList(); ``` +=== "Swift" + + ```swift title="list.swift" + + ``` + **访问与更新元素**。列表的底层数据结构是数组,因此可以在 $O(1)$ 时间内访问与更新元素,效率很高。 === "Java" @@ -169,6 +175,12 @@ comments: true list[1] = 0; // 将索引 1 处的元素更新为 0 ``` +=== "Swift" + + ```swift title="list.swift" + + ``` + **在列表中添加、插入、删除元素**。相对于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但是插入与删除元素的效率仍与数组一样低,时间复杂度为 $O(N)$ 。 === "Java" @@ -317,6 +329,12 @@ comments: true list.RemoveAt(3); ``` +=== "Swift" + + ```swift title="list.swift" + + ``` + **遍历列表**。与数组一样,列表可以使用索引遍历,也可以使用 `for-each` 直接遍历。 === "Java" @@ -437,6 +455,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="list.swift" + + ``` + **拼接两个列表**。再创建一个新列表 `list1` ,我们可以将其中一个列表拼接到另一个的尾部。 === "Java" @@ -502,6 +526,12 @@ comments: true list.AddRange(list1); // 将列表 list1 拼接到 list 之后 ``` +=== "Swift" + + ```swift title="list.swift" + + ``` + **排序列表**。排序也是常用的方法之一,完成列表排序后,我们就可以使用在数组类算法题中经常考察的「二分查找」和「双指针」算法了。 === "Java" @@ -559,6 +589,12 @@ comments: true list.Sort(); // 排序后,列表元素从小到大排列 ``` +=== "Swift" + + ```swift title="list.swift" + + ``` + ## 列表简易实现 * 为了帮助加深对列表的理解,我们在此提供一个列表的简易版本的实现。需要关注三个核心点: @@ -1220,3 +1256,10 @@ comments: true } } ``` + +=== "Swift" + + ```swift title="my_list.swift" + + ``` + diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 217981ae8..7b0e66fbd 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -421,12 +421,12 @@ $$ ```go title="" func algorithm(n int) { - a := 1 // +1 - a = a + 1 // +1 - a = a * 2 // +1 + a := 1 // +1 + a = a + 1 // +1 + a = a * 2 // +1 // 循环 n 次 - for i := 0; i < n; i++ { // +1 - fmt.Println(a) // +1 + for i := 0; i < n; i++ { // +1 + fmt.Println(a) // +1 } } ``` @@ -2657,7 +2657,7 @@ $$ === "Swift" - ```swift title="" + ```swift title="worst_best_time_complexity.swift" // 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 func randomNumbers(n: Int) -> [Int] { // 生成数组 nums = { 1, 2, 3, ..., n } diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index d8244d80a..3a69d88fc 100644 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -108,25 +108,25 @@ comments: true === "Go" - ```go title="hash_map_test.go" - /* 初始化哈希表 */ - mapp := make(map[int]string) + ```go title="hash_map.go" + /* 初始化哈希表 */ + mapp := make(map[int]string) - /* 添加操作 */ - // 在哈希表中添加键值对 (key, value) - mapp[12836] = "小哈" - mapp[15937] = "小啰" - mapp[16750] = "小算" - mapp[13276] = "小法" - mapp[10583] = "小鸭" + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + mapp[12836] = "小哈" + mapp[15937] = "小啰" + mapp[16750] = "小算" + mapp[13276] = "小法" + mapp[10583] = "小鸭" - /* 查询操作 */ - // 向哈希表输入键 key ,得到值 value - name := mapp[15937] + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + name := mapp[15937] - /* 删除操作 */ - // 在哈希表中删除键值对 (key, value) - delete(mapp, 10583) + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + delete(mapp, 10583) ``` === "JavaScript" @@ -207,6 +207,12 @@ comments: true map.Remove(10583); ``` +=== "Swift" + + ```swift title="hash_map.swift" + + ``` + 遍历哈希表有三种方式,即 **遍历键值对、遍历键、遍历值**。 === "Java" @@ -339,6 +345,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="hash_map.swift" + + ``` + ## 哈希函数 哈希表中存储元素的数据结构被称为「桶 Bucket」,底层实现可能是数组、链表、二叉树(红黑树),或是它们的组合。 @@ -756,6 +768,12 @@ $$ } ``` +=== "Swift" + + ```swift title="array_hash_map.swift" + + ``` + ## 哈希冲突 细心的同学可能会发现,**哈希函数 $f(x) = x \% 100$ 会在某些情况下失效**。具体地,当输入的 key 后两位相同时,哈希函数的计算结果也相同,指向同一个 value 。例如,分别查询两个学号 12836 和 20336 ,则有 diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index cb926b941..2fba23502 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -192,6 +192,19 @@ comments: true */ ``` +=== "Swift" + + ```swift title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + """ 在 Java, C, C++, C#, Go, JS, TS 的代码注释中,`/* ... */` 用于注释函数、类、测试样例等标题, `// ...` 用于解释代码内容;类似地,在 Python 中,`""" ... """` 用于注释标题, `# ...` 用于解释代码。 diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index 8827588ef..466e76658 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -210,6 +210,12 @@ $$ } ``` +=== "Swift" + + ```swift title="binary_search.swift" + + ``` + ### “左闭右开”实现 当然,我们也可以使用“左闭右开”的表示方法,写出相同功能的二分查找代码。 @@ -374,6 +380,12 @@ $$ } ``` +=== "Swift" + + ```swift title="binary_search.swift" + + ``` + ### 两种表示对比 对比下来,两种表示的代码写法有以下不同点: @@ -460,6 +472,12 @@ $$ int m = i + (j - i) / 2; ``` +=== "Swift" + + ```swift title="" + + ``` + ## 复杂度分析 **时间复杂度 $O(\log n)$ :** 其中 $n$ 为数组或链表长度;每轮排除一半的区间,因此循环轮数为 $\log_2 n$ ,使用 $O(\log n)$ 时间。 diff --git a/docs/chapter_searching/hashing_search.md b/docs/chapter_searching/hashing_search.md index a70a55483..12746d2ef 100644 --- a/docs/chapter_searching/hashing_search.md +++ b/docs/chapter_searching/hashing_search.md @@ -95,6 +95,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="hashing_search.swift" + + ``` + 再比如,如果我们想要给定一个目标结点值 `target` ,获取对应的链表结点对象,那么也可以使用哈希查找实现。 ![hash_search_listnode](hashing_search.assets/hash_search_listnode.png) @@ -179,6 +185,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="hashing_search.swift" + + ``` + ## 复杂度分析 **时间复杂度:** $O(1)$ ,哈希表的查找操作使用 $O(1)$ 时间。 diff --git a/docs/chapter_searching/linear_search.md b/docs/chapter_searching/linear_search.md index 1c07027e6..be98c013d 100644 --- a/docs/chapter_searching/linear_search.md +++ b/docs/chapter_searching/linear_search.md @@ -122,6 +122,12 @@ comments: true ``` +=== "Swift" + + ```swift title="linear_search.swift" + + ``` + 再比如,我们想要在给定一个目标结点值 `target` ,返回此结点对象,也可以在链表中进行线性查找。 === "Java" @@ -238,6 +244,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="linear_search.swift" + + ``` + ## 复杂度分析 **时间复杂度 $O(n)$ :** 其中 $n$ 为数组或链表长度。 diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index bb35fabe4..1dbc56475 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -212,6 +212,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="bubble_sort.swift" + + ``` + ## 算法特性 **时间复杂度 $O(n^2)$ :** 各轮「冒泡」遍历的数组长度为 $n - 1$ , $n - 2$ , $\cdots$ , $2$ , $1$ 次,求和为 $\frac{(n - 1) n}{2}$ ,因此使用 $O(n^2)$ 时间。 @@ -414,3 +420,9 @@ comments: true } } ``` + +=== "Swift" + + ```swift title="bubble_sort.swift" + + ``` diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index 85016f718..679182f42 100644 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -175,6 +175,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="insertion_sort.swift" + + ``` + ## 算法特性 **时间复杂度 $O(n^2)$ :** 最差情况下,各轮插入操作循环 $n - 1$ , $n-2$ , $\cdots$ , $2$ , $1$ 次,求和为 $\frac{(n - 1) n}{2}$ ,使用 $O(n^2)$ 时间。 diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index 48a5046c7..396baa089 100644 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -388,6 +388,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="merge_sort.swift" + + ``` + 下面重点解释一下合并方法 `merge()` 的流程: 1. 初始化一个辅助数组 `tmp` 暂存待合并区间 `[left, right]` 内的元素,后续通过覆盖原数组 `nums` 的元素来实现合并; diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index ebc161caa..f8dcc3238 100644 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -223,6 +223,12 @@ comments: true ``` +=== "Swift" + + ```swift title="quick_sort.swift" + + ``` + !!! note "快速排序的分治思想" 哨兵划分的实质是将 **一个长数组的排序问题** 简化为 **两个短数组的排序问题**。 @@ -359,6 +365,12 @@ comments: true ``` +=== "Swift" + + ```swift title="quick_sort.swift" + + ``` + ## 算法特性 **平均时间复杂度 $O(n \log n)$ :** 平均情况下,哨兵划分的递归层数为 $\log n$ ,每层中的总循环数为 $n$ ,总体使用 $O(n \log n)$ 时间。 @@ -574,6 +586,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="quick_sort.swift" + + ``` + ## 尾递归优化 **普通快速排序在某些输入下的空间效率变差**。仍然以完全倒序的输入数组为例,由于每轮哨兵划分后右子数组长度为 0 ,那么将形成一个高度为 $n - 1$ 的递归树,此时使用的栈帧空间大小劣化至 $O(n)$ 。 @@ -652,10 +670,10 @@ comments: true // 对两个子数组中较短的那个执行快排 if pivot-left < right-pivot { quickSort(nums, left, pivot-1) // 递归排序左子数组 - left = pivot + 1 // 剩余待排序区间为 [pivot + 1, right] + left = pivot + 1 // 剩余待排序区间为 [pivot + 1, right] } else { quickSort(nums, pivot+1, right) // 递归排序右子数组 - right = pivot - 1 // 剩余待排序区间为 [left, pivot - 1] + right = pivot - 1 // 剩余待排序区间为 [left, pivot - 1] } } } @@ -734,3 +752,9 @@ comments: true } } ``` + +=== "Swift" + + ```swift title="quick_sort.swift" + + ``` diff --git a/docs/chapter_stack_and_queue/deque.md b/docs/chapter_stack_and_queue/deque.md index c178c0e0a..52b0540d5 100644 --- a/docs/chapter_stack_and_queue/deque.md +++ b/docs/chapter_stack_and_queue/deque.md @@ -192,3 +192,9 @@ comments: true /* 判断双向队列是否为空 */ bool isEmpty = deque.Count == 0; ``` + +=== "Swift" + + ```swift title="deque.swift" + + ``` diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index 8a964d3b7..ce47edb38 100644 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -228,6 +228,12 @@ comments: true bool isEmpty = queue.Count() == 0; ``` +=== "Swift" + + ```swift title="queue.swift" + + ``` + ## 队列实现 队列需要一种可以在一端添加,并在另一端删除的数据结构,也可以使用链表或数组来实现。 @@ -612,6 +618,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="linkedlist_queue.swift" + + ``` + ### 基于数组的实现 数组的删除首元素的时间复杂度为 $O(n)$ ,因此不适合直接用来实现队列。然而,我们可以借助两个指针 `front` , `rear` 来分别记录队首和队尾的索引位置,在入队 / 出队时分别将 `front` / `rear` 向后移动一位即可,这样每次仅需操作一个元素,时间复杂度降至 $O(1)$ 。 @@ -1015,6 +1027,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="array_queue.swift" + + ``` + ## 队列典型应用 - **淘宝订单**。购物者下单后,订单就被加入到队列之中,随后系统再根据顺序依次处理队列中的订单。在双十一时,在短时间内会产生海量的订单,如何处理「高并发」则是工程师们需要重点思考的问题。 diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index 985a4cf66..374de7424 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -228,6 +228,12 @@ comments: true bool isEmpty = stack.Count()==0; ``` +=== "Swift" + + ```swift title="stack.swift" + + ``` + ## 栈的实现 为了更加清晰地了解栈的运行机制,接下来我们来自己动手实现一个栈类。 @@ -591,6 +597,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="linkedlist_stack.swift" + + ``` + ### 基于数组的实现 使用「数组」实现栈时,将数组的尾部当作栈顶,这样可以保证入栈与出栈操作的时间复杂度都为 $O(1)$ 。准确地说,由于入栈的元素可能是源源不断的,我们需要使用可以动态扩容的「列表」。 @@ -870,6 +882,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="array_stack.swift" + + ``` + !!! tip 某些语言并未专门提供栈类,但我们可以直接把该语言的「数组」或「链表」看作栈来使用,并通过“脑补”来屏蔽无关操作,而无需像上述代码去特意包装一层。 diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index fe08828c1..43e0d9f45 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -94,6 +94,12 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit } ``` +=== "Swift" + + ```swift title="avl_tree.swift" + + ``` + 「结点高度」是最远叶结点到该结点的距离,即走过的「边」的数量。需要特别注意,**叶结点的高度为 0 ,空结点的高度为 -1** 。我们封装两个工具函数,分别用于获取与更新结点的高度。 === "Java" @@ -176,6 +182,12 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit } ``` +=== "Swift" + + ```swift title="avl_tree.swift" + + ``` + ### 结点平衡因子 结点的「平衡因子 Balance Factor」是 **结点的左子树高度减去右子树高度**,并定义空结点的平衡因子为 0 。同样地,我们将获取结点平衡因子封装成函数,以便后续使用。 @@ -247,6 +259,12 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit } ``` +=== "Swift" + + ```swift title="avl_tree.swift" + + ``` + !!! note 设平衡因子为 $f$ ,则一棵 AVL 树的任意结点的平衡因子皆满足 $-1 \le f \le 1$ 。 @@ -361,6 +379,12 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 ``` +=== "Swift" + + ```swift title="avl_tree.swift" + + ``` + ### Case 2 - 左旋 类似地,如果将取上述失衡二叉树的“镜像”,那么则需要「左旋」操作。 @@ -457,6 +481,12 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 } ``` +=== "Swift" + + ```swift title="avl_tree.swift" + + ``` + ### Case 3 - 先左后右 对于下图的失衡结点 3 ,**单一使用左旋或右旋都无法使子树恢复平衡**,此时需要「先左旋后右旋」,即先对 `child` 执行「左旋」,再对 `node` 执行「右旋」。 @@ -626,6 +656,12 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 } ``` +=== "Swift" + + ```swift title="avl_tree.swift" + + ``` + ## AVL 树常用操作 ### 插入结点 @@ -744,6 +780,12 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 } ``` +=== "Swift" + + ```swift title="avl_tree.swift" + + ``` + ### 删除结点 「AVL 树」删除结点操作与「二叉搜索树」删除结点操作总体相同。类似地,**在删除结点后,也需要从底至顶地执行旋转操作,使所有失衡结点恢复平衡**。 @@ -902,6 +944,12 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 } ``` +=== "Swift" + + ```swift title="avl_tree.swift" + + ``` + ### 查找结点 「AVL 树」的结点查找操作与「二叉搜索树」一致,在此不再赘述。 diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index 60a5598bd..979612852 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -192,6 +192,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="binary_search_tree.swift" + + ``` + ### 插入结点 给定一个待插入元素 `num` ,为了保持二叉搜索树“左子树 < 根结点 < 右子树”的性质,插入操作分为两步: @@ -422,6 +428,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="binary_search_tree.swift" + + ``` + 为了插入结点,需要借助 **辅助结点 `prev`** 保存上一轮循环的结点,这样在遍历到 $\text{null}$ 时,我们也可以获取到其父结点,从而完成结点插入操作。 与查找结点相同,插入结点使用 $O(\log n)$ 时间。 @@ -808,6 +820,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="binary_search_tree.swift" + + ``` + ## 二叉搜索树的优势 假设给定 $n$ 个数字,最常用的存储方式是「数组」,那么对于这串乱序的数字,常见操作的效率为: diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 7977ab6a2..077c03e0d 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -106,6 +106,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="" + + ``` + 结点的两个指针分别指向「左子结点 Left Child Node」和「右子结点 Right Child Node」,并且称该结点为两个子结点的「父结点 Parent Node」。给定二叉树某结点,将左子结点以下的树称为该结点的「左子树 Left Subtree」,右子树同理。 除了叶结点外,每个结点都有子结点和子树。例如,若将上图的「结点 2」看作父结点,那么其左子结点和右子结点分别为「结点 4」和「结点 5」,左子树和右子树分别为「结点 4 以下的树」和「结点 5 以下的树」。 @@ -263,6 +269,12 @@ comments: true n2.right = n5; ``` +=== "Swift" + + ```swift title="binary_tree.swift" + + ``` + **插入与删除结点**。与链表类似,插入与删除结点都可以通过修改指针实现。 ![binary_tree_add_remove](binary_tree.assets/binary_tree_add_remove.png) @@ -358,6 +370,12 @@ comments: true n1.left = n2; ``` +=== "Swift" + + ```swift title="binary_tree.swift" + + ``` + !!! note 插入结点会改变二叉树的原有逻辑结构,删除结点往往意味着删除了该结点的所有子树。因此,二叉树中的插入与删除一般都是由一套操作配合完成的,这样才能实现有意义的操作。 @@ -495,6 +513,12 @@ comments: true int?[] tree = { 1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15 }; ``` +=== "Swift" + + ```swift title="" + + ``` + ![array_representation_with_empty](binary_tree.assets/array_representation_with_empty.png) 回顾「完全二叉树」的定义,其只有最底层有空结点,并且最底层的结点尽量靠左,因而所有空结点都一定出现在层序遍历序列的末尾。**因为我们先验地确定了空位的位置,所以在使用数组表示完全二叉树时,可以省略存储“空位”**。因此,完全二叉树非常适合使用数组来表示。 diff --git a/docs/chapter_tree/binary_tree_traversal.md b/docs/chapter_tree/binary_tree_traversal.md index f46348412..c64077072 100644 --- a/docs/chapter_tree/binary_tree_traversal.md +++ b/docs/chapter_tree/binary_tree_traversal.md @@ -185,6 +185,12 @@ comments: true ``` +=== "Swift" + + ```swift title="binary_tree_bfs.swift" + + ``` + ## 前序、中序、后序遍历 相对地,前、中、后序遍历皆属于「深度优先遍历 Depth-First Traversal」,其体现着一种“先走到尽头,再回头继续”的回溯遍历方式。 @@ -443,6 +449,12 @@ comments: true } ``` +=== "Swift" + + ```swift title="binary_tree_dfs.swift" + + ``` + !!! note 使用循环一样可以实现前、中、后序遍历,但代码相对繁琐,有兴趣的同学可以自行实现。 From 230c7723d53746c1fcf8c3d08849d5d67730336d Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Sun, 8 Jan 2023 19:55:08 +0800 Subject: [PATCH 124/158] Update comment format of array.swift --- .../chapter_array_and_linkedlist/array.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/codes/swift/chapter_array_and_linkedlist/array.swift b/codes/swift/chapter_array_and_linkedlist/array.swift index 08cd0707f..344dc5abc 100644 --- a/codes/swift/chapter_array_and_linkedlist/array.swift +++ b/codes/swift/chapter_array_and_linkedlist/array.swift @@ -69,34 +69,34 @@ func find(nums: [Int], target: Int) -> Int { @main enum _Array { - // Driver Code + /* Driver Code */ static func main() { - // 初始化数组 + /* 初始化数组 */ let arr = Array(repeating: 0, count: 5) print("数组 arr = \(arr)") var nums = [1, 3, 2, 5, 4] print("数组 nums = \(nums)") - // 随机访问 + /* 随机访问 */ let randomNum = randomAccess(nums: nums) print("在 nums 中获取随机元素 \(randomNum)") - // 长度扩展 + /* 长度扩展 */ nums = extend(nums: nums, enlarge: 3) print("将数组长度扩展至 8 ,得到 nums = \(nums)") - // 插入元素 + /* 插入元素 */ insert(nums: &nums, num: 6, index: 3) print("在索引 3 处插入数字 6 ,得到 nums = \(nums)") - // 删除元素 + /* 删除元素 */ remove(nums: &nums, index: 2) print("删除索引 2 处的元素,得到 nums = \(nums)") - // 遍历数组 + /* 遍历数组 */ traverse(nums: nums) - // 查找元素 + /* 查找元素 */ let index = find(nums: nums, target: 3) print("在 nums 中查找元素 3 ,得到索引 = \(index)") } From 7556558704ec593d0509869c8b5a0d5a297baa7e Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Sun, 8 Jan 2023 20:22:59 +0800 Subject: [PATCH 125/158] feat: add Swift codes for linked_list article --- codes/swift/Package.swift | 2 + .../linked_list.swift | 91 +++++++++++++++++++ codes/swift/utils/PrintUtil.swift | 10 ++ .../linked_list.md | 73 ++++++++++++++- 4 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 codes/swift/chapter_array_and_linkedlist/linked_list.swift diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index d9368354b..4cf3601c7 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -10,6 +10,7 @@ let package = Package( .executable(name: "space_complexity", targets: ["space_complexity"]), .executable(name: "leetcode_two_sum", targets: ["leetcode_two_sum"]), .executable(name: "array", targets: ["array"]), + .executable(name: "linked_list", targets: ["linked_list"]), ], targets: [ .target(name: "utils", path: "utils"), @@ -18,5 +19,6 @@ let package = Package( .executableTarget(name: "space_complexity", dependencies: ["utils"], path: "chapter_computational_complexity", sources: ["space_complexity.swift"]), .executableTarget(name: "leetcode_two_sum", path: "chapter_computational_complexity", sources: ["leetcode_two_sum.swift"]), .executableTarget(name: "array", path: "chapter_array_and_linkedlist", sources: ["array.swift"]), + .executableTarget(name: "linked_list", dependencies: ["utils"], path: "chapter_array_and_linkedlist", sources: ["linked_list.swift"]), ] ) diff --git a/codes/swift/chapter_array_and_linkedlist/linked_list.swift b/codes/swift/chapter_array_and_linkedlist/linked_list.swift new file mode 100644 index 000000000..06837f750 --- /dev/null +++ b/codes/swift/chapter_array_and_linkedlist/linked_list.swift @@ -0,0 +1,91 @@ +/** + * File: linked_list.swift + * Created Time: 2023-01-08 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* 在链表的结点 n0 之后插入结点 P */ +func insert(n0: ListNode, P: ListNode) { + let n1 = n0.next + n0.next = P + P.next = n1 +} + +/* 删除链表的结点 n0 之后的首个结点 */ +func remove(n0: ListNode) { + if n0.next == nil { + return + } + // n0 -> P -> n1 + let P = n0.next + let n1 = P?.next + n0.next = n1 + P?.next = nil +} + +/* 访问链表中索引为 index 的结点 */ +func access(head: ListNode, index: Int) -> ListNode? { + var head: ListNode? = head + for _ in 0 ..< index { + head = head?.next + if head == nil { + return nil + } + } + return head +} + +/* 在链表中查找值为 target 的首个结点 */ +func find(head: ListNode, target: Int) -> Int { + var head: ListNode? = head + var index = 0 + while head != nil { + if head?.val == target { + return index + } + head = head?.next + index += 1 + } + return -1 +} + +@main +enum LinkedList { + /* Driver Code */ + static func main() { + /* 初始化链表 */ + // 初始化各个结点 + let n0 = ListNode(x: 1) + let n1 = ListNode(x: 3) + let n2 = ListNode(x: 2) + let n3 = ListNode(x: 5) + let n4 = ListNode(x: 4) + // 构建引用指向 + n0.next = n1 + n1.next = n2 + n2.next = n3 + n3.next = n4 + print("初始化的链表为") + PrintUtil.printLinkedList(head: n0) + + /* 插入结点 */ + insert(n0: n0, P: ListNode(x: 0)) + print("插入结点后的链表为") + PrintUtil.printLinkedList(head: n0) + + /* 删除结点 */ + remove(n0: n0) + print("删除结点后的链表为") + PrintUtil.printLinkedList(head: n0) + + /* 访问结点 */ + let node = access(head: n0, index: 3) + print("链表中索引 3 处的结点的值 = \(node!.val)") + + /* 查找结点 */ + let index = find(head: n0, target: 2) + print("链表中值为 2 的结点的索引 = \(index)") + } +} diff --git a/codes/swift/utils/PrintUtil.swift b/codes/swift/utils/PrintUtil.swift index f2b54693d..cbeab1225 100644 --- a/codes/swift/utils/PrintUtil.swift +++ b/codes/swift/utils/PrintUtil.swift @@ -15,6 +15,16 @@ public enum PrintUtil { } } + public static func printLinkedList(head: ListNode) { + var head: ListNode? = head + var list: [String] = [] + while head != nil { + list.append("\(head!.val)") + head = head?.next + } + print(list.joined(separator: " -> ")) + } + public static func printTree(root: TreeNode?) { printTree(root: root, prev: nil, isLeft: false) } diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index aa9c93ec7..7aa2754ca 100644 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -115,7 +115,15 @@ comments: true === "Swift" ```swift title="" + /* 链表结点类 */ + class ListNode { + var val: Int // 结点值 + var next: ListNode? // 指向下一结点的指针(引用) + init(x: Int) { // 构造函数 + val = x + } + } ``` **尾结点指向什么?** 我们一般将链表的最后一个结点称为「尾结点」,其指向的是「空」,在 Java / C++ / Python 中分别记为 `null` / `nullptr` / `None` 。在不引起歧义下,本书都使用 `null` 来表示空。 @@ -255,7 +263,18 @@ comments: true === "Swift" ```swift title="linked_list.swift" - + /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ + // 初始化各个结点 + let n0 = ListNode(x: 1) + let n1 = ListNode(x: 3) + let n2 = ListNode(x: 2) + let n3 = ListNode(x: 5) + let n4 = ListNode(x: 4) + // 构建引用指向 + n0.next = n1 + n1.next = n2 + n2.next = n3 + n3.next = n4 ``` ## 链表优点 @@ -425,7 +444,24 @@ comments: true === "Swift" ```swift title="linked_list.swift" + /* 在链表的结点 n0 之后插入结点 P */ + func insert(n0: ListNode, P: ListNode) { + let n1 = n0.next + n0.next = P + P.next = n1 + } + /* 删除链表的结点 n0 之后的首个结点 */ + func remove(n0: ListNode) { + if n0.next == nil { + return + } + // n0 -> P -> n1 + let P = n0.next + let n1 = P?.next + n0.next = n1 + P?.next = nil + } ``` ## 链表缺点 @@ -541,7 +577,17 @@ comments: true === "Swift" ```swift title="linked_list.swift" - + /* 访问链表中索引为 index 的结点 */ + func access(head: ListNode, index: Int) -> ListNode? { + var head: ListNode? = head + for _ in 0 ..< index { + head = head?.next + if head == nil { + return nil + } + } + return head + } ``` **链表的内存占用多**。链表以结点为单位,每个结点除了保存值外,还需额外保存指针(引用)。这意味着同样数据量下,链表比数组需要占用更多内存空间。 @@ -674,7 +720,19 @@ comments: true === "Swift" ```swift title="linked_list.swift" - + /* 在链表中查找值为 target 的首个结点 */ + func find(head: ListNode, target: Int) -> Int { + var head: ListNode? = head + var index = 0 + while head != nil { + if head?.val == target { + return index + } + head = head?.next + index += 1 + } + return -1 + } ``` ## 常见链表类型 @@ -793,7 +851,16 @@ comments: true === "Swift" ```swift title="" + /* 双向链表结点类 */ + class ListNode { + var val: Int // 结点值 + var next: ListNode? // 指向后继结点的指针(引用) + var prev: ListNode? // 指向前驱结点的指针(引用) + init(x: Int) { // 构造函数 + val = x + } + } ``` ![linkedlist_common_types](linked_list.assets/linkedlist_common_types.png) From e8f7d8f8bab845eeca8082a0035cf01002739cfd Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Sun, 8 Jan 2023 20:30:09 +0800 Subject: [PATCH 126/158] Update .gitignore --- .gitignore | 4 +- codes/java/chapter_heap/my_heap.java | 115 --------------------------- 2 files changed, 1 insertion(+), 118 deletions(-) delete mode 100644 codes/java/chapter_heap/my_heap.java diff --git a/.gitignore b/.gitignore index e93873c95..c960a1bb3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # Editor .vscode/ .idea/ +hello-algo.iml # mkdocs files site/ @@ -13,6 +14,3 @@ docs/overrides/ # python files __pycache__ - -# in-progress articles -docs/chapter_heap diff --git a/codes/java/chapter_heap/my_heap.java b/codes/java/chapter_heap/my_heap.java deleted file mode 100644 index da2dffc0a..000000000 --- a/codes/java/chapter_heap/my_heap.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * File: my_heap.java - * Created Time: 2023-01-07 - * Author: Krahets (krahets@163.com) - */ - -package chapter_heap; - -import include.*; -import java.util.*; - -class MaxHeap { - private List heap; - - public MaxHeap() { - heap = new ArrayList<>(); - } - - public MaxHeap(List nums) { - // 将元素拷贝至堆中 - heap = new ArrayList<>(nums); - // 堆化除叶结点外的其他所有结点 - for (int i = parent(size() - 1); i >= 0; i--) { - heapify(i); - } - } - - /* 获取左子结点 */ - private int left(int i) { - return 2 * i + 1; - } - - /* 获取右子结点 */ - private int right(int i) { - return 2 * i + 2; - } - - /* 获取父结点 */ - private int parent(int i) { - return (i - 1) / 2; - } - - /* 交换元素 */ - private void swap(int i, int j) { - int tmp = heap.get(i); - heap.set(i, j); - heap.set(j, tmp); - } - - public int size() { - return heap.size(); - } - - public boolean isEmpty() { - return size() == 0; - } - - /* 获取堆顶元素 */ - public int peek() { - return heap.get(0); - } - - /* 元素入堆 */ - public void push(int val) { - heap.add(val); - - // 从底至顶堆化 - int i = size(); - while (true) { - int p = parent(i); - if (p < 0 || heap.get(i) > heap.get(p)) - break; - swap(i, p); - i = p; - } - } - - /* 元素出堆 */ - public int poll() { - // 判空处理 - if (isEmpty()) - throw new EmptyStackException(); - // 交换根结点与右下角(即最后一个)结点 - swap(0, size() - 1); - // 删除结点 - int val = heap.remove(size() - 1); - // 从顶至底堆化 - heapify(0); - // 返回堆顶元素 - return val; - } - - /* 从结点 i 开始,从顶至底堆化 */ - private void heapify(int i) { - while (true) { - // 判断结点 i, l, r 中的最大结点,记为 ma ; - int l = left(i), r = right(i), ma = i; - if (heap.get(l) > heap.get(ma)) ma = l; - if (heap.get(r) > heap.get(ma)) ma = r; - // 若结点 i 最大,则无需继续堆化,跳出 - if (ma == i) break; - // 交换结点 i 与结点 max - swap(i, ma); - // 循环向下堆化 - i = ma; - } - } -} - - -public class my_heap { - public static void main(String[] args) { - - } -} From b73ac7bf4b0d3c21ece986a40b734d7f561cc7e4 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Tue, 3 Jan 2023 14:39:31 +0800 Subject: [PATCH 127/158] style(go): fix go code style Make the classes and methods in the package private, in case misuse --- .../chapter_array_and_linkedlist/my_list.go | 24 ++++----- .../space_complexity.go | 32 ++++++------ codes/go/chapter_hashing/array_hash_map.go | 32 ++++++------ .../chapter_searching/hashing_search_test.go | 3 +- codes/go/chapter_sorting/merge_sort.go | 12 ++--- codes/go/chapter_sorting/quick_sort.go | 20 +++---- codes/go/chapter_sorting/quick_sort_test.go | 6 +-- .../go/chapter_stack_and_queue/array_queue.go | 38 +++++++------- .../go/chapter_stack_and_queue/array_stack.go | 36 ++++++------- .../go/chapter_stack_and_queue/deque_test.go | 30 +++++------ .../linkedlist_deque.go | 52 +++++++++---------- .../linkedlist_queue.go | 34 ++++++------ .../linkedlist_stack.go | 34 ++++++------ .../go/chapter_stack_and_queue/queue_test.go | 52 +++++++++---------- .../go/chapter_stack_and_queue/stack_test.go | 52 +++++++++---------- codes/go/chapter_tree/binary_search_tree.go | 24 ++++----- .../chapter_tree/binary_search_tree_test.go | 24 ++++----- codes/go/pkg/print_utils.go | 4 +- 18 files changed, 255 insertions(+), 254 deletions(-) diff --git a/codes/go/chapter_array_and_linkedlist/my_list.go b/codes/go/chapter_array_and_linkedlist/my_list.go index 8f13630be..134096d0a 100644 --- a/codes/go/chapter_array_and_linkedlist/my_list.go +++ b/codes/go/chapter_array_and_linkedlist/my_list.go @@ -5,7 +5,7 @@ package chapter_array_and_linkedlist /* 列表类简易实现 */ -type MyList struct { +type myList struct { numsCapacity int nums []int numsSize int @@ -13,8 +13,8 @@ type MyList struct { } /* 构造函数 */ -func newMyList() *MyList { - return &MyList{ +func newMyList() *myList { + return &myList{ numsCapacity: 10, // 列表容量 nums: make([]int, 10), // 数组(存储列表元素) numsSize: 0, // 列表长度(即当前元素数量) @@ -23,17 +23,17 @@ func newMyList() *MyList { } /* 获取列表长度(即当前元素数量) */ -func (l *MyList) size() int { +func (l *myList) size() int { return l.numsSize } /* 获取列表容量 */ -func (l *MyList) capacity() int { +func (l *myList) capacity() int { return l.numsCapacity } /* 访问元素 */ -func (l *MyList) get(index int) int { +func (l *myList) get(index int) int { // 索引如果越界则抛出异常,下同 if index >= l.numsSize { panic("索引越界") @@ -42,7 +42,7 @@ func (l *MyList) get(index int) int { } /* 更新元素 */ -func (l *MyList) set(num, index int) { +func (l *myList) set(num, index int) { if index >= l.numsSize { panic("索引越界") } @@ -50,7 +50,7 @@ func (l *MyList) set(num, index int) { } /* 尾部添加元素 */ -func (l *MyList) add(num int) { +func (l *myList) add(num int) { // 元素数量超出容量时,触发扩容机制 if l.numsSize == l.numsCapacity { l.extendCapacity() @@ -61,7 +61,7 @@ func (l *MyList) add(num int) { } /* 中间插入元素 */ -func (l *MyList) insert(num, index int) { +func (l *myList) insert(num, index int) { if index >= l.numsSize { panic("索引越界") } @@ -79,7 +79,7 @@ func (l *MyList) insert(num, index int) { } /* 删除元素 */ -func (l *MyList) remove(index int) int { +func (l *myList) remove(index int) int { if index >= l.numsSize { panic("索引越界") } @@ -95,7 +95,7 @@ func (l *MyList) remove(index int) int { } /* 列表扩容 */ -func (l *MyList) extendCapacity() { +func (l *myList) extendCapacity() { // 新建一个长度为 self.__size 的数组,并将原数组拷贝到新数组 l.nums = append(l.nums, make([]int, l.numsCapacity*(l.extendRatio-1))...) // 更新列表容量 @@ -103,7 +103,7 @@ func (l *MyList) extendCapacity() { } /* 返回有效长度的列表 */ -func (l *MyList) toArray() []int { +func (l *myList) toArray() []int { // 仅转换有效长度范围内的列表元素 return l.nums[:l.numsSize] } diff --git a/codes/go/chapter_computational_complexity/space_complexity.go b/codes/go/chapter_computational_complexity/space_complexity.go index eb3ebec97..93f5b3380 100644 --- a/codes/go/chapter_computational_complexity/space_complexity.go +++ b/codes/go/chapter_computational_complexity/space_complexity.go @@ -9,31 +9,31 @@ import ( "strconv" ) -/* Node 结构体 */ -type Node struct { +/* 结构体 */ +type node struct { val int - next *Node + next *node } -/* TreeNode 二叉树 */ -type TreeNode struct { +/* treeNode 二叉树 */ +type treeNode struct { val int - left *TreeNode - right *TreeNode + left *treeNode + right *treeNode } -/* 创建 Node 结构体 */ -func newNode(val int) *Node { - return &Node{val: val} +/* 创建 node 结构体 */ +func newNode(val int) *node { + return &node{val: val} } -/* 创建 TreeNode 结构体 */ -func newTreeNode(val int) *TreeNode { - return &TreeNode{val: val} +/* 创建 treeNode 结构体 */ +func newTreeNode(val int) *treeNode { + return &treeNode{val: val} } /* 输出二叉树 */ -func printTree(root *TreeNode) { +func printTree(root *treeNode) { if root == nil { return } @@ -72,7 +72,7 @@ func spaceLinear(n int) { // 长度为 n 的数组占用 O(n) 空间 _ = make([]int, n) // 长度为 n 的列表占用 O(n) 空间 - var nodes []*Node + var nodes []*node for i := 0; i < n; i++ { nodes = append(nodes, newNode(i)) } @@ -112,7 +112,7 @@ func spaceQuadraticRecur(n int) int { } /* 指数阶(建立满二叉树) */ -func buildTree(n int) *TreeNode { +func buildTree(n int) *treeNode { if n == 0 { return nil } diff --git a/codes/go/chapter_hashing/array_hash_map.go b/codes/go/chapter_hashing/array_hash_map.go index a962b6a4d..5fb1480cc 100644 --- a/codes/go/chapter_hashing/array_hash_map.go +++ b/codes/go/chapter_hashing/array_hash_map.go @@ -7,30 +7,30 @@ package chapter_hashing import "fmt" /* 键值对 int->String */ -type Entry struct { +type entry struct { key int val string } /* 基于数组简易实现的哈希表 */ -type ArrayHashMap struct { - bucket []*Entry +type arrayHashMap struct { + bucket []*entry } -func newArrayHashMap() *ArrayHashMap { +func newArrayHashMap() *arrayHashMap { // 初始化一个长度为 100 的桶(数组) - bucket := make([]*Entry, 100) - return &ArrayHashMap{bucket: bucket} + bucket := make([]*entry, 100) + return &arrayHashMap{bucket: bucket} } /* 哈希函数 */ -func (a *ArrayHashMap) hashFunc(key int) int { +func (a *arrayHashMap) hashFunc(key int) int { index := key % 100 return index } /* 查询操作 */ -func (a *ArrayHashMap) get(key int) string { +func (a *arrayHashMap) get(key int) string { index := a.hashFunc(key) pair := a.bucket[index] if pair == nil { @@ -40,22 +40,22 @@ func (a *ArrayHashMap) get(key int) string { } /* 添加操作 */ -func (a *ArrayHashMap) put(key int, val string) { - pair := &Entry{key: key, val: val} +func (a *arrayHashMap) put(key int, val string) { + pair := &entry{key: key, val: val} index := a.hashFunc(key) a.bucket[index] = pair } /* 删除操作 */ -func (a *ArrayHashMap) remove(key int) { +func (a *arrayHashMap) remove(key int) { index := a.hashFunc(key) // 置为 nil ,代表删除 a.bucket[index] = nil } /* 获取所有键对 */ -func (a *ArrayHashMap) entrySet() []*Entry { - var pairs []*Entry +func (a *arrayHashMap) entrySet() []*entry { + var pairs []*entry for _, pair := range a.bucket { if pair != nil { pairs = append(pairs, pair) @@ -65,7 +65,7 @@ func (a *ArrayHashMap) entrySet() []*Entry { } /* 获取所有键 */ -func (a *ArrayHashMap) keySet() []int { +func (a *arrayHashMap) keySet() []int { var keys []int for _, pair := range a.bucket { if pair != nil { @@ -76,7 +76,7 @@ func (a *ArrayHashMap) keySet() []int { } /* 获取所有值 */ -func (a *ArrayHashMap) valueSet() []string { +func (a *arrayHashMap) valueSet() []string { var values []string for _, pair := range a.bucket { if pair != nil { @@ -87,7 +87,7 @@ func (a *ArrayHashMap) valueSet() []string { } /* 打印哈希表 */ -func (a *ArrayHashMap) print() { +func (a *arrayHashMap) print() { for _, pair := range a.bucket { if pair != nil { fmt.Println(pair.key, "->", pair.val) diff --git a/codes/go/chapter_searching/hashing_search_test.go b/codes/go/chapter_searching/hashing_search_test.go index 4bccd038f..18cf5c651 100644 --- a/codes/go/chapter_searching/hashing_search_test.go +++ b/codes/go/chapter_searching/hashing_search_test.go @@ -6,8 +6,9 @@ package chapter_searching import ( "fmt" - . "github.com/krahets/hello-algo/pkg" "testing" + + . "github.com/krahets/hello-algo/pkg" ) func TestHashingSearch(t *testing.T) { diff --git a/codes/go/chapter_sorting/merge_sort.go b/codes/go/chapter_sorting/merge_sort.go index 1b3a3932d..43aff01a7 100644 --- a/codes/go/chapter_sorting/merge_sort.go +++ b/codes/go/chapter_sorting/merge_sort.go @@ -8,25 +8,25 @@ package chapter_sorting // 左子数组区间 [left, mid] // 右子数组区间 [mid + 1, right] func merge(nums []int, left, mid, right int) { - // 初始化辅助数组 借助 copy模块 + // 初始化辅助数组 借助 copy 模块 tmp := make([]int, right-left+1) for i := left; i <= right; i++ { tmp[i-left] = nums[i] } // 左子数组的起始索引和结束索引 - left_start, left_end := left-left, mid-left + leftStart, leftEnd := left-left, mid-left // 右子数组的起始索引和结束索引 - right_start, right_end := mid+1-left, right-left + rightStart, rightEnd := mid+1-left, right-left // i, j 分别指向左子数组、右子数组的首元素 - i, j := left_start, right_start + i, j := leftStart, rightStart // 通过覆盖原数组 nums 来合并左子数组和右子数组 for k := left; k <= right; k++ { // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ - if i > left_end { + if i > leftEnd { nums[k] = tmp[j] j++ // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ - } else if j > right_end || tmp[i] <= tmp[j] { + } else if j > rightEnd || tmp[i] <= tmp[j] { nums[k] = tmp[i] i++ // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ diff --git a/codes/go/chapter_sorting/quick_sort.go b/codes/go/chapter_sorting/quick_sort.go index 88aa14264..853bce452 100644 --- a/codes/go/chapter_sorting/quick_sort.go +++ b/codes/go/chapter_sorting/quick_sort.go @@ -5,16 +5,16 @@ package chapter_sorting // 快速排序 -type QuickSort struct{} +type quickSort struct{} // 快速排序(中位基准数优化) -type QuickSortMedian struct{} +type quickSortMedian struct{} // 快速排序(尾递归优化) -type QuickSortTailCall struct{} +type quickSortTailCall struct{} /* 哨兵划分 */ -func (q *QuickSort) partition(nums []int, left, right int) int { +func (q *quickSort) partition(nums []int, left, right int) int { // 以 nums[left] 作为基准数 i, j := left, right for i < j { @@ -33,7 +33,7 @@ func (q *QuickSort) partition(nums []int, left, right int) int { } /* 快速排序 */ -func (q *QuickSort) quickSort(nums []int, left, right int) { +func (q *quickSort) quickSort(nums []int, left, right int) { // 子数组长度为 1 时终止递归 if left >= right { return @@ -46,7 +46,7 @@ func (q *QuickSort) quickSort(nums []int, left, right int) { } /* 选取三个元素的中位数 */ -func (q *QuickSortMedian) medianThree(nums []int, left, mid, right int) int { +func (q *quickSortMedian) medianThree(nums []int, left, mid, right int) int { if (nums[left] > nums[mid]) != (nums[left] > nums[right]) { return left } else if (nums[mid] < nums[left]) != (nums[mid] > nums[right]) { @@ -56,7 +56,7 @@ func (q *QuickSortMedian) medianThree(nums []int, left, mid, right int) int { } /* 哨兵划分(三数取中值)*/ -func (q *QuickSortMedian) partition(nums []int, left, right int) int { +func (q *quickSortMedian) partition(nums []int, left, right int) int { // 以 nums[left] 作为基准数 med := q.medianThree(nums, left, (left+right)/2, right) // 将中位数交换至数组最左端 @@ -79,7 +79,7 @@ func (q *QuickSortMedian) partition(nums []int, left, right int) int { } /* 快速排序 */ -func (q *QuickSortMedian) quickSort(nums []int, left, right int) { +func (q *quickSortMedian) quickSort(nums []int, left, right int) { // 子数组长度为 1 时终止递归 if left >= right { return @@ -92,7 +92,7 @@ func (q *QuickSortMedian) quickSort(nums []int, left, right int) { } /* 哨兵划分 */ -func (q *QuickSortTailCall) partition(nums []int, left, right int) int { +func (q *quickSortTailCall) partition(nums []int, left, right int) int { // 以 nums[left] 作为基准数 i, j := left, right for i < j { @@ -111,7 +111,7 @@ func (q *QuickSortTailCall) partition(nums []int, left, right int) int { } /* 快速排序(尾递归优化)*/ -func (q *QuickSortTailCall) quickSort(nums []int, left, right int) { +func (q *quickSortTailCall) quickSort(nums []int, left, right int) { // 子数组长度为 1 时终止 for left < right { // 哨兵划分操作 diff --git a/codes/go/chapter_sorting/quick_sort_test.go b/codes/go/chapter_sorting/quick_sort_test.go index 86ae0115a..d780663e1 100644 --- a/codes/go/chapter_sorting/quick_sort_test.go +++ b/codes/go/chapter_sorting/quick_sort_test.go @@ -11,7 +11,7 @@ import ( // 快速排序 func TestQuickSort(t *testing.T) { - q := QuickSort{} + q := quickSort{} nums := []int{4, 1, 3, 1, 5, 2} q.quickSort(nums, 0, len(nums)-1) fmt.Println("快速排序完成后 nums = ", nums) @@ -19,7 +19,7 @@ func TestQuickSort(t *testing.T) { // 快速排序(中位基准数优化) func TestQuickSortMedian(t *testing.T) { - q := QuickSortMedian{} + q := quickSortMedian{} nums := []int{4, 1, 3, 1, 5, 2} q.quickSort(nums, 0, len(nums)-1) fmt.Println("快速排序(中位基准数优化)完成后 nums = ", nums) @@ -27,7 +27,7 @@ func TestQuickSortMedian(t *testing.T) { // 快速排序(尾递归优化) func TestQuickSortTailCall(t *testing.T) { - q := QuickSortTailCall{} + q := quickSortTailCall{} nums := []int{4, 1, 3, 1, 5, 2} q.quickSort(nums, 0, len(nums)-1) fmt.Println("快速排序(尾递归优化)完成后 nums = ", nums) diff --git a/codes/go/chapter_stack_and_queue/array_queue.go b/codes/go/chapter_stack_and_queue/array_queue.go index 863768ba8..7d0367df8 100644 --- a/codes/go/chapter_stack_and_queue/array_queue.go +++ b/codes/go/chapter_stack_and_queue/array_queue.go @@ -5,16 +5,16 @@ package chapter_stack_and_queue /* 基于环形数组实现的队列 */ -type ArrayQueue struct { +type arrayQueue struct { data []int // 用于存储队列元素的数组 capacity int // 队列容量(即最多容量的元素个数) front int // 头指针,指向队首 rear int // 尾指针,指向队尾 + 1 } -// NewArrayQueue 基于环形数组实现的队列 -func NewArrayQueue(capacity int) *ArrayQueue { - return &ArrayQueue{ +// newArrayQueue 基于环形数组实现的队列 +func newArrayQueue(capacity int) *arrayQueue { + return &arrayQueue{ data: make([]int, capacity), capacity: capacity, front: 0, @@ -22,21 +22,21 @@ func NewArrayQueue(capacity int) *ArrayQueue { } } -// Size 获取队列的长度 -func (q *ArrayQueue) Size() int { +// size 获取队列的长度 +func (q *arrayQueue) size() int { size := (q.capacity + q.rear - q.front) % q.capacity return size } -// IsEmpty 判断队列是否为空 -func (q *ArrayQueue) IsEmpty() bool { +// isEmpty 判断队列是否为空 +func (q *arrayQueue) isEmpty() bool { return q.rear-q.front == 0 } -// Offer 入队 -func (q *ArrayQueue) Offer(v int) { +// offer 入队 +func (q *arrayQueue) offer(v int) { // 当 rear == capacity 表示队列已满 - if q.Size() == q.capacity { + if q.size() == q.capacity { return } // 尾结点后添加 @@ -45,9 +45,9 @@ func (q *ArrayQueue) Offer(v int) { q.rear = (q.rear + 1) % q.capacity } -// Poll 出队 -func (q *ArrayQueue) Poll() any { - if q.IsEmpty() { +// poll 出队 +func (q *arrayQueue) poll() any { + if q.isEmpty() { return nil } v := q.data[q.front] @@ -56,9 +56,9 @@ func (q *ArrayQueue) Poll() any { return v } -// Peek 访问队首元素 -func (q *ArrayQueue) Peek() any { - if q.IsEmpty() { +// peek 访问队首元素 +func (q *arrayQueue) peek() any { + if q.isEmpty() { return nil } v := q.data[q.front] @@ -66,6 +66,6 @@ func (q *ArrayQueue) Peek() any { } // 获取 Slice 用于打印 -func (s *ArrayQueue) toSlice() []int { - return s.data[s.front:s.rear] +func (q *arrayQueue) toSlice() []int { + return q.data[q.front:q.rear] } diff --git a/codes/go/chapter_stack_and_queue/array_stack.go b/codes/go/chapter_stack_and_queue/array_stack.go index dca97404d..ef32ebe4c 100644 --- a/codes/go/chapter_stack_and_queue/array_stack.go +++ b/codes/go/chapter_stack_and_queue/array_stack.go @@ -5,47 +5,47 @@ package chapter_stack_and_queue /* 基于数组实现的栈 */ -type ArrayStack struct { +type arrayStack struct { data []int // 数据 } -func NewArrayStack() *ArrayStack { - return &ArrayStack{ +func newArrayStack() *arrayStack { + return &arrayStack{ // 设置栈的长度为 0,容量为 16 data: make([]int, 0, 16), } } -// Size 栈的长度 -func (s *ArrayStack) Size() int { +// size 栈的长度 +func (s *arrayStack) size() int { return len(s.data) } -// IsEmpty 栈是否为空 -func (s *ArrayStack) IsEmpty() bool { - return s.Size() == 0 +// isEmpty 栈是否为空 +func (s *arrayStack) isEmpty() bool { + return s.size() == 0 } -// Push 入栈 -func (s *ArrayStack) Push(v int) { +// push 入栈 +func (s *arrayStack) push(v int) { // 切片会自动扩容 s.data = append(s.data, v) } -// Pop 出栈 -func (s *ArrayStack) Pop() any { +// pop 出栈 +func (s *arrayStack) pop() any { // 弹出栈前,先判断是否为空 - if s.IsEmpty() { + if s.isEmpty() { return nil } - val := s.Peek() + val := s.peek() s.data = s.data[:len(s.data)-1] return val } -// Peek 获取栈顶元素 -func (s *ArrayStack) Peek() any { - if s.IsEmpty() { +// peek 获取栈顶元素 +func (s *arrayStack) peek() any { + if s.isEmpty() { return nil } val := s.data[len(s.data)-1] @@ -53,6 +53,6 @@ func (s *ArrayStack) Peek() any { } // 获取 Slice 用于打印 -func (s *ArrayStack) toSlice() []int { +func (s *arrayStack) toSlice() []int { return s.data } diff --git a/codes/go/chapter_stack_and_queue/deque_test.go b/codes/go/chapter_stack_and_queue/deque_test.go index 647ac642e..4c0f2f533 100644 --- a/codes/go/chapter_stack_and_queue/deque_test.go +++ b/codes/go/chapter_stack_and_queue/deque_test.go @@ -51,48 +51,48 @@ func TestDeque(t *testing.T) { func TestLinkedListDeque(t *testing.T) { // 初始化队列 - deque := NewLinkedListDeque() + deque := newLinkedListDeque() // 元素入队 - deque.OfferLast(2) - deque.OfferLast(5) - deque.OfferLast(4) - deque.OfferFirst(3) - deque.OfferFirst(1) + deque.offerLast(2) + deque.offerLast(5) + deque.offerLast(4) + deque.offerFirst(3) + deque.offerFirst(1) fmt.Print("队列 deque = ") PrintList(deque.toList()) // 访问队首元素 - front := deque.PeekFirst() + front := deque.peekFirst() fmt.Println("队首元素 front =", front) - rear := deque.PeekLast() + rear := deque.peekLast() fmt.Println("队尾元素 rear =", rear) // 元素出队 - pollFirst := deque.PollFirst() + pollFirst := deque.pollFirst() fmt.Print("队首出队元素 pollFirst = ", pollFirst, ",队首出队后 deque = ") PrintList(deque.toList()) - pollLast := deque.PollLast() + pollLast := deque.pollLast() fmt.Print("队尾出队元素 pollLast = ", pollLast, ",队尾出队后 deque = ") PrintList(deque.toList()) // 获取队的长度 - size := deque.Size() + size := deque.size() fmt.Println("队的长度 size =", size) // 判断是否为空 - isEmpty := deque.IsEmpty() + isEmpty := deque.isEmpty() fmt.Println("队是否为空 =", isEmpty) } // BenchmarkArrayQueue 67.92 ns/op in Mac M1 Pro func BenchmarkLinkedListDeque(b *testing.B) { - stack := NewLinkedListDeque() + stack := newLinkedListDeque() // use b.N for looping for i := 0; i < b.N; i++ { - stack.OfferLast(777) + stack.offerLast(777) } for i := 0; i < b.N; i++ { - stack.PollFirst() + stack.pollFirst() } } diff --git a/codes/go/chapter_stack_and_queue/linkedlist_deque.go b/codes/go/chapter_stack_and_queue/linkedlist_deque.go index 019aa778b..b1db77ca6 100644 --- a/codes/go/chapter_stack_and_queue/linkedlist_deque.go +++ b/codes/go/chapter_stack_and_queue/linkedlist_deque.go @@ -8,31 +8,31 @@ import ( "container/list" ) -// LinkedListDeque 基于链表实现的双端队列, 使用内置包 list 来实现栈 -type LinkedListDeque struct { +// linkedListDeque 基于链表实现的双端队列, 使用内置包 list 来实现栈 +type linkedListDeque struct { data *list.List } -// NewLinkedListDeque 初始化双端队列 -func NewLinkedListDeque() *LinkedListDeque { - return &LinkedListDeque{ +// newLinkedListDeque 初始化双端队列 +func newLinkedListDeque() *linkedListDeque { + return &linkedListDeque{ data: list.New(), } } -// OfferFirst 队首元素入队 -func (s *LinkedListDeque) OfferFirst(value any) { +// offerFirst 队首元素入队 +func (s *linkedListDeque) offerFirst(value any) { s.data.PushFront(value) } -// OfferLast 队尾元素入队 -func (s *LinkedListDeque) OfferLast(value any) { +// offerLast 队尾元素入队 +func (s *linkedListDeque) offerLast(value any) { s.data.PushBack(value) } -// PollFirst 队首元素出队 -func (s *LinkedListDeque) PollFirst() any { - if s.IsEmpty() { +// pollFirst 队首元素出队 +func (s *linkedListDeque) pollFirst() any { + if s.isEmpty() { return nil } e := s.data.Front() @@ -40,9 +40,9 @@ func (s *LinkedListDeque) PollFirst() any { return e.Value } -// PollLast 队尾元素出队 -func (s *LinkedListDeque) PollLast() any { - if s.IsEmpty() { +// pollLast 队尾元素出队 +func (s *linkedListDeque) pollLast() any { + if s.isEmpty() { return nil } e := s.data.Back() @@ -50,35 +50,35 @@ func (s *LinkedListDeque) PollLast() any { return e.Value } -// PeekFirst 访问队首元素 -func (s *LinkedListDeque) PeekFirst() any { - if s.IsEmpty() { +// peekFirst 访问队首元素 +func (s *linkedListDeque) peekFirst() any { + if s.isEmpty() { return nil } e := s.data.Front() return e.Value } -// PeekLast 访问队尾元素 -func (s *LinkedListDeque) PeekLast() any { - if s.IsEmpty() { +// peekLast 访问队尾元素 +func (s *linkedListDeque) peekLast() any { + if s.isEmpty() { return nil } e := s.data.Back() return e.Value } -// Size 获取队列的长度 -func (s *LinkedListDeque) Size() int { +// size 获取队列的长度 +func (s *linkedListDeque) size() int { return s.data.Len() } -// IsEmpty 判断队列是否为空 -func (s *LinkedListDeque) IsEmpty() bool { +// isEmpty 判断队列是否为空 +func (s *linkedListDeque) isEmpty() bool { return s.data.Len() == 0 } // 获取 List 用于打印 -func (s *LinkedListDeque) toList() *list.List { +func (s *linkedListDeque) toList() *list.List { return s.data } diff --git a/codes/go/chapter_stack_and_queue/linkedlist_queue.go b/codes/go/chapter_stack_and_queue/linkedlist_queue.go index d30461805..f5d164a82 100644 --- a/codes/go/chapter_stack_and_queue/linkedlist_queue.go +++ b/codes/go/chapter_stack_and_queue/linkedlist_queue.go @@ -9,26 +9,26 @@ import ( ) /* 基于链表实现的队列 */ -type LinkedListQueue struct { +type linkedListQueue struct { // 使用内置包 list 来实现队列 data *list.List } -// NewLinkedListQueue 初始化链表 -func NewLinkedListQueue() *LinkedListQueue { - return &LinkedListQueue{ +// newLinkedListQueue 初始化链表 +func newLinkedListQueue() *linkedListQueue { + return &linkedListQueue{ data: list.New(), } } -// Offer 入队 -func (s *LinkedListQueue) Offer(value any) { +// offer 入队 +func (s *linkedListQueue) offer(value any) { s.data.PushBack(value) } -// Poll 出队 -func (s *LinkedListQueue) Poll() any { - if s.IsEmpty() { +// poll 出队 +func (s *linkedListQueue) poll() any { + if s.isEmpty() { return nil } e := s.data.Front() @@ -36,26 +36,26 @@ func (s *LinkedListQueue) Poll() any { return e.Value } -// Peek 访问队首元素 -func (s *LinkedListQueue) Peek() any { - if s.IsEmpty() { +// peek 访问队首元素 +func (s *linkedListQueue) peek() any { + if s.isEmpty() { return nil } e := s.data.Front() return e.Value } -// Size 获取队列的长度 -func (s *LinkedListQueue) Size() int { +// size 获取队列的长度 +func (s *linkedListQueue) size() int { return s.data.Len() } -// IsEmpty 判断队列是否为空 -func (s *LinkedListQueue) IsEmpty() bool { +// isEmpty 判断队列是否为空 +func (s *linkedListQueue) isEmpty() bool { return s.data.Len() == 0 } // 获取 List 用于打印 -func (s *LinkedListQueue) toList() *list.List { +func (s *linkedListQueue) toList() *list.List { return s.data } diff --git a/codes/go/chapter_stack_and_queue/linkedlist_stack.go b/codes/go/chapter_stack_and_queue/linkedlist_stack.go index ab8a06284..8509c2b83 100644 --- a/codes/go/chapter_stack_and_queue/linkedlist_stack.go +++ b/codes/go/chapter_stack_and_queue/linkedlist_stack.go @@ -9,26 +9,26 @@ import ( ) /* 基于链表实现的栈 */ -type LinkedListStack struct { +type linkedListStack struct { // 使用内置包 list 来实现栈 data *list.List } -// NewLinkedListStack 初始化链表 -func NewLinkedListStack() *LinkedListStack { - return &LinkedListStack{ +// newLinkedListStack 初始化链表 +func newLinkedListStack() *linkedListStack { + return &linkedListStack{ data: list.New(), } } -// Push 入栈 -func (s *LinkedListStack) Push(value int) { +// push 入栈 +func (s *linkedListStack) push(value int) { s.data.PushBack(value) } -// Pop 出栈 -func (s *LinkedListStack) Pop() any { - if s.IsEmpty() { +// pop 出栈 +func (s *linkedListStack) pop() any { + if s.isEmpty() { return nil } e := s.data.Back() @@ -36,26 +36,26 @@ func (s *LinkedListStack) Pop() any { return e.Value } -// Peek 访问栈顶元素 -func (s *LinkedListStack) Peek() any { - if s.IsEmpty() { +// peek 访问栈顶元素 +func (s *linkedListStack) peek() any { + if s.isEmpty() { return nil } e := s.data.Back() return e.Value } -// Size 获取栈的长度 -func (s *LinkedListStack) Size() int { +// size 获取栈的长度 +func (s *linkedListStack) size() int { return s.data.Len() } -// IsEmpty 判断栈是否为空 -func (s *LinkedListStack) IsEmpty() bool { +// isEmpty 判断栈是否为空 +func (s *linkedListStack) isEmpty() bool { return s.data.Len() == 0 } // 获取 List 用于打印 -func (s *LinkedListStack) toList() *list.List { +func (s *linkedListStack) toList() *list.List { return s.data } diff --git a/codes/go/chapter_stack_and_queue/queue_test.go b/codes/go/chapter_stack_and_queue/queue_test.go index 368becbc7..b9ec79df2 100644 --- a/codes/go/chapter_stack_and_queue/queue_test.go +++ b/codes/go/chapter_stack_and_queue/queue_test.go @@ -48,87 +48,87 @@ func TestQueue(t *testing.T) { func TestArrayQueue(t *testing.T) { // 初始化队列,使用队列的通用接口 capacity := 10 - queue := NewArrayQueue(capacity) + queue := newArrayQueue(capacity) // 元素入队 - queue.Offer(1) - queue.Offer(3) - queue.Offer(2) - queue.Offer(5) - queue.Offer(4) + queue.offer(1) + queue.offer(3) + queue.offer(2) + queue.offer(5) + queue.offer(4) fmt.Print("队列 queue = ") PrintSlice(queue.toSlice()) // 访问队首元素 - peek := queue.Peek() + peek := queue.peek() fmt.Println("队首元素 peek =", peek) // 元素出队 - poll := queue.Poll() + poll := queue.poll() fmt.Print("出队元素 poll = ", poll, ", 出队后 queue = ") PrintSlice(queue.toSlice()) // 获取队的长度 - size := queue.Size() + size := queue.size() fmt.Println("队的长度 size =", size) // 判断是否为空 - isEmpty := queue.IsEmpty() + isEmpty := queue.isEmpty() fmt.Println("队是否为空 =", isEmpty) } func TestLinkedListQueue(t *testing.T) { // 初始化队 - queue := NewLinkedListQueue() + queue := newLinkedListQueue() // 元素入队 - queue.Offer(1) - queue.Offer(3) - queue.Offer(2) - queue.Offer(5) - queue.Offer(4) + queue.offer(1) + queue.offer(3) + queue.offer(2) + queue.offer(5) + queue.offer(4) fmt.Print("队列 queue = ") PrintList(queue.toList()) // 访问队首元素 - peek := queue.Peek() + peek := queue.peek() fmt.Println("队首元素 peek =", peek) // 元素出队 - poll := queue.Poll() + poll := queue.poll() fmt.Print("出队元素 poll = ", poll, ", 出队后 queue = ") PrintList(queue.toList()) // 获取队的长度 - size := queue.Size() + size := queue.size() fmt.Println("队的长度 size =", size) // 判断是否为空 - isEmpty := queue.IsEmpty() + isEmpty := queue.isEmpty() fmt.Println("队是否为空 =", isEmpty) } // BenchmarkArrayQueue 8 ns/op in Mac M1 Pro func BenchmarkArrayQueue(b *testing.B) { capacity := 1000 - stack := NewArrayQueue(capacity) + stack := newArrayQueue(capacity) // use b.N for looping for i := 0; i < b.N; i++ { - stack.Offer(777) + stack.offer(777) } for i := 0; i < b.N; i++ { - stack.Poll() + stack.poll() } } // BenchmarkLinkedQueue 62.66 ns/op in Mac M1 Pro func BenchmarkLinkedQueue(b *testing.B) { - stack := NewLinkedListQueue() + stack := newLinkedListQueue() // use b.N for looping for i := 0; i < b.N; i++ { - stack.Offer(777) + stack.offer(777) } for i := 0; i < b.N; i++ { - stack.Poll() + stack.poll() } } diff --git a/codes/go/chapter_stack_and_queue/stack_test.go b/codes/go/chapter_stack_and_queue/stack_test.go index 9dc97e697..a4cf338b7 100644 --- a/codes/go/chapter_stack_and_queue/stack_test.go +++ b/codes/go/chapter_stack_and_queue/stack_test.go @@ -46,85 +46,85 @@ func TestStack(t *testing.T) { func TestArrayStack(t *testing.T) { // 初始化栈, 使用接口承接 - stack := NewArrayStack() + stack := newArrayStack() // 元素入栈 - stack.Push(1) - stack.Push(3) - stack.Push(2) - stack.Push(5) - stack.Push(4) + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) fmt.Print("栈 stack = ") PrintSlice(stack.toSlice()) // 访问栈顶元素 - peek := stack.Peek() + peek := stack.peek() fmt.Println("栈顶元素 peek =", peek) // 元素出栈 - pop := stack.Pop() + pop := stack.pop() fmt.Print("出栈元素 pop = ", pop, ", 出栈后 stack = ") PrintSlice(stack.toSlice()) // 获取栈的长度 - size := stack.Size() + size := stack.size() fmt.Println("栈的长度 size =", size) // 判断是否为空 - isEmpty := stack.IsEmpty() + isEmpty := stack.isEmpty() fmt.Println("栈是否为空 =", isEmpty) } func TestLinkedListStack(t *testing.T) { // 初始化栈 - stack := NewLinkedListStack() + stack := newLinkedListStack() // 元素入栈 - stack.Push(1) - stack.Push(3) - stack.Push(2) - stack.Push(5) - stack.Push(4) + stack.push(1) + stack.push(3) + stack.push(2) + stack.push(5) + stack.push(4) fmt.Print("栈 stack = ") PrintList(stack.toList()) // 访问栈顶元素 - peek := stack.Peek() + peek := stack.peek() fmt.Println("栈顶元素 peek =", peek) // 元素出栈 - pop := stack.Pop() + pop := stack.pop() fmt.Print("出栈元素 pop = ", pop, ", 出栈后 stack = ") PrintList(stack.toList()) // 获取栈的长度 - size := stack.Size() + size := stack.size() fmt.Println("栈的长度 size =", size) // 判断是否为空 - isEmpty := stack.IsEmpty() + isEmpty := stack.isEmpty() fmt.Println("栈是否为空 =", isEmpty) } // BenchmarkArrayStack 8 ns/op in Mac M1 Pro func BenchmarkArrayStack(b *testing.B) { - stack := NewArrayStack() + stack := newArrayStack() // use b.N for looping for i := 0; i < b.N; i++ { - stack.Push(777) + stack.push(777) } for i := 0; i < b.N; i++ { - stack.Pop() + stack.pop() } } // BenchmarkLinkedListStack 65.02 ns/op in Mac M1 Pro func BenchmarkLinkedListStack(b *testing.B) { - stack := NewLinkedListStack() + stack := newLinkedListStack() // use b.N for looping for i := 0; i < b.N; i++ { - stack.Push(777) + stack.push(777) } for i := 0; i < b.N; i++ { - stack.Pop() + stack.pop() } } diff --git a/codes/go/chapter_tree/binary_search_tree.go b/codes/go/chapter_tree/binary_search_tree.go index c8fc5d62c..61ed400d5 100644 --- a/codes/go/chapter_tree/binary_search_tree.go +++ b/codes/go/chapter_tree/binary_search_tree.go @@ -10,26 +10,26 @@ import ( . "github.com/krahets/hello-algo/pkg" ) -type BinarySearchTree struct { +type binarySearchTree struct { root *TreeNode } -func NewBinarySearchTree(nums []int) *BinarySearchTree { +func newBinarySearchTree(nums []int) *binarySearchTree { // sorting array sort.Ints(nums) root := buildBinarySearchTree(nums, 0, len(nums)-1) - return &BinarySearchTree{ + return &binarySearchTree{ root: root, } } /* 获取根结点 */ -func (bst *BinarySearchTree) GetRoot() *TreeNode { +func (bst *binarySearchTree) getRoot() *TreeNode { return bst.root } /* 获取中序遍历的下一个结点 */ -func (bst *BinarySearchTree) GetInOrderNext(node *TreeNode) *TreeNode { +func (bst *binarySearchTree) getInOrderNext(node *TreeNode) *TreeNode { if node == nil { return node } @@ -41,7 +41,7 @@ func (bst *BinarySearchTree) GetInOrderNext(node *TreeNode) *TreeNode { } /* 查找结点 */ -func (bst *BinarySearchTree) Search(num int) *TreeNode { +func (bst *binarySearchTree) search(num int) *TreeNode { node := bst.root // 循环查找,越过叶结点后跳出 for node != nil { @@ -61,7 +61,7 @@ func (bst *BinarySearchTree) Search(num int) *TreeNode { } /* 插入结点 */ -func (bst *BinarySearchTree) Insert(num int) *TreeNode { +func (bst *binarySearchTree) insert(num int) *TreeNode { cur := bst.root // 若树为空,直接提前返回 if cur == nil { @@ -92,7 +92,7 @@ func (bst *BinarySearchTree) Insert(num int) *TreeNode { } /* 删除结点 */ -func (bst *BinarySearchTree) Remove(num int) *TreeNode { +func (bst *binarySearchTree) remove(num int) *TreeNode { cur := bst.root // 若树为空,直接提前返回 if cur == nil { @@ -136,10 +136,10 @@ func (bst *BinarySearchTree) Remove(num int) *TreeNode { // 子结点数为 2 } else { // 获取中序遍历中待删除结点 cur 的下一个结点 - next := bst.GetInOrderNext(cur) + next := bst.getInOrderNext(cur) temp := next.Val // 递归删除结点 next - bst.Remove(next.Val) + bst.remove(next.Val) // 将 next 的值复制给 cur cur.Val = temp } @@ -160,7 +160,7 @@ func buildBinarySearchTree(nums []int, left, right int) *TreeNode { return root } -// Print binary search tree -func (bst *BinarySearchTree) Print() { +// print binary search tree +func (bst *binarySearchTree) print() { PrintTree(bst.root) } diff --git a/codes/go/chapter_tree/binary_search_tree_test.go b/codes/go/chapter_tree/binary_search_tree_test.go index cdf7d8d48..877f1b541 100644 --- a/codes/go/chapter_tree/binary_search_tree_test.go +++ b/codes/go/chapter_tree/binary_search_tree_test.go @@ -11,31 +11,31 @@ import ( func TestBinarySearchTree(t *testing.T) { nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - bst := NewBinarySearchTree(nums) + bst := newBinarySearchTree(nums) fmt.Println("\n初始化的二叉树为:") - bst.Print() + bst.print() // 获取根结点 - node := bst.GetRoot() + node := bst.getRoot() fmt.Println("\n二叉树的根结点为:", node.Val) // 查找结点 - node = bst.Search(5) + node = bst.search(5) fmt.Println("\n查找到的结点对象为", node, ",结点值 =", node.Val) // 插入结点 - node = bst.Insert(16) + node = bst.insert(16) fmt.Println("\n插入结点后 16 的二叉树为:") - bst.Print() + bst.print() // 删除结点 - bst.Remove(1) + bst.remove(1) fmt.Println("\n删除结点 1 后的二叉树为:") - bst.Print() - bst.Remove(2) + bst.print() + bst.remove(2) fmt.Println("\n删除结点 2 后的二叉树为:") - bst.Print() - bst.Remove(4) + bst.print() + bst.remove(4) fmt.Println("\n删除结点 4 后的二叉树为:") - bst.Print() + bst.print() } diff --git a/codes/go/pkg/print_utils.go b/codes/go/pkg/print_utils.go index a42d48b2f..575ab6b94 100644 --- a/codes/go/pkg/print_utils.go +++ b/codes/go/pkg/print_utils.go @@ -76,7 +76,7 @@ func printTreeHelper(root *TreeNode, prev *trunk, isLeft bool) { printTreeHelper(root.Left, trunk, false) } -// trunk Help to Print tree structure +// trunk Help to print tree structure type trunk struct { prev *trunk str string @@ -103,4 +103,4 @@ func PrintMap[K comparable, V any](m map[K]V) { for key, value := range m { fmt.Println(key, "->", value) } -} \ No newline at end of file +} From 33e2c4f4d31450ac03b201746bca3c3248addcc5 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Sun, 8 Jan 2023 20:00:36 +0800 Subject: [PATCH 128/158] fix(tree): fix ArrToTree in go code --- codes/go/chapter_tree/binary_tree_bfs_test.go | 2 +- codes/go/chapter_tree/binary_tree_dfs_test.go | 2 +- codes/go/pkg/tree_node.go | 17 +++++++++++------ codes/go/pkg/tree_node_test.go | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/codes/go/chapter_tree/binary_tree_bfs_test.go b/codes/go/chapter_tree/binary_tree_bfs_test.go index d32dbdca9..7b1bbf1f5 100644 --- a/codes/go/chapter_tree/binary_tree_bfs_test.go +++ b/codes/go/chapter_tree/binary_tree_bfs_test.go @@ -14,7 +14,7 @@ import ( func TestLevelOrder(t *testing.T) { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - root := ArrToTree([]int{1, 2, 3, 4, 5, 6, 7}) + root := ArrToTree([]any{1, 2, 3, 4, 5, 6, 7}) fmt.Println("\n初始化二叉树: ") PrintTree(root) diff --git a/codes/go/chapter_tree/binary_tree_dfs_test.go b/codes/go/chapter_tree/binary_tree_dfs_test.go index b0db8086c..9e0dfe22f 100644 --- a/codes/go/chapter_tree/binary_tree_dfs_test.go +++ b/codes/go/chapter_tree/binary_tree_dfs_test.go @@ -14,7 +14,7 @@ import ( func TestPreInPostOrderTraversal(t *testing.T) { /* 初始化二叉树 */ // 这里借助了一个从数组直接生成二叉树的函数 - root := ArrToTree([]int{1, 2, 3, 4, 5, 6, 7}) + root := ArrToTree([]any{1, 2, 3, 4, 5, 6, 7}) fmt.Println("\n初始化二叉树: ") PrintTree(root) diff --git a/codes/go/pkg/tree_node.go b/codes/go/pkg/tree_node.go index b1e630e67..23eb3d154 100644 --- a/codes/go/pkg/tree_node.go +++ b/codes/go/pkg/tree_node.go @@ -23,11 +23,12 @@ func NewTreeNode(v int) *TreeNode { } // ArrToTree Generate a binary tree given an array -func ArrToTree(arr []int) *TreeNode { +func ArrToTree(arr []any) *TreeNode { if len(arr) <= 0 { return nil } - root := NewTreeNode(arr[0]) + // TreeNode only accept integer value for now. + root := NewTreeNode(arr[0].(int)) // Let container.list as queue queue := list.New() queue.PushBack(root) @@ -37,13 +38,17 @@ func ArrToTree(arr []int) *TreeNode { node := queue.Remove(queue.Front()).(*TreeNode) i++ if i < len(arr) { - node.Left = NewTreeNode(arr[i]) - queue.PushBack(node.Left) + if arr[i] != nil { + node.Left = NewTreeNode(arr[i].(int)) + queue.PushBack(node.Left) + } } i++ if i < len(arr) { - node.Right = NewTreeNode(arr[i]) - queue.PushBack(node.Right) + if arr[i] != nil { + node.Right = NewTreeNode(arr[i].(int)) + queue.PushBack(node.Right) + } } } return root diff --git a/codes/go/pkg/tree_node_test.go b/codes/go/pkg/tree_node_test.go index bb1885ee1..ec7831bf7 100644 --- a/codes/go/pkg/tree_node_test.go +++ b/codes/go/pkg/tree_node_test.go @@ -10,7 +10,7 @@ import ( ) func TestTreeNode(t *testing.T) { - arr := []int{2, 3, 5, 6, 7} + arr := []any{1, 2, 3, nil, 5, 6, nil} node := ArrToTree(arr) // print tree From 0243957015fb75675ae48ea1dcab8038aafb72e6 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Sun, 8 Jan 2023 20:29:13 +0800 Subject: [PATCH 129/158] doc(code): modify go code in docs --- docs/chapter_array_and_linkedlist/list.md | 25 +++--- .../space_complexity.md | 16 ++-- docs/chapter_hashing/hash_map.md | 24 +++--- docs/chapter_sorting/merge_sort.md | 19 +++-- docs/chapter_sorting/quick_sort.md | 10 +-- docs/chapter_stack_and_queue/queue.md | 78 +++++++++++-------- docs/chapter_stack_and_queue/stack.md | 78 +++++++++++-------- docs/chapter_tree/binary_search_tree.md | 10 +-- 8 files changed, 143 insertions(+), 117 deletions(-) diff --git a/docs/chapter_array_and_linkedlist/list.md b/docs/chapter_array_and_linkedlist/list.md index d1109d65b..ca4774a04 100644 --- a/docs/chapter_array_and_linkedlist/list.md +++ b/docs/chapter_array_and_linkedlist/list.md @@ -864,7 +864,7 @@ comments: true ```go title="my_list.go" /* 列表类简易实现 */ - type MyList struct { + type myList struct { numsCapacity int nums []int numsSize int @@ -872,8 +872,8 @@ comments: true } /* 构造函数 */ - func newMyList() *MyList { - return &MyList{ + func newMyList() *myList { + return &myList{ numsCapacity: 10, // 列表容量 nums: make([]int, 10), // 数组(存储列表元素) numsSize: 0, // 列表长度(即当前元素数量) @@ -882,17 +882,17 @@ comments: true } /* 获取列表长度(即当前元素数量) */ - func (l *MyList) size() int { + func (l *myList) size() int { return l.numsSize } /* 获取列表容量 */ - func (l *MyList) capacity() int { + func (l *myList) capacity() int { return l.numsCapacity } /* 访问元素 */ - func (l *MyList) get(index int) int { + func (l *myList) get(index int) int { // 索引如果越界则抛出异常,下同 if index >= l.numsSize { panic("索引越界") @@ -901,7 +901,7 @@ comments: true } /* 更新元素 */ - func (l *MyList) set(num, index int) { + func (l *myList) set(num, index int) { if index >= l.numsSize { panic("索引越界") } @@ -909,7 +909,7 @@ comments: true } /* 尾部添加元素 */ - func (l *MyList) add(num int) { + func (l *myList) add(num int) { // 元素数量超出容量时,触发扩容机制 if l.numsSize == l.numsCapacity { l.extendCapacity() @@ -920,7 +920,7 @@ comments: true } /* 中间插入元素 */ - func (l *MyList) insert(num, index int) { + func (l *myList) insert(num, index int) { if index >= l.numsSize { panic("索引越界") } @@ -938,20 +938,23 @@ comments: true } /* 删除元素 */ - func (l *MyList) Remove(index int) { + func (l *myList) remove(index int) int { if index >= l.numsSize { panic("索引越界") } + num := l.nums[index] // 索引 i 之后的元素都向前移动一位 for j := index; j < l.numsSize-1; j++ { l.nums[j] = l.nums[j+1] } // 更新元素数量 l.numsSize-- + // 返回被删除元素 + return num } /* 列表扩容 */ - func (l *MyList) extendCapacity() { + func (l *myList) extendCapacity() { // 新建一个长度为 self.__size 的数组,并将原数组拷贝到新数组 l.nums = append(l.nums, make([]int, l.numsCapacity*(l.extendRatio-1))...) // 更新列表容量 diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index 1756f2e24..f563f4953 100644 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -103,14 +103,14 @@ comments: true ```go title="" /* 结构体 */ - type Node struct { + type node struct { val int - next *Node + next *node } - - /* 创建 Node 结构体 */ - func newNode(val int) *Node { - return &Node{val: val} + + /* 创建 node 结构体 */ + func newNode(val int) *node { + return &node{val: val} } /* 函数 */ @@ -687,7 +687,7 @@ $$ // 长度为 n 的数组占用 O(n) 空间 _ = make([]int, n) // 长度为 n 的列表占用 O(n) 空间 - var nodes []*Node + var nodes []*node for i := 0; i < n; i++ { nodes = append(nodes, newNode(i)) } @@ -1108,7 +1108,7 @@ $$ ```go title="space_complexity.go" /* 指数阶(建立满二叉树) */ - func buildTree(n int) *TreeNode { + func buildTree(n int) *treeNode { if n == 0 { return nil } diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index 3a69d88fc..2d8a66a5d 100644 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -524,30 +524,30 @@ $$ ```go title="array_hash_map.go" /* 键值对 int->String */ - type Entry struct { + type entry struct { key int val string } /* 基于数组简易实现的哈希表 */ - type ArrayHashMap struct { - bucket []*Entry + type arrayHashMap struct { + bucket []*entry } - func newArrayHashMap() *ArrayHashMap { + func newArrayHashMap() *arrayHashMap { // 初始化一个长度为 100 的桶(数组) - bucket := make([]*Entry, 100) - return &ArrayHashMap{bucket: bucket} + bucket := make([]*entry, 100) + return &arrayHashMap{bucket: bucket} } /* 哈希函数 */ - func (a *ArrayHashMap) hashFunc(key int) int { + func (a *arrayHashMap) hashFunc(key int) int { index := key % 100 return index } /* 查询操作 */ - func (a *ArrayHashMap) get(key int) string { + func (a *arrayHashMap) get(key int) string { index := a.hashFunc(key) pair := a.bucket[index] if pair == nil { @@ -557,16 +557,16 @@ $$ } /* 添加操作 */ - func (a *ArrayHashMap) put(key int, val string) { - pair := &Entry{key: key, val: val} + func (a *arrayHashMap) put(key int, val string) { + pair := &entry{key: key, val: val} index := a.hashFunc(key) a.bucket[index] = pair } /* 删除操作 */ - func (a *ArrayHashMap) remove(key int) { + func (a *arrayHashMap) remove(key int) { index := a.hashFunc(key) - // 置为空字符,代表删除 + // 置为 nil ,代表删除 a.bucket[index] = nil } ``` diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index 396baa089..ae0cfc7c2 100644 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -201,36 +201,35 @@ comments: true 右子数组区间 [mid + 1, right] */ func merge(nums []int, left, mid, right int) { - // 初始化辅助数组 借助 copy模块 + // 初始化辅助数组 借助 copy 模块 tmp := make([]int, right-left+1) for i := left; i <= right; i++ { tmp[i-left] = nums[i] } // 左子数组的起始索引和结束索引 - left_start, left_end := left-left, mid-left + leftStart, leftEnd := left-left, mid-left // 右子数组的起始索引和结束索引 - right_start, right_end := mid+1-left, right-left + rightStart, rightEnd := mid+1-left, right-left // i, j 分别指向左子数组、右子数组的首元素 - i, j := left_start, right_start + i, j := leftStart, rightStart // 通过覆盖原数组 nums 来合并左子数组和右子数组 for k := left; k <= right; k++ { // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ - if i > left_end { + if i > leftEnd { nums[k] = tmp[j] j++ - // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ - } else if j > right_end || tmp[i] <= tmp[j] { + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + } else if j > rightEnd || tmp[i] <= tmp[j] { nums[k] = tmp[i] i++ - // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ } else { nums[k] = tmp[j] j++ } } } - - /* 归并排序 */ + func mergeSort(nums []int, left, right int) { // 终止条件 if left >= right { diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index f8dcc3238..3eeac9c65 100644 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -111,21 +111,21 @@ comments: true ```go title="quick_sort.go" /* 哨兵划分 */ func partition(nums []int, left, right int) int { - //以 nums[left] 作为基准数 + // 以 nums[left] 作为基准数 i, j := left, right for i < j { for i < j && nums[j] >= nums[left] { - j-- //从右向左找首个小于基准数的元素 + j-- // 从右向左找首个小于基准数的元素 } for i < j && nums[i] <= nums[left] { - i++ //从左向右找首个大于基准数的元素 + i++ // 从左向右找首个大于基准数的元素 } //元素交换 nums[i], nums[j] = nums[j], nums[i] } - //将基准数交换至两子数组的分界线 + // 将基准数交换至两子数组的分界线 nums[i], nums[left] = nums[left], nums[i] - return i //返回基准数的索引 + return i // 返回基准数的索引 } ``` diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index ce47edb38..0c589fa69 100644 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -404,43 +404,49 @@ comments: true ```go title="linkedlist_queue.go" /* 基于链表实现的队列 */ - type LinkedListQueue struct { + type linkedListQueue struct { // 使用内置包 list 来实现队列 data *list.List } - // NewLinkedListQueue 初始化链表 - func NewLinkedListQueue() *LinkedListQueue { - return &LinkedListQueue{ + + // newLinkedListQueue 初始化链表 + func newLinkedListQueue() *linkedListQueue { + return &linkedListQueue{ data: list.New(), } } - // Offer 入队 - func (s *LinkedListQueue) Offer(value any) { + + // offer 入队 + func (s *linkedListQueue) offer(value any) { s.data.PushBack(value) } - // Poll 出队 - func (s *LinkedListQueue) Poll() any { - if s.IsEmpty() { + + // poll 出队 + func (s *linkedListQueue) poll() any { + if s.isEmpty() { return nil } e := s.data.Front() s.data.Remove(e) return e.Value } - // Peek 访问队首元素 - func (s *LinkedListQueue) Peek() any { - if s.IsEmpty() { + + // peek 访问队首元素 + func (s *linkedListQueue) peek() any { + if s.isEmpty() { return nil } e := s.data.Front() return e.Value } - // Size 获取队列的长度 - func (s *LinkedListQueue) Size() int { + + // size 获取队列的长度 + func (s *linkedListQueue) size() int { return s.data.Len() } - // IsEmpty 判断队列是否为空 - func (s *LinkedListQueue) IsEmpty() bool { + + // isEmpty 判断队列是否为空 + func (s *linkedListQueue) isEmpty() bool { return s.data.Len() == 0 } ``` @@ -805,34 +811,38 @@ comments: true ```go title="array_queue.go" /* 基于环形数组实现的队列 */ - type ArrayQueue struct { + type arrayQueue struct { data []int // 用于存储队列元素的数组 capacity int // 队列容量(即最多容量的元素个数) front int // 头指针,指向队首 rear int // 尾指针,指向队尾 + 1 } - // NewArrayQueue 基于环形数组实现的队列 - func NewArrayQueue(capacity int) *ArrayQueue { - return &ArrayQueue{ + + // newArrayQueue 基于环形数组实现的队列 + func newArrayQueue(capacity int) *arrayQueue { + return &arrayQueue{ data: make([]int, capacity), capacity: capacity, front: 0, rear: 0, } } - // Size 获取队列的长度 - func (q *ArrayQueue) Size() int { + + // size 获取队列的长度 + func (q *arrayQueue) size() int { size := (q.capacity + q.rear - q.front) % q.capacity return size } - // IsEmpty 判断队列是否为空 - func (q *ArrayQueue) IsEmpty() bool { + + // isEmpty 判断队列是否为空 + func (q *arrayQueue) isEmpty() bool { return q.rear-q.front == 0 } - // Offer 入队 - func (q *ArrayQueue) Offer(v int) { + + // offer 入队 + func (q *arrayQueue) offer(v int) { // 当 rear == capacity 表示队列已满 - if q.Size() == q.capacity { + if q.size() == q.capacity { return } // 尾结点后添加 @@ -840,9 +850,10 @@ comments: true // 尾指针向后移动一位,越过尾部后返回到数组头部 q.rear = (q.rear + 1) % q.capacity } - // Poll 出队 - func (q *ArrayQueue) Poll() any { - if q.IsEmpty() { + + // poll 出队 + func (q *arrayQueue) poll() any { + if q.isEmpty() { return nil } v := q.data[q.front] @@ -850,9 +861,10 @@ comments: true q.front = (q.front + 1) % q.capacity return v } - // Peek 访问队首元素 - func (q *ArrayQueue) Peek() any { - if q.IsEmpty() { + + // peek 访问队首元素 + func (q *arrayQueue) peek() any { + if q.isEmpty() { return nil } v := q.data[q.front] diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index 374de7424..8bb343c4f 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -378,43 +378,49 @@ comments: true ```go title="linkedlist_stack.go" /* 基于链表实现的栈 */ - type LinkedListStack struct { + type linkedListStack struct { // 使用内置包 list 来实现栈 data *list.List } - // NewLinkedListStack 初始化链表 - func NewLinkedListStack() *LinkedListStack { - return &LinkedListStack{ + + // newLinkedListStack 初始化链表 + func newLinkedListStack() *linkedListStack { + return &linkedListStack{ data: list.New(), } } - // Push 入栈 - func (s *LinkedListStack) Push(value int) { + + // push 入栈 + func (s *linkedListStack) push(value int) { s.data.PushBack(value) } - // Pop 出栈 - func (s *LinkedListStack) Pop() any { - if s.IsEmpty() { + + // pop 出栈 + func (s *linkedListStack) pop() any { + if s.isEmpty() { return nil } e := s.data.Back() s.data.Remove(e) return e.Value } - // Peek 访问栈顶元素 - func (s *LinkedListStack) Peek() any { - if s.IsEmpty() { + + // peek 访问栈顶元素 + func (s *linkedListStack) peek() any { + if s.isEmpty() { return nil } e := s.data.Back() return e.Value } - // Size 获取栈的长度 - func (s *LinkedListStack) Size() int { + + // size 获取栈的长度 + func (s *linkedListStack) size() int { return s.data.Len() } - // IsEmpty 判断栈是否为空 - func (s *LinkedListStack) IsEmpty() bool { + + // isEmpty 判断栈是否为空 + func (s *linkedListStack) isEmpty() bool { return s.data.Len() == 0 } ``` @@ -716,41 +722,47 @@ comments: true ```go title="array_stack.go" /* 基于数组实现的栈 */ - type ArrayStack struct { + type arrayStack struct { data []int // 数据 } - func NewArrayStack() *ArrayStack { - return &ArrayStack{ + + func newArrayStack() *arrayStack { + return &arrayStack{ // 设置栈的长度为 0,容量为 16 data: make([]int, 0, 16), } } - // Size 栈的长度 - func (s *ArrayStack) Size() int { + + // size 栈的长度 + func (s *arrayStack) size() int { return len(s.data) } - // IsEmpty 栈是否为空 - func (s *ArrayStack) IsEmpty() bool { - return s.Size() == 0 + + // isEmpty 栈是否为空 + func (s *arrayStack) isEmpty() bool { + return s.size() == 0 } - // Push 入栈 - func (s *ArrayStack) Push(v int) { + + // push 入栈 + func (s *arrayStack) push(v int) { // 切片会自动扩容 s.data = append(s.data, v) } - // Pop 出栈 - func (s *ArrayStack) Pop() any { + + // pop 出栈 + func (s *arrayStack) pop() any { // 弹出栈前,先判断是否为空 - if s.IsEmpty() { + if s.isEmpty() { return nil } - val := s.Peek() + val := s.peek() s.data = s.data[:len(s.data)-1] return val } - // Peek 获取栈顶元素 - func (s *ArrayStack) Peek() any { - if s.IsEmpty() { + + // peek 获取栈顶元素 + func (s *arrayStack) peek() any { + if s.isEmpty() { return nil } val := s.data[len(s.data)-1] diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index 979612852..e3ddfaaac 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -103,7 +103,7 @@ comments: true ```go title="binary_search_tree.go" /* 查找结点 */ - func (bst *BinarySearchTree) Search(num int) *TreeNode { + func (bst *binarySearchTree) search(num int) *TreeNode { node := bst.root // 循环查找,越过叶结点后跳出 for node != nil { @@ -299,7 +299,7 @@ comments: true ```go title="binary_search_tree.go" /* 插入结点 */ - func (bst *BinarySearchTree) Insert(num int) *TreeNode { + func (bst *binarySearchTree) insert(num int) *TreeNode { cur := bst.root // 若树为空,直接提前返回 if cur == nil { @@ -609,7 +609,7 @@ comments: true ```go title="binary_search_tree.go" /* 删除结点 */ - func (bst *BinarySearchTree) Remove(num int) *TreeNode { + func (bst *binarySearchTree) remove(num int) *TreeNode { cur := bst.root // 若树为空,直接提前返回 if cur == nil { @@ -653,10 +653,10 @@ comments: true // 子结点数为 2 } else { // 获取中序遍历中待删除结点 cur 的下一个结点 - next := bst.GetInOrderNext(cur) + next := bst.getInOrderNext(cur) temp := next.Val // 递归删除结点 next - bst.Remove(next.Val) + bst.remove(next.Val) // 将 next 的值复制给 cur cur.Val = temp } From b6abf2b09280d746f261e5508356e369acc189c0 Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Sun, 8 Jan 2023 20:38:48 +0800 Subject: [PATCH 130/158] (PR #217)update a .gitignore file in the codes/zig dir --- codes/zig/.gitignore | 2 + codes/zig/build.zig | 46 ++++++++++++++ .../leetcode_two_sum.zig | 61 +++++++++++++++++++ .../worst_best_time_complexity.zig | 9 ++- codes/zig/include/PrintUtil.zig | 13 ++++ codes/zig/include/include.zig | 5 ++ 6 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 codes/zig/.gitignore create mode 100644 codes/zig/build.zig create mode 100644 codes/zig/chapter_computational_complexity/leetcode_two_sum.zig create mode 100644 codes/zig/include/PrintUtil.zig create mode 100644 codes/zig/include/include.zig diff --git a/codes/zig/.gitignore b/codes/zig/.gitignore new file mode 100644 index 000000000..4a0641ed7 --- /dev/null +++ b/codes/zig/.gitignore @@ -0,0 +1,2 @@ +zig-cache/ +zig-out/ \ No newline at end of file diff --git a/codes/zig/build.zig b/codes/zig/build.zig new file mode 100644 index 000000000..b04b3fd18 --- /dev/null +++ b/codes/zig/build.zig @@ -0,0 +1,46 @@ +const std = @import("std"); + +// zig version 0.10.0 +pub fn build(b: *std.build.Builder) void { + const target = b.standardTargetOptions(.{}); + const mode = b.standardReleaseOptions(); + + // "chapter_computational_complexity/time_complexity.zig" + // Run Command: zig build run_time_complexity + const exe_time_complexity = b.addExecutable("time_complexity", "chapter_computational_complexity/time_complexity.zig"); + exe_time_complexity.addPackagePath("include", "include/include.zig"); + exe_time_complexity.setTarget(target); + exe_time_complexity.setBuildMode(mode); + exe_time_complexity.install(); + const run_cmd_time_complexity = exe_time_complexity.run(); + run_cmd_time_complexity.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_time_complexity.addArgs(args); + const run_step_time_complexity = b.step("run_time_complexity", "Run time_complexity"); + run_step_time_complexity.dependOn(&run_cmd_time_complexity.step); + + // "chapter_computational_complexity/worst_best_time_complexity.zig" + // Run Command: zig build run_worst_best_time_complexity + const exe_worst_best_time_complexity = b.addExecutable("worst_best_time_complexity", "chapter_computational_complexity/worst_best_time_complexity.zig"); + exe_worst_best_time_complexity.addPackagePath("include", "include/include.zig"); + exe_worst_best_time_complexity.setTarget(target); + exe_worst_best_time_complexity.setBuildMode(mode); + exe_worst_best_time_complexity.install(); + const run_cmd_worst_best_time_complexity = exe_worst_best_time_complexity.run(); + run_cmd_worst_best_time_complexity.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_worst_best_time_complexity.addArgs(args); + const run_step_worst_best_time_complexity = b.step("run_worst_best_time_complexity", "Run worst_best_time_complexity"); + run_step_worst_best_time_complexity.dependOn(&run_cmd_worst_best_time_complexity.step); + + // "chapter_computational_complexity/leetcode_two_sum.zig" + // Run Command: zig build run_leetcode_two_sum + const exe_leetcode_two_sum = b.addExecutable("leetcode_two_sum", "chapter_computational_complexity/leetcode_two_sum.zig"); + exe_leetcode_two_sum.addPackagePath("include", "include/include.zig"); + exe_leetcode_two_sum.setTarget(target); + exe_leetcode_two_sum.setBuildMode(mode); + exe_leetcode_two_sum.install(); + const run_cmd_leetcode_two_sum = exe_leetcode_two_sum.run(); + run_cmd_leetcode_two_sum.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_leetcode_two_sum.addArgs(args); + const run_step_leetcode_two_sum = b.step("run_leetcode_two_sum", "Run leetcode_two_sum"); + run_step_leetcode_two_sum.dependOn(&run_cmd_leetcode_two_sum.step); +} diff --git a/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig b/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig new file mode 100644 index 000000000..66b96f95e --- /dev/null +++ b/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig @@ -0,0 +1,61 @@ +// File: leetcode_two_sum.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +const SolutionBruteForce = struct { + pub fn twoSum(self: *SolutionBruteForce, nums: []i32, target: i32) [2]i32 { + _ = self; + var size: usize = nums.len; + var i: usize = 0; + // 两层循环,时间复杂度 O(n^2) + while (i < size - 1) : (i += 1) { + var j = i + 1; + while (j < size) : (j += 1) { + if (nums[i] + nums[j] == target) { + return [_]i32{@intCast(i32, i), @intCast(i32, j)}; + } + } + } + return undefined; + } +}; + +const SolutionHashMap = struct { + pub fn twoSum(self: *SolutionHashMap, nums: []i32, target: i32) ![2]i32 { + _ = self; + var size: usize = nums.len; + // 辅助哈希表,空间复杂度 O(n) + var dic = std.AutoHashMap(i32, i32).init(std.heap.page_allocator); + defer dic.deinit(); + var i: usize = 0; + // 单层循环,时间复杂度 O(n) + while (i < size) : (i += 1) { + if (dic.contains(target - nums[i])) { + return [_]i32{dic.get(target - nums[i]).?, @intCast(i32, i)}; + } + try dic.put(nums[i], @intCast(i32, i)); + } + return undefined; + } +}; + +// Driver Code +pub fn main() !void { + // ======= Test Case ======= + var nums = [_]i32{ 2, 7, 11, 15 }; + var target: i32 = 9; + // 方法一 + var slt1 = SolutionBruteForce{}; + var res = slt1.twoSum(&nums, target); + std.debug.print("方法一 res = ", .{}); + inc.PrintUtil.printArray(i32, &res); + // 方法二 + var slt2 = SolutionHashMap{}; + res = try slt2.twoSum(&nums, target); + std.debug.print("方法二 res = ", .{}); + inc.PrintUtil.printArray(i32, &res); +} + diff --git a/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig b/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig index dacf45952..bd2a1464a 100644 --- a/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig +++ b/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig @@ -3,6 +3,7 @@ // Author: sjinzh (sjinzh@gmail.com) const std = @import("std"); +const inc = @import("include"); // 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 pub fn randomNumbers(comptime n: usize) [n]i32 { @@ -26,17 +27,15 @@ pub fn findOne(nums: []i32) i32 { } // Driver Code -pub fn main() !void { +pub fn main() void { var i: i32 = 0; while (i < 10) : (i += 1) { const n: usize = 100; var nums = randomNumbers(n); var index = findOne(&nums); std.debug.print("\n数组 [ 1, 2, ..., n ] 被打乱后 = ", .{}); - for (nums) |num, j| { - std.debug.print("{}{s}", .{num, if (j == nums.len-1) "" else "," }); - } - std.debug.print("\n数字 1 的索引为 {}\n", .{index}); + inc.PrintUtil.printArray(i32, &nums); + std.debug.print("数字 1 的索引为 {}\n", .{index}); } } diff --git a/codes/zig/include/PrintUtil.zig b/codes/zig/include/PrintUtil.zig new file mode 100644 index 000000000..5adac2a6e --- /dev/null +++ b/codes/zig/include/PrintUtil.zig @@ -0,0 +1,13 @@ +// File: TreeNode.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); + +// Print an Array +pub fn printArray(comptime T: type, nums: []T) void { + std.debug.print("[", .{}); + for (nums) |num, j| { + std.debug.print("{}{s}", .{num, if (j == nums.len-1) "]\n" else ", " }); + } +} diff --git a/codes/zig/include/include.zig b/codes/zig/include/include.zig new file mode 100644 index 000000000..b4f347821 --- /dev/null +++ b/codes/zig/include/include.zig @@ -0,0 +1,5 @@ +// File: include.zig +// Created Time: 2023-01-04 +// Author: sjinzh (sjinzh@gmail.com) + +pub const PrintUtil = @import("PrintUtil.zig"); \ No newline at end of file From 3b52df2a8f2725f88db56c4696ea9a3ef48b1663 Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Sun, 8 Jan 2023 20:53:24 +0800 Subject: [PATCH 131/158] style: update comment format --- .../space_complexity.swift | 16 +++++------ .../time_complexity.swift | 23 +++++++-------- .../worst_best_time_complexity.swift | 6 ++-- docs/chapter_array_and_linkedlist/array.md | 14 +++++----- .../space_complexity.md | 20 ++++++------- .../time_complexity.md | 28 +++++++++---------- .../chapter_data_structure/data_and_memory.md | 2 +- 7 files changed, 55 insertions(+), 54 deletions(-) diff --git a/codes/swift/chapter_computational_complexity/space_complexity.swift b/codes/swift/chapter_computational_complexity/space_complexity.swift index 92a1187a0..fdd29f8e2 100644 --- a/codes/swift/chapter_computational_complexity/space_complexity.swift +++ b/codes/swift/chapter_computational_complexity/space_complexity.swift @@ -6,14 +6,14 @@ import utils -// 函数 +/* 函数 */ @discardableResult func function() -> Int { // do something return 0 } -// 常数阶 +/* 常数阶 */ func constant(n: Int) { // 常量、变量、对象占用 O(1) 空间 let a = 0 @@ -30,7 +30,7 @@ func constant(n: Int) { } } -// 线性阶 +/* 线性阶 */ func linear(n: Int) { // 长度为 n 的数组占用 O(n) 空间 let nums = Array(repeating: 0, count: n) @@ -40,7 +40,7 @@ func linear(n: Int) { let map = Dictionary(uniqueKeysWithValues: (0 ..< n).map { ($0, "\($0)") }) } -// 线性阶(递归实现) +/* 线性阶(递归实现) */ func linearRecur(n: Int) { print("递归 n = \(n)") if n == 1 { @@ -49,13 +49,13 @@ func linearRecur(n: Int) { linearRecur(n: n - 1) } -// 平方阶 +/* 平方阶 */ func quadratic(n: Int) { // 二维列表占用 O(n^2) 空间 let numList = Array(repeating: Array(repeating: 0, count: n), count: n) } -// 平方阶(递归实现) +/* 平方阶(递归实现) */ @discardableResult func quadraticRecur(n: Int) -> Int { if n <= 0 { @@ -67,7 +67,7 @@ func quadraticRecur(n: Int) -> Int { return quadraticRecur(n: n - 1) } -// 指数阶(建立满二叉树) +/* 指数阶(建立满二叉树) */ func buildTree(n: Int) -> TreeNode? { if n == 0 { return nil @@ -80,7 +80,7 @@ func buildTree(n: Int) -> TreeNode? { @main enum SpaceComplexity { - // Driver Code + /* Driver Code */ static func main() { let n = 5 // 常数阶 diff --git a/codes/swift/chapter_computational_complexity/time_complexity.swift b/codes/swift/chapter_computational_complexity/time_complexity.swift index 44addb800..a0e0a9cff 100644 --- a/codes/swift/chapter_computational_complexity/time_complexity.swift +++ b/codes/swift/chapter_computational_complexity/time_complexity.swift @@ -4,7 +4,7 @@ * Author: nuomi1 (nuomi1@qq.com) */ -// 常数阶 +/* 常数阶 */ func constant(n: Int) -> Int { var count = 0 let size = 100_000 @@ -14,7 +14,7 @@ func constant(n: Int) -> Int { return count } -// 线性阶 +/* 线性阶 */ func linear(n: Int) -> Int { var count = 0 for _ in 0 ..< n { @@ -23,7 +23,7 @@ func linear(n: Int) -> Int { return count } -// 线性阶(遍历数组) +/* 线性阶(遍历数组) */ func arrayTraversal(nums: [Int]) -> Int { var count = 0 // 循环次数与数组长度成正比 @@ -33,7 +33,7 @@ func arrayTraversal(nums: [Int]) -> Int { return count } -// 平方阶 +/* 平方阶 */ func quadratic(n: Int) -> Int { var count = 0 // 循环次数与数组长度成平方关系 @@ -45,7 +45,7 @@ func quadratic(n: Int) -> Int { return count } -// 平方阶(冒泡排序) +/* 平方阶(冒泡排序) */ func bubbleSort(nums: inout [Int]) -> Int { var count = 0 // 计数器 // 外循环:待排序元素数量为 n-1, n-2, ..., 1 @@ -64,7 +64,7 @@ func bubbleSort(nums: inout [Int]) -> Int { return count } -// 指数阶(循环实现) +/* 指数阶(循环实现) */ func exponential(n: Int) -> Int { var count = 0 var base = 1 @@ -79,7 +79,7 @@ func exponential(n: Int) -> Int { return count } -// 指数阶(递归实现) +/* 指数阶(递归实现) */ func expRecur(n: Int) -> Int { if n == 1 { return 1 @@ -87,7 +87,7 @@ func expRecur(n: Int) -> Int { return expRecur(n: n - 1) + expRecur(n: n - 1) + 1 } -// 对数阶(循环实现) +/* 对数阶(循环实现) */ func logarithmic(n: Int) -> Int { var count = 0 var n = n @@ -98,7 +98,7 @@ func logarithmic(n: Int) -> Int { return count } -// 对数阶(递归实现) +/* 对数阶(递归实现) */ func logRecur(n: Int) -> Int { if n <= 1 { return 0 @@ -106,7 +106,7 @@ func logRecur(n: Int) -> Int { return logRecur(n: n / 2) + 1 } -// 线性对数阶 +/* 线性对数阶 */ func linearLogRecur(n: Double) -> Int { if n <= 1 { return 1 @@ -118,7 +118,7 @@ func linearLogRecur(n: Double) -> Int { return count } -// 阶乘阶(递归实现) +/* 阶乘阶(递归实现) */ func factorialRecur(n: Int) -> Int { if n == 0 { return 1 @@ -133,6 +133,7 @@ func factorialRecur(n: Int) -> Int { @main enum TimeComplexity { + /* Driver Code */ static func main() { // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 let n = 8 diff --git a/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift b/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift index 09d7de1f4..34f0518f1 100644 --- a/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift +++ b/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift @@ -4,7 +4,7 @@ * Author: nuomi1 (nuomi1@qq.com) */ -// 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 +/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ func randomNumbers(n: Int) -> [Int] { // 生成数组 nums = { 1, 2, 3, ..., n } var nums = Array(1 ... n) @@ -13,7 +13,7 @@ func randomNumbers(n: Int) -> [Int] { return nums } -// 查找数组 nums 中数字 1 所在索引 +/* 查找数组 nums 中数字 1 所在索引 */ func findOne(nums: [Int]) -> Int { for i in nums.indices { if nums[i] == 1 { @@ -25,7 +25,7 @@ func findOne(nums: [Int]) -> Int { @main enum WorstBestTimeComplexity { - // Driver Code + /* Driver Code */ static func main() { for _ in 0 ..< 10 { let n = 100 diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 461cdcf37..2cf096137 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -84,7 +84,7 @@ comments: true === "Swift" ```swift title="array.swift" - // 初始化数组 + /* 初始化数组 */ let arr = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0] let nums = [1, 3, 2, 5, 4] ``` @@ -204,7 +204,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Swift" ```swift title="array.swift" - // 随机返回一个数组元素 + /* 随机返回一个数组元素 */ func randomAccess(nums: [Int]) -> Int { // 在区间 [0, nums.count) 中随机抽取一个数字 let randomIndex = nums.indices.randomElement()! @@ -341,7 +341,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Swift" ```swift title="array.swift" - // 扩展数组长度 + /* 扩展数组长度 */ func extend(nums: [Int], enlarge: Int) -> [Int] { // 初始化一个扩展长度后的数组 var res = Array(repeating: 0, count: nums.count + enlarge) @@ -526,7 +526,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Swift" ```swift title="array.swift" - // 在数组的索引 index 处插入元素 num + /* 在数组的索引 index 处插入元素 num */ func insert(nums: inout [Int], num: Int, index: Int) { // 把索引 index 以及之后的所有元素向后移动一位 for i in sequence(first: nums.count - 1, next: { $0 > index + 1 ? $0 - 1 : nil }) { @@ -536,7 +536,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex nums[index] = num } - // 删除索引 index 处元素 + /* 删除索引 index 处元素 */ func remove(nums: inout [Int], index: Int) { let count = nums.count // 把索引 index 之后的所有元素向前移动一位 @@ -674,7 +674,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Swift" ```swift title="array.swift" - // 遍历数组 + /* 遍历数组 */ func traverse(nums: [Int]) { var count = 0 // 通过索引遍历数组 @@ -793,7 +793,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex === "Swift" ```swift title="array.swift" - // 在数组中查找指定元素 + /* 在数组中查找指定元素 */ func find(nums: [Int], target: Int) -> Int { for i in nums.indices { if nums[i] == target { diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index 1756f2e24..939b61ecf 100644 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -177,7 +177,7 @@ comments: true === "Swift" ```swift title="" - // 类 + /* 类 */ class Node { var val: Int var next: Node? @@ -187,7 +187,7 @@ comments: true } } - // 函数 + /* 函数 */ func function() -> Int { // do something... return 0 @@ -436,14 +436,14 @@ comments: true return 0 } - // 循环 O(1) + /* 循环 O(1) */ func loop(n: Int) { for _ in 0 ..< n { function() } } - // 递归 O(n) + /* 递归 O(n) */ func recur(n: Int) { if n == 1 { return @@ -604,7 +604,7 @@ $$ === "Swift" ```swift title="space_complexity.swift" - // 常数阶 + /* 常数阶 */ func constant(n: Int) { // 常量、变量、对象占用 O(1) 空间 let a = 0 @@ -743,7 +743,7 @@ $$ === "Swift" ```swift title="space_complexity.swift" - // 线性阶 + /* 线性阶 */ func linear(n: Int) { // 长度为 n 的数组占用 O(n) 空间 let nums = Array(repeating: 0, count: n) @@ -834,7 +834,7 @@ $$ === "Swift" ```swift title="space_complexity.swift" - // 线性阶(递归实现) + /* 线性阶(递归实现) */ func linearRecur(n: Int) { print("递归 n = \(n)") if n == 1 { @@ -954,7 +954,7 @@ $$ === "Swift" ```swift title="space_complexity.swift" - // 平方阶 + /* 平方阶 */ func quadratic(n: Int) { // 二维列表占用 O(n^2) 空间 let numList = Array(repeating: Array(repeating: 0, count: n), count: n) @@ -1047,7 +1047,7 @@ $$ === "Swift" ```swift title="space_complexity.swift" - // 平方阶(递归实现) + /* 平方阶(递归实现) */ func quadraticRecur(n: Int) -> Int { if n <= 0 { return 0 @@ -1154,7 +1154,7 @@ $$ === "Swift" ```swift title="space_complexity.swift" - // 指数阶(建立满二叉树) + /* 指数阶(建立满二叉树) */ func buildTree(n: Int) -> TreeNode? { if n == 0 { return nil diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 7b0e66fbd..f6b6e9356 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -876,7 +876,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 常数阶 + /* 常数阶 */ func constant(n: Int) -> Int { var count = 0 let size = 100000 @@ -990,7 +990,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 线性阶 + /* 线性阶 */ func linear(n: Int) -> Int { var count = 0 for _ in 0 ..< n { @@ -1121,7 +1121,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 线性阶(遍历数组) + /* 线性阶(遍历数组) */ func arrayTraversal(nums: [Int]) -> Int { var count = 0 // 循环次数与数组长度成正比 @@ -1267,7 +1267,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 平方阶 + /* 平方阶 */ func quadratic(n: Int) -> Int { var count = 0 // 循环次数与数组长度成平方关系 @@ -1477,7 +1477,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 平方阶(冒泡排序) + /* 平方阶(冒泡排序) */ func bubbleSort(nums: inout [Int]) -> Int { var count = 0 // 计数器 // 外循环:待排序元素数量为 n-1, n-2, ..., 1 @@ -1656,7 +1656,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 指数阶(循环实现) + /* 指数阶(循环实现) */ func exponential(n: Int) -> Int { var count = 0 var base = 1 @@ -1764,7 +1764,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 指数阶(递归实现) + /* 指数阶(递归实现) */ func expRecur(n: Int) -> Int { if n == 1 { return 1 @@ -1896,7 +1896,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 对数阶(循环实现) + /* 对数阶(循环实现) */ func logarithmic(n: Int) -> Int { var count = 0 var n = n @@ -1999,7 +1999,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 对数阶(递归实现) + /* 对数阶(递归实现) */ func logRecur(n: Int) -> Int { if n <= 1 { return 0 @@ -2137,7 +2137,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 线性对数阶 + /* 线性对数阶 */ func linearLogRecur(n: Double) -> Int { if n <= 1 { return 1 @@ -2288,7 +2288,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 阶乘阶(递归实现) + /* 阶乘阶(递归实现) */ func factorialRecur(n: Int) -> Int { if n == 0 { return 1 @@ -2658,7 +2658,7 @@ $$ === "Swift" ```swift title="worst_best_time_complexity.swift" - // 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 + /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ func randomNumbers(n: Int) -> [Int] { // 生成数组 nums = { 1, 2, 3, ..., n } var nums = Array(1 ... n) @@ -2667,7 +2667,7 @@ $$ return nums } - // 查找数组 nums 中数字 1 所在索引 + /* 查找数组 nums 中数字 1 所在索引 */ func findOne(nums: [Int]) -> Int { for i in nums.indices { if nums[i] == 1 { @@ -2677,7 +2677,7 @@ $$ return -1 } - // Driver Code + /* Driver Code */ func main() { for _ in 0 ..< 10 { let n = 100 diff --git a/docs/chapter_data_structure/data_and_memory.md b/docs/chapter_data_structure/data_and_memory.md index 3a11fdf36..59cb74e45 100644 --- a/docs/chapter_data_structure/data_and_memory.md +++ b/docs/chapter_data_structure/data_and_memory.md @@ -117,7 +117,7 @@ comments: true === "Swift" ```swift title="" - // 使用多种「基本数据类型」来初始化「数组」 + /* 使用多种「基本数据类型」来初始化「数组」 */ let numbers = Array(repeating: Int(), count: 5) let decimals = Array(repeating: Double(), count: 5) let characters = Array(repeating: Character("a"), count: 5) From f3e9c2cf896fbfe5ab794a6b53e2351457f9f5bd Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Sun, 8 Jan 2023 20:54:40 +0800 Subject: [PATCH 132/158] style: use string interpolation in print --- .../time_complexity.swift | 24 +++++++++---------- .../worst_best_time_complexity.swift | 4 ++-- .../time_complexity.md | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/codes/swift/chapter_computational_complexity/time_complexity.swift b/codes/swift/chapter_computational_complexity/time_complexity.swift index a0e0a9cff..41695cfd6 100644 --- a/codes/swift/chapter_computational_complexity/time_complexity.swift +++ b/codes/swift/chapter_computational_complexity/time_complexity.swift @@ -137,36 +137,36 @@ enum TimeComplexity { static func main() { // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 let n = 8 - print("输入数据大小 n =", n) + print("输入数据大小 n = \(n)") var count = constant(n: n) - print("常数阶的计算操作数量 =", count) + print("常数阶的计算操作数量 = \(count)") count = linear(n: n) - print("线性阶的计算操作数量 =", count) + print("线性阶的计算操作数量 = \(count)") count = arrayTraversal(nums: Array(repeating: 0, count: n)) - print("线性阶(遍历数组)的计算操作数量 =", count) + print("线性阶(遍历数组)的计算操作数量 = \(count)") count = quadratic(n: n) - print("平方阶的计算操作数量 =", count) + print("平方阶的计算操作数量 = \(count)") var nums = Array(sequence(first: n, next: { $0 > 0 ? $0 - 1 : nil })) // [n,n-1,...,2,1] count = bubbleSort(nums: &nums) - print("平方阶(冒泡排序)的计算操作数量 =", count) + print("平方阶(冒泡排序)的计算操作数量 = \(count)") count = exponential(n: n) - print("指数阶(循环实现)的计算操作数量 =", count) + print("指数阶(循环实现)的计算操作数量 = \(count)") count = expRecur(n: n) - print("指数阶(递归实现)的计算操作数量 =", count) + print("指数阶(递归实现)的计算操作数量 = \(count)") count = logarithmic(n: n) - print("对数阶(循环实现)的计算操作数量 =", count) + print("对数阶(循环实现)的计算操作数量 = \(count)") count = logRecur(n: n) - print("对数阶(递归实现)的计算操作数量 =", count) + print("对数阶(递归实现)的计算操作数量 = \(count)") count = linearLogRecur(n: Double(n)) - print("线性对数阶(递归实现)的计算操作数量 =", count) + print("线性对数阶(递归实现)的计算操作数量 = \(count)") count = factorialRecur(n: n) - print("阶乘阶(递归实现)的计算操作数量 =", count) + print("阶乘阶(递归实现)的计算操作数量 = \(count)") } } diff --git a/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift b/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift index 34f0518f1..3dac661d2 100644 --- a/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift +++ b/codes/swift/chapter_computational_complexity/worst_best_time_complexity.swift @@ -31,8 +31,8 @@ enum WorstBestTimeComplexity { let n = 100 let nums = randomNumbers(n: n) let index = findOne(nums: nums) - print("数组 [ 1, 2, ..., n ] 被打乱后 =", nums) - print("数字 1 的索引为", index) + print("数组 [ 1, 2, ..., n ] 被打乱后 = \(nums)") + print("数字 1 的索引为 \(index)") } } } diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index f6b6e9356..25d9fce88 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -2683,8 +2683,8 @@ $$ let n = 100 let nums = randomNumbers(n: n) let index = findOne(nums: nums) - print("数组 [ 1, 2, ..., n ] 被打乱后 =", nums) - print("数字 1 的索引为", index) + print("数组 [ 1, 2, ..., n ] 被打乱后 = \(nums)") + print("数字 1 的索引为 \(index)") } } ``` From cb0071924e98bbf593d172d684acc54a492749c8 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Mon, 9 Jan 2023 01:05:21 +0800 Subject: [PATCH 133/158] feat(tree/avl_tree): add go code --- codes/go/chapter_tree/avl_tree.go | 212 +++++++++++++++++++++++++ codes/go/chapter_tree/avl_tree_test.go | 54 +++++++ codes/go/pkg/tree_node.go | 14 +- 3 files changed, 274 insertions(+), 6 deletions(-) create mode 100644 codes/go/chapter_tree/avl_tree.go create mode 100644 codes/go/chapter_tree/avl_tree_test.go diff --git a/codes/go/chapter_tree/avl_tree.go b/codes/go/chapter_tree/avl_tree.go new file mode 100644 index 000000000..111abd722 --- /dev/null +++ b/codes/go/chapter_tree/avl_tree.go @@ -0,0 +1,212 @@ +// File: avl_tree.go +// Created Time: 2023-01-08 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import . "github.com/krahets/hello-algo/pkg" + +/* AVL Tree*/ +type avlTree struct { + // 根节点 + root *TreeNode +} + +func newAVLTree() *avlTree { + return &avlTree{root: nil} +} + +/* 获取结点高度 + */ +func height(node *TreeNode) int { + // 空结点高度为 -1 ,叶结点高度为 0 + if node != nil { + return node.Height + } + return -1 +} + +/* 更新结点高度 */ +func updateHeight(node *TreeNode) { + lh := height(node.Left) + rh := height(node.Right) + // 结点高度等于最高子树高度 + 1 + if lh > rh { + node.Height = lh + 1 + } else { + node.Height = rh + 1 + } +} + +/* 获取平衡因子 */ +func balanceFactor(node *TreeNode) int { + // 空结点平衡因子为 0 + if node == nil { + return 0 + } + // 结点平衡因子 = 左子树高度 - 右子树高度 + return height(node.Left) - height(node.Right) +} + +/* 右旋操作 */ +func rightRotate(node *TreeNode) *TreeNode { + child := node.Left + grandChild := child.Right + // 以 child 为原点,将 node 向右旋转 + child.Right = node + node.Left = grandChild + // 更新结点高度 + updateHeight(node) + updateHeight(child) + // 返回旋转后子树的根节点 + return child +} + +/* 左旋操作 */ +func leftRotate(node *TreeNode) *TreeNode { + child := node.Right + grandChild := child.Left + // 以 child 为原点,将 node 向左旋转 + child.Left = node + node.Right = grandChild + // 更新结点高度 + updateHeight(node) + updateHeight(child) + // 返回旋转后子树的根节点 + return child +} + +/* 执行旋转操作,使该子树重新恢复平衡 */ +func rotate(node *TreeNode) *TreeNode { + // 获取结点 node 的平衡因子 + // Go 推荐短变量,这里 bf 指代 balanceFactor + bf := balanceFactor(node) + // 左偏树 + if bf > 1 { + if balanceFactor(node.Left) >= 0 { + // 右旋 + return rightRotate(node) + } else { + // 先左旋后右旋 + node.Left = leftRotate(node.Left) + return rightRotate(node) + } + } + // 右偏树 + if bf < -1 { + if balanceFactor(node.Right) <= 0 { + // 左旋 + return leftRotate(node) + } else { + // 先右旋后左旋 + node.Right = rightRotate(node.Right) + return leftRotate(node) + } + } + // 平衡树,无需旋转,直接返回 + return node +} + +/* 插入结点 */ +func (t *avlTree) insert(val int) *TreeNode { + t.root = insertHelper(t.root, val) + return t.root +} + +/* 递归插入结点(辅助函数) */ +func insertHelper(node *TreeNode, val int) *TreeNode { + if node == nil { + return NewTreeNode(val) + } + /* 1. 查找插入位置,并插入结点 */ + if val < node.Val { + node.Left = insertHelper(node.Left, val) + } else if val > node.Val { + node.Right = insertHelper(node.Right, val) + } else { + // 重复结点不插入,直接返回 + return node + } + // 更新结点高度 + updateHeight(node) + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node) + // 返回子树的根节点 + return node +} + +/* 删除结点 */ +func (t *avlTree) remove(val int) *TreeNode { + root := removeHelper(t.root, val) + return root +} + +/* 递归删除结点(辅助函数) */ +func removeHelper(node *TreeNode, val int) *TreeNode { + if node == nil { + return nil + } + /* 1. 查找结点,并删除之 */ + if val < node.Val { + node.Left = removeHelper(node.Left, val) + } else if val > node.Val { + node.Right = removeHelper(node.Right, val) + } else { + if node.Left == nil || node.Right == nil { + child := node.Left + if node.Right != nil { + child = node.Right + } + // 子结点数量 = 0 ,直接删除 node 并返回 + if child == nil { + return nil + } else { + // 子结点数量 = 1 ,直接删除 node + node = child + } + } else { + // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 + temp := getInOrderNext(node.Right) + node.Right = removeHelper(node.Right, temp.Val) + node.Val = temp.Val + } + } + // 更新结点高度 + updateHeight(node) + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node) + // 返回子树的根节点 + return node +} + +/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ +func getInOrderNext(node *TreeNode) *TreeNode { + if node == nil { + return node + } + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + for node.Left != nil { + node = node.Left + } + return node +} + +/* 查找结点 */ +func (t *avlTree) search(val int) *TreeNode { + cur := t.root + // 循环查找,越过叶结点后跳出 + for cur != nil { + // 目标结点在 root 的右子树中 + if cur.Val < val { + cur = cur.Right + } else if cur.Val > val { + // 目标结点在 root 的左子树中 + cur = cur.Left + } else { + // 找到目标结点,跳出循环 + break + } + } + // 返回目标结点 + return cur +} diff --git a/codes/go/chapter_tree/avl_tree_test.go b/codes/go/chapter_tree/avl_tree_test.go new file mode 100644 index 000000000..c4fc6b719 --- /dev/null +++ b/codes/go/chapter_tree/avl_tree_test.go @@ -0,0 +1,54 @@ +// File: avl_tree_test.go +// Created Time: 2023-01-08 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + "fmt" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestAVLTree(t *testing.T) { + /* 初始化空 AVL 树 */ + tree := newAVLTree() + /* 插入结点 */ + // 请关注插入结点后,AVL 树是如何保持平衡的 + testInsert(tree, 1) + testInsert(tree, 2) + testInsert(tree, 3) + testInsert(tree, 4) + testInsert(tree, 5) + testInsert(tree, 8) + testInsert(tree, 7) + testInsert(tree, 9) + testInsert(tree, 10) + testInsert(tree, 6) + + /* 插入重复结点 */ + testInsert(tree, 7) + + /* 删除结点 */ + // 请关注删除结点后,AVL 树是如何保持平衡的 + testRemove(tree, 8) // 删除度为 0 的结点 + testRemove(tree, 5) // 删除度为 1 的结点 + testRemove(tree, 4) // 删除度为 2 的结点 + + /* 查询结点 */ + node := tree.search(7) + fmt.Printf("\n查找到的结点对象为 %#v ,结点值 = %d \n", node, node.Val) +} + +func testInsert(tree *avlTree, val int) { + tree.insert(val) + fmt.Printf("\n插入结点 %d 后,AVL 树为 \n", val) + PrintTree(tree.root) +} + +func testRemove(tree *avlTree, val int) { + tree.remove(val) + fmt.Printf("\n删除结点 %d 后,AVL 树为 \n", val) + PrintTree(tree.root) +} diff --git a/codes/go/pkg/tree_node.go b/codes/go/pkg/tree_node.go index b1e630e67..2ac2e22d1 100644 --- a/codes/go/pkg/tree_node.go +++ b/codes/go/pkg/tree_node.go @@ -9,16 +9,18 @@ import ( ) type TreeNode struct { - Val int - Left *TreeNode - Right *TreeNode + Val int // 结点值 + Height int // 结点高度 + Left *TreeNode // 左子结点引用 + Right *TreeNode // 右子结点引用 } func NewTreeNode(v int) *TreeNode { return &TreeNode{ - Left: nil, - Right: nil, - Val: v, + Val: v, + Height: 0, + Left: nil, + Right: nil, } } From 388509a84261c5690a511df8888d251cd7f151a9 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Mon, 9 Jan 2023 01:12:19 +0800 Subject: [PATCH 134/158] docs(tree/avl_tree): add go code --- codes/go/chapter_tree/avl_tree.go | 3 +- docs/chapter_tree/avl_tree.md | 167 ++++++++++++++++++++++++++++-- 2 files changed, 161 insertions(+), 9 deletions(-) diff --git a/codes/go/chapter_tree/avl_tree.go b/codes/go/chapter_tree/avl_tree.go index 111abd722..e83b2882b 100644 --- a/codes/go/chapter_tree/avl_tree.go +++ b/codes/go/chapter_tree/avl_tree.go @@ -16,8 +16,7 @@ func newAVLTree() *avlTree { return &avlTree{root: nil} } -/* 获取结点高度 - */ +/* 获取结点高度 */ func height(node *TreeNode) int { // 空结点高度为 -1 ,叶结点高度为 0 if node != nil { diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index 43e0d9f45..8fc2313c7 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -60,7 +60,13 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Go" ```go title="avl_tree.go" - + /* AVL 树结点类 */ + type TreeNode struct { + Val int // 结点值 + Height int // 结点高度 + Left *TreeNode // 左子结点引用 + Right *TreeNode // 右子结点引用 + } ``` === "JavaScript" @@ -143,7 +149,26 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Go" ```go title="avl_tree.go" - + /* 获取结点高度 */ + func height(node *TreeNode) int { + // 空结点高度为 -1 ,叶结点高度为 0 + if node != nil { + return node.Height + } + return -1 + } + + /* 更新结点高度 */ + func updateHeight(node *TreeNode) { + lh := height(node.Left) + rh := height(node.Right) + // 结点高度等于最高子树高度 + 1 + if lh > rh { + node.Height = lh + 1 + } else { + node.Height = rh + 1 + } + } ``` === "JavaScript" @@ -225,7 +250,15 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Go" ```go title="avl_tree.go" - + /* 获取平衡因子 */ + func balanceFactor(node *TreeNode) int { + // 空结点平衡因子为 0 + if node == nil { + return 0 + } + // 结点平衡因子 = 左子树高度 - 右子树高度 + return height(node.Left) - height(node.Right) + } ``` === "JavaScript" @@ -338,7 +371,19 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Go" ```go title="avl_tree.go" - + /* 右旋操作 */ + func rightRotate(node *TreeNode) *TreeNode { + child := node.Left + grandChild := child.Right + // 以 child 为原点,将 node 向右旋转 + child.Right = node + node.Left = grandChild + // 更新结点高度 + updateHeight(node) + updateHeight(child) + // 返回旋转后子树的根节点 + return child + } ``` === "JavaScript" @@ -441,7 +486,19 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Go" ```go title="avl_tree.go" - + /* 左旋操作 */ + func leftRotate(node *TreeNode) *TreeNode { + child := node.Right + grandChild := child.Left + // 以 child 为原点,将 node 向左旋转 + child.Left = node + node.Right = grandChild + // 更新结点高度 + updateHeight(node) + updateHeight(child) + // 返回旋转后子树的根节点 + return child + } ``` === "JavaScript" @@ -592,7 +649,36 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Go" ```go title="avl_tree.go" - + /* 执行旋转操作,使该子树重新恢复平衡 */ + func rotate(node *TreeNode) *TreeNode { + // 获取结点 node 的平衡因子 + // Go 推荐短变量,这里 bf 指代 balanceFactor + bf := balanceFactor(node) + // 左偏树 + if bf > 1 { + if balanceFactor(node.Left) >= 0 { + // 右旋 + return rightRotate(node) + } else { + // 先左旋后右旋 + node.Left = leftRotate(node.Left) + return rightRotate(node) + } + } + // 右偏树 + if bf < -1 { + if balanceFactor(node.Right) <= 0 { + // 左旋 + return leftRotate(node) + } else { + // 先右旋后左旋 + node.Right = rightRotate(node.Right) + return leftRotate(node) + } + } + // 平衡树,无需旋转,直接返回 + return node + } ``` === "JavaScript" @@ -730,7 +816,32 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Go" ```go title="avl_tree.go" - + /* 插入结点 */ + func (t *avlTree) insert(val int) *TreeNode { + t.root = insertHelper(t.root, val) + return t.root + } + /* 递归插入结点(辅助函数) */ + func insertHelper(node *TreeNode, val int) *TreeNode { + if node == nil { + return NewTreeNode(val) + } + /* 1. 查找插入位置,并插入结点 */ + if val < node.Val { + node.Left = insertHelper(node.Left, val) + } else if val > node.Val { + node.Right = insertHelper(node.Right, val) + } else { + // 重复结点不插入,直接返回 + return node + } + // 更新结点高度 + updateHeight(node) + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node) + // 返回子树的根节点 + return node + } ``` === "JavaScript" @@ -876,7 +987,49 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Go" ```go title="avl_tree.go" + /* 删除结点 */ + func (t *avlTree) remove(val int) *TreeNode { + root := removeHelper(t.root, val) + return root + } + /* 递归删除结点(辅助函数) */ + func removeHelper(node *TreeNode, val int) *TreeNode { + if node == nil { + return nil + } + /* 1. 查找结点,并删除之 */ + if val < node.Val { + node.Left = removeHelper(node.Left, val) + } else if val > node.Val { + node.Right = removeHelper(node.Right, val) + } else { + if node.Left == nil || node.Right == nil { + child := node.Left + if node.Right != nil { + child = node.Right + } + // 子结点数量 = 0 ,直接删除 node 并返回 + if child == nil { + return nil + } else { + // 子结点数量 = 1 ,直接删除 node + node = child + } + } else { + // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 + temp := getInOrderNext(node.Right) + node.Right = removeHelper(node.Right, temp.Val) + node.Val = temp.Val + } + } + // 更新结点高度 + updateHeight(node) + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node) + // 返回子树的根节点 + return node + } ``` === "JavaScript" From 4d0143613827ec426b058aaa21648de023ce7641 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 9 Jan 2023 02:30:10 +0800 Subject: [PATCH 135/158] Remove avl_tree.cpp to match the latest docs. --- codes/cpp/chapter_tree/avl_tree.cpp | 228 ---------------------------- 1 file changed, 228 deletions(-) delete mode 100644 codes/cpp/chapter_tree/avl_tree.cpp diff --git a/codes/cpp/chapter_tree/avl_tree.cpp b/codes/cpp/chapter_tree/avl_tree.cpp deleted file mode 100644 index 8bc0741dc..000000000 --- a/codes/cpp/chapter_tree/avl_tree.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/** - * File: avl_tree.cpp - * Created Time: 2022-12-2 - * Author: mgisr (maguagua0706@gmail.com) - */ - -#include "../include/include.hpp" - -class AvlTree { -private: - TreeNode *root{}; - static bool isBalance(const TreeNode *p); - static int getBalanceFactor(const TreeNode *p); - static void updateHeight(TreeNode *p); - void fixBalance(TreeNode *p); - static bool isLeftChild(const TreeNode *p); - static TreeNode *&fromParentTo(TreeNode *node); -public: - AvlTree() = default; - AvlTree(const AvlTree &p) = default; - const TreeNode *search(int val); - bool insert(int val); - bool remove(int val); - void printTree(); -}; - -// 判断该结点是否平衡 -bool AvlTree::isBalance(const TreeNode *p) { - int balance_factor = getBalanceFactor(p); - if (-1 <= balance_factor && balance_factor <= 1) { return true; } - else { return false; } -} - -// 获取当前结点的平衡因子 -int AvlTree::getBalanceFactor(const TreeNode *p) { - if (p->left == nullptr && p->right == nullptr) { return 0; } - else if (p->left == nullptr) { return (-1 - p->right->height); } - else if (p->right == nullptr) { return p->left->height + 1; } - else { return p->left->height - p->right->height; } -} - -// 更新结点高度 -void AvlTree::updateHeight(TreeNode *p) { - if (p->left == nullptr && p->right == nullptr) { p->height = 0; } - else if (p->left == nullptr) { p->height = p->right->height + 1; } - else if (p->right == nullptr) { p->height = p->left->height + 1; } - else { p->height = std::max(p->left->height, p->right->height) + 1; } -} - -void AvlTree::fixBalance(TreeNode *p) { - // 左旋操作 - auto rotate_left = [&](TreeNode *node) -> TreeNode * { - TreeNode *temp = node->right; - temp->parent = p->parent; - node->right = temp->left; - if (temp->left != nullptr) { - temp->left->parent = node; - } - temp->left = node; - node->parent = temp; - updateHeight(node); - updateHeight(temp); - return temp; - }; - // 右旋操作 - auto rotate_right = [&](TreeNode *node) -> TreeNode * { - TreeNode *temp = node->left; - temp->parent = p->parent; - node->left = temp->right; - if (temp->right != nullptr) { - temp->right->parent = node; - } - temp->right = node; - node->parent = temp; - updateHeight(node); - updateHeight(temp); - return temp; - }; - // 根据规则选取旋转方式 - if (getBalanceFactor(p) > 1) { - if (getBalanceFactor(p->left) > 0) { - if (p->parent == nullptr) { root = rotate_right(p); } - else { fromParentTo(p) = rotate_right(p); } - } else { - p->left = rotate_left(p->left); - if (p->parent == nullptr) { root = rotate_right(p); } - else { fromParentTo(p) = rotate_right(p); } - } - } else { - if (getBalanceFactor(p->right) < 0) { - if (p->parent == nullptr) { root = rotate_left(p); } - else { fromParentTo(p) = rotate_left(p); } - } else { - p->right = rotate_right(p->right); - if (p->parent == nullptr) { root = rotate_left(p); } - else { fromParentTo(p) = rotate_left(p); } - } - } -} - -// 判断当前结点是否为其父节点的左孩子 -bool AvlTree::isLeftChild(const TreeNode *p) { - if (p->parent == nullptr) { return false; } - return (p->parent->left == p); -} - -// 返回父节点指向当前结点指针的引用 -TreeNode *&AvlTree::fromParentTo(TreeNode *node) { - if (isLeftChild(node)) { return node->parent->left; } - else { return node->parent->right; } -} - -const TreeNode *AvlTree::search(int val) { - TreeNode *p = root; - while (p != nullptr) { - if (p->val == val) { return p; } - else if (p->val > val) { p = p->left; } - else { p = p->right; } - } - return nullptr; -} - -bool AvlTree::insert(int val) { - TreeNode *p = root; - if (p == nullptr) { - root = new TreeNode(val); - return true; - } - for (;;) { - if (p->val == val) { return false; } - else if (p->val > val) { - if (p->left == nullptr) { - p->left = new TreeNode(val, p); - break; - } else { - p = p->left; - } - } else { - if (p->right == nullptr) { - p->right = new TreeNode(val, p); - break; - } else { - p = p->right; - } - } - } - for (; p != nullptr; p = p->parent) { - if (!isBalance(p)) { - fixBalance(p); - break; - } else { updateHeight(p); } - } - return true; -} - -bool AvlTree::remove(int val) { - TreeNode *p = root; - if (p == nullptr) { return false; } - while (p != nullptr) { - if (p->val == val) { - TreeNode *real_delete_node = p; - TreeNode *next_node; - if (p->left == nullptr) { - next_node = p->right; - if (p->parent == nullptr) { root = next_node; } - else { fromParentTo(p) = next_node; } - } else if (p->right == nullptr) { - next_node = p->left; - if (p->parent == nullptr) { root = next_node; } - else { fromParentTo(p) = next_node; } - } else { - while (real_delete_node->left != nullptr) { - real_delete_node = real_delete_node->left; - } - std::swap(p->val, real_delete_node->val); - next_node = real_delete_node->right; - if (real_delete_node->parent == p) { p->right = next_node; } - else { real_delete_node->parent->left = next_node; } - } - if (next_node != nullptr) { - next_node->parent = real_delete_node->parent; - } - for (p = real_delete_node; p != nullptr; p = p->parent) { - if (!isBalance(p)) { fixBalance(p); } - updateHeight(p); - } - delete real_delete_node; - return true; - } else if (p->val > val) { - p = p->left; - } else { - p = p->right; - } - } - return false; -} - -void inOrder(const TreeNode *root) { - if (root == nullptr) return; - inOrder(root->left); - cout << root->val << ' '; - inOrder(root->right); -} - -void AvlTree::printTree() { - inOrder(root); - cout << endl; -} - -int main() { - AvlTree tree = AvlTree(); - // tree.insert(13); - // tree.insert(24); - // tree.insert(37); - // tree.insert(90); - // tree.insert(53); - - tree.insert(53); - tree.insert(90); - tree.insert(37); - tree.insert(24); - tree.insert(13); - tree.remove(90); - tree.printTree(); - const TreeNode *p = tree.search(37); - cout << p->val; - return 0; -} \ No newline at end of file From 97ee638d31a0f88b13644ff0c81352094d18496f Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 9 Jan 2023 02:49:34 +0800 Subject: [PATCH 136/158] Update the Optional alias of Python codes. --- codes/python/chapter_tree/avl_tree.py | 22 +++++++++---------- .../python/chapter_tree/binary_search_tree.py | 16 ++++++-------- codes/python/chapter_tree/binary_tree_bfs.py | 4 +--- codes/python/chapter_tree/binary_tree_dfs.py | 8 +++---- codes/python/include/__init__.py | 2 +- docs/chapter_tree/avl_tree.md | 16 +++++++------- docs/chapter_tree/binary_search_tree.md | 6 ++--- docs/chapter_tree/binary_tree_traversal.md | 8 +++---- 8 files changed, 37 insertions(+), 45 deletions(-) diff --git a/codes/python/chapter_tree/avl_tree.py b/codes/python/chapter_tree/avl_tree.py index 04218e46a..83929981d 100644 --- a/codes/python/chapter_tree/avl_tree.py +++ b/codes/python/chapter_tree/avl_tree.py @@ -5,30 +5,28 @@ Author: a16su (lpluls001@gmail.com) """ import sys, os.path as osp -import typing - sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * class AVLTree: - def __init__(self, root: typing.Optional[TreeNode] = None): + def __init__(self, root: Optional[TreeNode] = None): self.root = root """ 获取结点高度 """ - def height(self, node: typing.Optional[TreeNode]) -> int: + def height(self, node: Optional[TreeNode]) -> int: # 空结点高度为 -1 ,叶结点高度为 0 if node is not None: return node.height return -1 """ 更新结点高度 """ - def __update_height(self, node: TreeNode): + def __update_height(self, node: Optional[TreeNode]): # 结点高度等于最高子树高度 + 1 node.height = max([self.height(node.left), self.height(node.right)]) + 1 """ 获取平衡因子 """ - def balance_factor(self, node: TreeNode) -> int: + def balance_factor(self, node: Optional[TreeNode]) -> int: # 空结点平衡因子为 0 if node is None: return 0 @@ -36,7 +34,7 @@ class AVLTree: return self.height(node.left) - self.height(node.right) """ 右旋操作 """ - def __right_rotate(self, node: TreeNode) -> TreeNode: + def __right_rotate(self, node: Optional[TreeNode]) -> TreeNode: child = node.left grand_child = child.right # 以 child 为原点,将 node 向右旋转 @@ -49,7 +47,7 @@ class AVLTree: return child """ 左旋操作 """ - def __left_rotate(self, node: TreeNode) -> TreeNode: + def __left_rotate(self, node: Optional[TreeNode]) -> TreeNode: child = node.right grand_child = child.left # 以 child 为原点,将 node 向左旋转 @@ -62,7 +60,7 @@ class AVLTree: return child """ 执行旋转操作,使该子树重新恢复平衡 """ - def __rotate(self, node: TreeNode) -> TreeNode: + def __rotate(self, node: Optional[TreeNode]) -> TreeNode: # 获取结点 node 的平衡因子 balance_factor = self.balance_factor(node) # 左偏树 @@ -92,7 +90,7 @@ class AVLTree: return self.root """ 递归插入结点(辅助函数)""" - def __insert_helper(self, node: typing.Optional[TreeNode], val: int) -> TreeNode: + def __insert_helper(self, node: Optional[TreeNode], val: int) -> TreeNode: if node is None: return TreeNode(val) # 1. 查找插入位置,并插入结点 @@ -114,7 +112,7 @@ class AVLTree: return root """ 递归删除结点(辅助函数) """ - def __remove_helper(self, node: typing.Optional[TreeNode], val: int) -> typing.Optional[TreeNode]: + def __remove_helper(self, node: Optional[TreeNode], val: int) -> Optional[TreeNode]: if node is None: return None # 1. 查找结点,并删除之 @@ -141,7 +139,7 @@ class AVLTree: return self.__rotate(node) """ 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) """ - def __get_inorder_next(self, node: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: + def __get_inorder_next(self, node: Optional[TreeNode]) -> Optional[TreeNode]: if node is None: return None # 循环访问左子结点,直到叶结点时为最小结点,跳出 diff --git a/codes/python/chapter_tree/binary_search_tree.py b/codes/python/chapter_tree/binary_search_tree.py index 7633b6288..2a8272761 100644 --- a/codes/python/chapter_tree/binary_search_tree.py +++ b/codes/python/chapter_tree/binary_search_tree.py @@ -5,20 +5,18 @@ Author: a16su (lpluls001@gmail.com) """ import sys, os.path as osp -import typing - sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * """ 二叉搜索树 """ class BinarySearchTree: - def __init__(self, nums: typing.List[int]) -> None: + def __init__(self, nums: List[int]) -> None: nums.sort() self.__root = self.build_tree(nums, 0, len(nums) - 1) """ 构建二叉搜索树 """ - def build_tree(self, nums: typing.List[int], start_index: int, end_index: int) -> typing.Optional[TreeNode]: + def build_tree(self, nums: List[int], start_index: int, end_index: int) -> Optional[TreeNode]: if start_index > end_index: return None @@ -31,11 +29,11 @@ class BinarySearchTree: return root @property - def root(self) -> typing.Optional[TreeNode]: + def root(self) -> Optional[TreeNode]: return self.__root """ 查找结点 """ - def search(self, num: int) -> typing.Optional[TreeNode]: + def search(self, num: int) -> Optional[TreeNode]: cur = self.root # 循环查找,越过叶结点后跳出 while cur is not None: @@ -51,7 +49,7 @@ class BinarySearchTree: return cur """ 插入结点 """ - def insert(self, num: int) -> typing.Optional[TreeNode]: + def insert(self, num: int) -> Optional[TreeNode]: root = self.root # 若树为空,直接提前返回 if root is None: @@ -81,7 +79,7 @@ class BinarySearchTree: return node """ 删除结点 """ - def remove(self, num: int) -> typing.Optional[TreeNode]: + def remove(self, num: int) -> Optional[TreeNode]: root = self.root # 若树为空,直接提前返回 if root is None: @@ -126,7 +124,7 @@ class BinarySearchTree: return cur """ 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) """ - def get_inorder_next(self, root: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: + def get_inorder_next(self, root: Optional[TreeNode]) -> Optional[TreeNode]: if root is None: return root # 循环访问左子结点,直到叶结点时为最小结点,跳出 diff --git a/codes/python/chapter_tree/binary_tree_bfs.py b/codes/python/chapter_tree/binary_tree_bfs.py index 225a8bd6e..fb3e93059 100644 --- a/codes/python/chapter_tree/binary_tree_bfs.py +++ b/codes/python/chapter_tree/binary_tree_bfs.py @@ -5,14 +5,12 @@ Author: a16su (lpluls001@gmail.com) """ import sys, os.path as osp -import typing - sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * """ 层序遍历 """ -def hier_order(root: TreeNode): +def hier_order(root: Optional[TreeNode]): # 初始化队列,加入根结点 queue = collections.deque() queue.append(root) diff --git a/codes/python/chapter_tree/binary_tree_dfs.py b/codes/python/chapter_tree/binary_tree_dfs.py index a1d46e697..b0efb14c8 100644 --- a/codes/python/chapter_tree/binary_tree_dfs.py +++ b/codes/python/chapter_tree/binary_tree_dfs.py @@ -5,8 +5,6 @@ Author: a16su (lpluls001@gmail.com) """ import sys, os.path as osp -import typing - sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from include import * @@ -14,7 +12,7 @@ from include import * res = [] """ 前序遍历 """ -def pre_order(root: typing.Optional[TreeNode]): +def pre_order(root: Optional[TreeNode]): if root is None: return # 访问优先级:根结点 -> 左子树 -> 右子树 @@ -23,7 +21,7 @@ def pre_order(root: typing.Optional[TreeNode]): pre_order(root=root.right) """ 中序遍历 """ -def in_order(root: typing.Optional[TreeNode]): +def in_order(root: Optional[TreeNode]): if root is None: return # 访问优先级:左子树 -> 根结点 -> 右子树 @@ -32,7 +30,7 @@ def in_order(root: typing.Optional[TreeNode]): in_order(root=root.right) """ 后序遍历 """ -def post_order(root: typing.Optional[TreeNode]): +def post_order(root: Optional[TreeNode]): if root is None: return # 访问优先级:左子树 -> 右子树 -> 根结点 diff --git a/codes/python/include/__init__.py b/codes/python/include/__init__.py index 31ed69ace..9d4e90bee 100644 --- a/codes/python/include/__init__.py +++ b/codes/python/include/__init__.py @@ -4,7 +4,7 @@ import queue import random import functools import collections -from typing import List +from typing import Optional, List, Dict, DefaultDict, OrderedDict, Set, Deque from .linked_list import ListNode, list_to_linked_list, linked_list_to_list, get_list_node from .binary_tree import TreeNode, list_to_tree, tree_to_list, get_tree_node from .print_util import print_matrix, print_linked_list, print_tree, print_dict \ No newline at end of file diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index 8fc2313c7..9944f29e8 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -134,14 +134,14 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit ```python title="avl_tree.py" """ 获取结点高度 """ - def height(self, node: typing.Optional[TreeNode]) -> int: + def height(self, node: Optional[TreeNode]) -> int: # 空结点高度为 -1 ,叶结点高度为 0 if node is not None: return node.height return -1 """ 更新结点高度 """ - def __update_height(self, node: TreeNode): + def __update_height(self, node: Optional[TreeNode]): # 结点高度等于最高子树高度 + 1 node.height = max([self.height(node.left), self.height(node.right)]) + 1 ``` @@ -239,7 +239,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit ```python title="avl_tree.py" """ 获取平衡因子 """ - def balance_factor(self, node: TreeNode) -> int: + def balance_factor(self, node: Optional[TreeNode]) -> int: # 空结点平衡因子为 0 if node is None: return 0 @@ -355,7 +355,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 ```python title="avl_tree.py" """ 右旋操作 """ - def __right_rotate(self, node: TreeNode) -> TreeNode: + def __right_rotate(self, node: Optional[TreeNode]) -> TreeNode: child = node.left grand_child = child.right # 以 child 为原点,将 node 向右旋转 @@ -470,7 +470,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 ```python title="avl_tree.py" """ 左旋操作 """ - def __left_rotate(self, node: TreeNode) -> TreeNode: + def __left_rotate(self, node: Optional[TreeNode]) -> TreeNode: child = node.right grand_child = child.left # 以 child 为原点,将 node 向左旋转 @@ -621,7 +621,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 ```python title="avl_tree.py" """ 执行旋转操作,使该子树重新恢复平衡 """ - def __rotate(self, node: TreeNode) -> TreeNode: + def __rotate(self, node: Optional[TreeNode]) -> TreeNode: # 获取结点 node 的平衡因子 balance_factor = self.balance_factor(node) # 左偏树 @@ -796,7 +796,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 return self.root """ 递归插入结点(辅助函数)""" - def __insert_helper(self, node: typing.Optional[TreeNode], val: int) -> TreeNode: + def __insert_helper(self, node: Optional[TreeNode], val: int) -> TreeNode: if node is None: return TreeNode(val) # 1. 查找插入位置,并插入结点 @@ -957,7 +957,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 return root """ 递归删除结点(辅助函数) """ - def __remove_helper(self, node: typing.Optional[TreeNode], val: int) -> typing.Optional[TreeNode]: + def __remove_helper(self, node: Optional[TreeNode], val: int) -> Optional[TreeNode]: if node is None: return None # 1. 查找结点,并删除之 diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index e3ddfaaac..a4c5a5d2a 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -83,7 +83,7 @@ comments: true ```python title="binary_search_tree.py" """ 查找结点 """ - def search(self, num: int) -> typing.Optional[TreeNode]: + def search(self, num: int) -> Optional[TreeNode]: cur = self.root # 循环查找,越过叶结点后跳出 while cur is not None: @@ -265,7 +265,7 @@ comments: true ```python title="binary_search_tree.py" """ 插入结点 """ - def insert(self, num: int) -> typing.Optional[TreeNode]: + def insert(self, num: int) -> Optional[TreeNode]: root = self.root # 若树为空,直接提前返回 if root is None: @@ -560,7 +560,7 @@ comments: true ```python title="binary_search_tree.py" """ 删除结点 """ - def remove(self, num: int) -> typing.Optional[TreeNode]: + def remove(self, num: int) -> Optional[TreeNode]: root = self.root # 若树为空,直接提前返回 if root is None: diff --git a/docs/chapter_tree/binary_tree_traversal.md b/docs/chapter_tree/binary_tree_traversal.md index c64077072..650a15297 100644 --- a/docs/chapter_tree/binary_tree_traversal.md +++ b/docs/chapter_tree/binary_tree_traversal.md @@ -66,7 +66,7 @@ comments: true ```python title="binary_tree_bfs.py" """ 层序遍历 """ - def hier_order(root: TreeNode): + def hier_order(root: Optional[TreeNode]): # 初始化队列,加入根结点 queue = collections.deque() queue.append(root) @@ -277,7 +277,7 @@ comments: true ```python title="binary_tree_dfs.py" """ 前序遍历 """ - def pre_order(root: typing.Optional[TreeNode]): + def pre_order(root: Optional[TreeNode]): if root is None: return # 访问优先级:根结点 -> 左子树 -> 右子树 @@ -286,7 +286,7 @@ comments: true pre_order(root=root.right) """ 中序遍历 """ - def in_order(root: typing.Optional[TreeNode]): + def in_order(root: Optional[TreeNode]): if root is None: return # 访问优先级:左子树 -> 根结点 -> 右子树 @@ -295,7 +295,7 @@ comments: true in_order(root=root.right) """ 后序遍历 """ - def post_order(root: typing.Optional[TreeNode]): + def post_order(root: Optional[TreeNode]): if root is None: return # 访问优先级:左子树 -> 右子树 -> 根结点 From 5e23c75870df5928e72fbd35e173a39910d304ba Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Sun, 8 Jan 2023 23:44:13 +0800 Subject: [PATCH 137/158] feat: add Swift codes for list article --- codes/swift/Package.swift | 4 + .../chapter_array_and_linkedlist/list.swift | 64 ++++++++ .../my_list.swift | 147 ++++++++++++++++++ docs/chapter_array_and_linkedlist/list.md | 143 ++++++++++++++++- 4 files changed, 354 insertions(+), 4 deletions(-) create mode 100644 codes/swift/chapter_array_and_linkedlist/list.swift create mode 100644 codes/swift/chapter_array_and_linkedlist/my_list.swift diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index 4cf3601c7..3a5a18ed3 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -11,6 +11,8 @@ let package = Package( .executable(name: "leetcode_two_sum", targets: ["leetcode_two_sum"]), .executable(name: "array", targets: ["array"]), .executable(name: "linked_list", targets: ["linked_list"]), + .executable(name: "list", targets: ["list"]), + .executable(name: "my_list", targets: ["my_list"]), ], targets: [ .target(name: "utils", path: "utils"), @@ -20,5 +22,7 @@ let package = Package( .executableTarget(name: "leetcode_two_sum", path: "chapter_computational_complexity", sources: ["leetcode_two_sum.swift"]), .executableTarget(name: "array", path: "chapter_array_and_linkedlist", sources: ["array.swift"]), .executableTarget(name: "linked_list", dependencies: ["utils"], path: "chapter_array_and_linkedlist", sources: ["linked_list.swift"]), + .executableTarget(name: "list", path: "chapter_array_and_linkedlist", sources: ["list.swift"]), + .executableTarget(name: "my_list", path: "chapter_array_and_linkedlist", sources: ["my_list.swift"]), ] ) diff --git a/codes/swift/chapter_array_and_linkedlist/list.swift b/codes/swift/chapter_array_and_linkedlist/list.swift new file mode 100644 index 000000000..27801d13f --- /dev/null +++ b/codes/swift/chapter_array_and_linkedlist/list.swift @@ -0,0 +1,64 @@ +/** + * File: list.swift + * Created Time: 2023-01-08 + * Author: nuomi1 (nuomi1@qq.com) + */ + +@main +enum List { + /* Driver Code */ + static func main() { + /* 初始化列表 */ + var list = [1, 3, 2, 5, 4] + print("列表 list = \(list)") + + /* 访问元素 */ + let num = list[1] + print("访问索引 1 处的元素,得到 num = \(num)") + + /* 更新元素 */ + list[1] = 0 + print("将索引 1 处的元素更新为 0 ,得到 list = \(list)") + + /* 清空列表 */ + list.removeAll() + print("清空列表后 list = \(list)") + + /* 尾部添加元素 */ + list.append(1) + list.append(3) + list.append(2) + list.append(5) + list.append(4) + print("添加元素后 list = \(list)") + + /* 中间插入元素 */ + list.insert(6, at: 3) + print("在索引 3 处插入数字 6 ,得到 list = \(list)") + + /* 删除元素 */ + list.remove(at: 3) + print("删除索引 3 处的元素,得到 list = \(list)") + + /* 通过索引遍历列表 */ + var count = 0 + for _ in list.indices { + count += 1 + } + + /* 直接遍历列表元素 */ + count = 0 + for _ in list { + count += 1 + } + + /* 拼接两个列表 */ + let list1 = [6, 8, 7, 10, 9] + list.append(contentsOf: list1) + print("将列表 list1 拼接到 list 之后,得到 list = \(list)") + + /* 排序列表 */ + list.sort() + print("排序列表后 list = \(list)") + } +} diff --git a/codes/swift/chapter_array_and_linkedlist/my_list.swift b/codes/swift/chapter_array_and_linkedlist/my_list.swift new file mode 100644 index 000000000..423b02027 --- /dev/null +++ b/codes/swift/chapter_array_and_linkedlist/my_list.swift @@ -0,0 +1,147 @@ +/** + * File: my_list.swift + * Created Time: 2023-01-08 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* 列表类简易实现 */ +class MyList { + private var nums: [Int] // 数组(存储列表元素) + private var _capacity = 10 // 列表容量 + private var _size = 0 // 列表长度(即当前元素数量) + private let extendRatio = 2 // 每次列表扩容的倍数 + + /* 构造函数 */ + init() { + nums = Array(repeating: 0, count: _capacity) + } + + /* 获取列表长度(即当前元素数量)*/ + func size() -> Int { + _size + } + + /* 获取列表容量 */ + func capacity() -> Int { + _capacity + } + + /* 访问元素 */ + func get(index: Int) -> Int { + // 索引如果越界则抛出错误,下同 + if index >= _size { + fatalError("索引越界") + } + return nums[index] + } + + /* 更新元素 */ + func set(index: Int, num: Int) { + if index >= _size { + fatalError("索引越界") + } + nums[index] = num + } + + /* 尾部添加元素 */ + func add(num: Int) { + // 元素数量超出容量时,触发扩容机制 + if _size == _capacity { + extendCapacity() + } + nums[_size] = num + // 更新元素数量 + _size += 1 + } + + /* 中间插入元素 */ + func insert(index: Int, num: Int) { + if index >= _size { + fatalError("索引越界") + } + // 元素数量超出容量时,触发扩容机制 + if _size == _capacity { + extendCapacity() + } + // 将索引 index 以及之后的元素都向后移动一位 + for j in sequence(first: _size - 1, next: { $0 >= index + 1 ? $0 - 1 : nil }) { + nums[j + 1] = nums[j] + } + nums[index] = num + // 更新元素数量 + _size += 1 + } + + /* 删除元素 */ + @discardableResult + func remove(index: Int) -> Int { + if index >= _size { + fatalError("索引越界") + } + let num = nums[index] + // 将索引 index 之后的元素都向前移动一位 + for j in index ..< (_size - 1) { + nums[j] = nums[j + 1] + } + // 更新元素数量 + _size -= 1 + // 返回被删除元素 + return num + } + + /* 列表扩容 */ + func extendCapacity() { + // 新建一个长度为 size 的数组,并将原数组拷贝到新数组 + nums = nums + Array(repeating: 0, count: _capacity * (extendRatio - 1)) + // 更新列表容量 + _capacity = nums.count + } + + /* 将列表转换为数组 */ + func toArray() -> [Int] { + var nums = Array(repeating: 0, count: _size) + for i in 0 ..< _size { + nums[i] = get(index: i) + } + return nums + } +} + +@main +enum _MyList { + /* Driver Code */ + static func main() { + /* 初始化列表 */ + let list = MyList() + /* 尾部添加元素 */ + list.add(num: 1) + list.add(num: 3) + list.add(num: 2) + list.add(num: 5) + list.add(num: 4) + print("列表 list = \(list.toArray()) ,容量 = \(list.capacity()) ,长度 = \(list.size())") + + /* 中间插入元素 */ + list.insert(index: 3, num: 6) + print("在索引 3 处插入数字 6 ,得到 list = \(list.toArray())") + + /* 删除元素 */ + list.remove(index: 3) + print("删除索引 3 处的元素,得到 list = \(list.toArray())") + + /* 访问元素 */ + let num = list.get(index: 1) + print("访问索引 1 处的元素,得到 num = \(num)") + + /* 更新元素 */ + list.set(index: 1, num: 0) + print("将索引 1 处的元素更新为 0 ,得到 list = \(list.toArray())") + + /* 测试扩容机制 */ + for i in 0 ..< 10 { + // 在 i = 5 时,列表长度将超出列表容量,此时触发扩容机制 + list.add(num: i) + } + print("扩容后的列表 list = \(list.toArray()) ,容量 = \(list.capacity()) ,长度 = \(list.size())") + } +} diff --git a/docs/chapter_array_and_linkedlist/list.md b/docs/chapter_array_and_linkedlist/list.md index ca4774a04..ce8bb8870 100644 --- a/docs/chapter_array_and_linkedlist/list.md +++ b/docs/chapter_array_and_linkedlist/list.md @@ -94,7 +94,11 @@ comments: true === "Swift" ```swift title="list.swift" - + /* 初始化列表 */ + // 无初始值 + let list1: [Int] = [] + // 有初始值 + var list = [1, 3, 2, 5, 4] ``` **访问与更新元素**。列表的底层数据结构是数组,因此可以在 $O(1)$ 时间内访问与更新元素,效率很高。 @@ -178,7 +182,11 @@ comments: true === "Swift" ```swift title="list.swift" + /* 访问元素 */ + let num = list[1] // 访问索引 1 处的元素 + /* 更新元素 */ + list[1] = 0 // 将索引 1 处的元素更新为 0 ``` **在列表中添加、插入、删除元素**。相对于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度为 $O(1)$ ,但是插入与删除元素的效率仍与数组一样低,时间复杂度为 $O(N)$ 。 @@ -332,7 +340,21 @@ comments: true === "Swift" ```swift title="list.swift" + /* 清空列表 */ + list.removeAll() + /* 尾部添加元素 */ + list.append(1) + list.append(3) + list.append(2) + list.append(5) + list.append(4) + + /* 中间插入元素 */ + list.insert(6, at: 3) // 在索引 3 处插入数字 6 + + /* 删除元素 */ + list.remove(at: 3) // 删除索引 3 处的元素 ``` **遍历列表**。与数组一样,列表可以使用索引遍历,也可以使用 `for-each` 直接遍历。 @@ -458,7 +480,17 @@ comments: true === "Swift" ```swift title="list.swift" + /* 通过索引遍历列表 */ + var count = 0 + for _ in list.indices { + count += 1 + } + /* 直接遍历列表元素 */ + count = 0 + for _ in list { + count += 1 + } ``` **拼接两个列表**。再创建一个新列表 `list1` ,我们可以将其中一个列表拼接到另一个的尾部。 @@ -529,7 +561,9 @@ comments: true === "Swift" ```swift title="list.swift" - + /* 拼接两个列表 */ + let list1 = [6, 8, 7, 10, 9] + list.append(contentsOf: list1) // 将列表 list1 拼接到 list 之后 ``` **排序列表**。排序也是常用的方法之一,完成列表排序后,我们就可以使用在数组类算法题中经常考察的「二分查找」和「双指针」算法了。 @@ -592,7 +626,8 @@ comments: true === "Swift" ```swift title="list.swift" - + /* 排序列表 */ + list.sort() // 排序后,列表元素从小到大排列 ``` ## 列表简易实现 * @@ -1263,6 +1298,106 @@ comments: true === "Swift" ```swift title="my_list.swift" + /* 列表类简易实现 */ + class MyList { + private var nums: [Int] // 数组(存储列表元素) + private var _capacity = 10 // 列表容量 + private var _size = 0 // 列表长度(即当前元素数量) + private let extendRatio = 2 // 每次列表扩容的倍数 + /* 构造函数 */ + init() { + nums = Array(repeating: 0, count: _capacity) + } + + /* 获取列表长度(即当前元素数量)*/ + func size() -> Int { + _size + } + + /* 获取列表容量 */ + func capacity() -> Int { + _capacity + } + + /* 访问元素 */ + func get(index: Int) -> Int { + // 索引如果越界则抛出错误,下同 + if index >= _size { + fatalError("索引越界") + } + return nums[index] + } + + /* 更新元素 */ + func set(index: Int, num: Int) { + if index >= _size { + fatalError("索引越界") + } + nums[index] = num + } + + /* 尾部添加元素 */ + func add(num: Int) { + // 元素数量超出容量时,触发扩容机制 + if _size == _capacity { + extendCapacity() + } + nums[_size] = num + // 更新元素数量 + _size += 1 + } + + /* 中间插入元素 */ + func insert(index: Int, num: Int) { + if index >= _size { + fatalError("索引越界") + } + // 元素数量超出容量时,触发扩容机制 + if _size == _capacity { + extendCapacity() + } + // 将索引 index 以及之后的元素都向后移动一位 + for j in sequence(first: _size - 1, next: { $0 >= index + 1 ? $0 - 1 : nil }) { + nums[j + 1] = nums[j] + } + nums[index] = num + // 更新元素数量 + _size += 1 + } + + /* 删除元素 */ + @discardableResult + func remove(index: Int) -> Int { + if index >= _size { + fatalError("索引越界") + } + let num = nums[index] + // 将索引 index 之后的元素都向前移动一位 + for j in index ..< (_size - 1) { + nums[j] = nums[j + 1] + } + // 更新元素数量 + _size -= 1 + // 返回被删除元素 + return num + } + + /* 列表扩容 */ + func extendCapacity() { + // 新建一个长度为 size 的数组,并将原数组拷贝到新数组 + nums = nums + Array(repeating: 0, count: _capacity * (extendRatio - 1)) + // 更新列表容量 + _capacity = nums.count + } + + /* 将列表转换为数组 */ + func toArray() -> [Int] { + var nums = Array(repeating: 0, count: _size) + for i in 0 ..< _size { + nums[i] = get(index: i) + } + return nums + } + } ``` - From e6021ff59e66fea7d5189135ccdf214697d647ab Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Sun, 8 Jan 2023 23:48:35 +0800 Subject: [PATCH 138/158] fix: predicate --- .../chapter_computational_complexity/time_complexity.swift | 4 ++-- docs/chapter_computational_complexity/time_complexity.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/codes/swift/chapter_computational_complexity/time_complexity.swift b/codes/swift/chapter_computational_complexity/time_complexity.swift index 41695cfd6..1b1027ef2 100644 --- a/codes/swift/chapter_computational_complexity/time_complexity.swift +++ b/codes/swift/chapter_computational_complexity/time_complexity.swift @@ -49,7 +49,7 @@ func quadratic(n: Int) -> Int { func bubbleSort(nums: inout [Int]) -> Int { var count = 0 // 计数器 // 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for i in sequence(first: nums.count - 1, next: { $0 > 0 ? $0 - 1 : nil }) { + for i in sequence(first: nums.count - 1, next: { $0 > 0 + 1 ? $0 - 1 : nil }) { // 内循环:冒泡操作 for j in 0 ..< i { if nums[j] > nums[j + 1] { @@ -149,7 +149,7 @@ enum TimeComplexity { count = quadratic(n: n) print("平方阶的计算操作数量 = \(count)") - var nums = Array(sequence(first: n, next: { $0 > 0 ? $0 - 1 : nil })) // [n,n-1,...,2,1] + var nums = Array(sequence(first: n, next: { $0 > 0 + 1 ? $0 - 1 : nil })) // [n,n-1,...,2,1] count = bubbleSort(nums: &nums) print("平方阶(冒泡排序)的计算操作数量 = \(count)") diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 25d9fce88..09aee7b79 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -1481,7 +1481,7 @@ $$ func bubbleSort(nums: inout [Int]) -> Int { var count = 0 // 计数器 // 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for i in sequence(first: nums.count - 1, next: { $0 > 0 ? $0 - 1 : nil }) { + for i in sequence(first: nums.count - 1, next: { $0 > 0 + 1 ? $0 - 1 : nil }) { // 内循环:冒泡操作 for j in 0 ..< i { if nums[j] > nums[j + 1] { From 0b965a5e32e9c4adeee9b869c35b355549c9d4bb Mon Sep 17 00:00:00 2001 From: iStig Date: Mon, 9 Jan 2023 11:15:26 +0800 Subject: [PATCH 139/158] Update stack.md --- docs/chapter_stack_and_queue/stack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index 8bb343c4f..d5a3f9164 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -906,5 +906,5 @@ comments: true ## 栈典型应用 -- **浏览器中的后退与前进、软件中的撤销与反撤销**。每当我们打开新的网页,浏览器就讲上一个网页执行入栈,这样我们就可以通过「后退」操作来回到上一页面,后退操作实际上是在执行出栈。如果要同时支持后退和前进,那么则需要两个栈来配合实现。 +- **浏览器中的后退与前进、软件中的撤销与反撤销**。每当我们打开新的网页,浏览器就将上一个网页执行入栈,这样我们就可以通过「后退」操作来回到上一页面,后退操作实际上是在执行出栈。如果要同时支持后退和前进,那么则需要两个栈来配合实现。 - **程序内存管理**。每当调用函数时,系统就会在栈顶添加一个栈帧,用来记录函数的上下文信息。在递归函数中,向下递推会不断执行入栈,向上回溯阶段时出栈。 From 2d461b03a4bdc51b0e7185ae9446fb6442d0fe4d Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Mon, 9 Jan 2023 19:31:45 +0800 Subject: [PATCH 140/158] add zig codes for Section 'Space Complexity' and 'Space Time Tradeoff' --- codes/zig/build.zig | 95 +++++++------ .../space_complexity.zig | 125 ++++++++++++++++++ codes/zig/include/ListNode.zig | 20 +++ codes/zig/include/PrintUtil.zig | 69 +++++++++- codes/zig/include/TreeNode.zig | 21 +++ codes/zig/include/include.zig | 6 +- 6 files changed, 292 insertions(+), 44 deletions(-) create mode 100644 codes/zig/chapter_computational_complexity/space_complexity.zig create mode 100644 codes/zig/include/ListNode.zig create mode 100644 codes/zig/include/TreeNode.zig diff --git a/codes/zig/build.zig b/codes/zig/build.zig index b04b3fd18..e72ca4a53 100644 --- a/codes/zig/build.zig +++ b/codes/zig/build.zig @@ -1,46 +1,67 @@ +// File: build.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + const std = @import("std"); -// zig version 0.10.0 +// Zig Version: 0.10.0 +// Zig Codes Build Command: zig build pub fn build(b: *std.build.Builder) void { const target = b.standardTargetOptions(.{}); const mode = b.standardReleaseOptions(); - // "chapter_computational_complexity/time_complexity.zig" - // Run Command: zig build run_time_complexity - const exe_time_complexity = b.addExecutable("time_complexity", "chapter_computational_complexity/time_complexity.zig"); - exe_time_complexity.addPackagePath("include", "include/include.zig"); - exe_time_complexity.setTarget(target); - exe_time_complexity.setBuildMode(mode); - exe_time_complexity.install(); - const run_cmd_time_complexity = exe_time_complexity.run(); - run_cmd_time_complexity.step.dependOn(b.getInstallStep()); - if (b.args) |args| run_cmd_time_complexity.addArgs(args); - const run_step_time_complexity = b.step("run_time_complexity", "Run time_complexity"); - run_step_time_complexity.dependOn(&run_cmd_time_complexity.step); + // Section: "Time Complexity" + // File: "chapter_computational_complexity/time_complexity.zig" + // Run Command: zig build run_time_complexity + const exe_time_complexity = b.addExecutable("time_complexity", "chapter_computational_complexity/time_complexity.zig"); + exe_time_complexity.addPackagePath("include", "include/include.zig"); + exe_time_complexity.setTarget(target); + exe_time_complexity.setBuildMode(mode); + exe_time_complexity.install(); + const run_cmd_time_complexity = exe_time_complexity.run(); + run_cmd_time_complexity.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_time_complexity.addArgs(args); + const run_step_time_complexity = b.step("run_time_complexity", "Run time_complexity"); + run_step_time_complexity.dependOn(&run_cmd_time_complexity.step); - // "chapter_computational_complexity/worst_best_time_complexity.zig" - // Run Command: zig build run_worst_best_time_complexity - const exe_worst_best_time_complexity = b.addExecutable("worst_best_time_complexity", "chapter_computational_complexity/worst_best_time_complexity.zig"); - exe_worst_best_time_complexity.addPackagePath("include", "include/include.zig"); - exe_worst_best_time_complexity.setTarget(target); - exe_worst_best_time_complexity.setBuildMode(mode); - exe_worst_best_time_complexity.install(); - const run_cmd_worst_best_time_complexity = exe_worst_best_time_complexity.run(); - run_cmd_worst_best_time_complexity.step.dependOn(b.getInstallStep()); - if (b.args) |args| run_cmd_worst_best_time_complexity.addArgs(args); - const run_step_worst_best_time_complexity = b.step("run_worst_best_time_complexity", "Run worst_best_time_complexity"); - run_step_worst_best_time_complexity.dependOn(&run_cmd_worst_best_time_complexity.step); + // File: "chapter_computational_complexity/worst_best_time_complexity.zig" + // Run Command: zig build run_worst_best_time_complexity + const exe_worst_best_time_complexity = b.addExecutable("worst_best_time_complexity", "chapter_computational_complexity/worst_best_time_complexity.zig"); + exe_worst_best_time_complexity.addPackagePath("include", "include/include.zig"); + exe_worst_best_time_complexity.setTarget(target); + exe_worst_best_time_complexity.setBuildMode(mode); + exe_worst_best_time_complexity.install(); + const run_cmd_worst_best_time_complexity = exe_worst_best_time_complexity.run(); + run_cmd_worst_best_time_complexity.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_worst_best_time_complexity.addArgs(args); + const run_step_worst_best_time_complexity = b.step("run_worst_best_time_complexity", "Run worst_best_time_complexity"); + run_step_worst_best_time_complexity.dependOn(&run_cmd_worst_best_time_complexity.step); - // "chapter_computational_complexity/leetcode_two_sum.zig" - // Run Command: zig build run_leetcode_two_sum - const exe_leetcode_two_sum = b.addExecutable("leetcode_two_sum", "chapter_computational_complexity/leetcode_two_sum.zig"); - exe_leetcode_two_sum.addPackagePath("include", "include/include.zig"); - exe_leetcode_two_sum.setTarget(target); - exe_leetcode_two_sum.setBuildMode(mode); - exe_leetcode_two_sum.install(); - const run_cmd_leetcode_two_sum = exe_leetcode_two_sum.run(); - run_cmd_leetcode_two_sum.step.dependOn(b.getInstallStep()); - if (b.args) |args| run_cmd_leetcode_two_sum.addArgs(args); - const run_step_leetcode_two_sum = b.step("run_leetcode_two_sum", "Run leetcode_two_sum"); - run_step_leetcode_two_sum.dependOn(&run_cmd_leetcode_two_sum.step); + // Section: "Space Complexity" + // File: "chapter_computational_complexity/space_complexity.zig" + // Run Command: zig build run_space_complexity + const exe_space_complexity = b.addExecutable("space_complexity", "chapter_computational_complexity/space_complexity.zig"); + exe_space_complexity.addPackagePath("include", "include/include.zig"); + exe_space_complexity.setTarget(target); + exe_space_complexity.setBuildMode(mode); + exe_space_complexity.install(); + const run_cmd_space_complexity = exe_space_complexity.run(); + run_cmd_space_complexity.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_space_complexity.addArgs(args); + const run_step_space_complexity = b.step("run_space_complexity", "Run space_complexity"); + run_step_space_complexity.dependOn(&run_cmd_space_complexity.step); + + // Section: "Space Time Tradeoff" + // File: "chapter_computational_complexity/leetcode_two_sum.zig" + // Run Command: zig build run_leetcode_two_sum + const exe_leetcode_two_sum = b.addExecutable("leetcode_two_sum", "chapter_computational_complexity/leetcode_two_sum.zig"); + exe_leetcode_two_sum.addPackagePath("include", "include/include.zig"); + exe_leetcode_two_sum.setTarget(target); + exe_leetcode_two_sum.setBuildMode(mode); + exe_leetcode_two_sum.install(); + const run_cmd_leetcode_two_sum = exe_leetcode_two_sum.run(); + run_cmd_leetcode_two_sum.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_leetcode_two_sum.addArgs(args); + const run_step_leetcode_two_sum = b.step("run_leetcode_two_sum", "Run leetcode_two_sum"); + run_step_leetcode_two_sum.dependOn(&run_cmd_leetcode_two_sum.step); } diff --git a/codes/zig/chapter_computational_complexity/space_complexity.zig b/codes/zig/chapter_computational_complexity/space_complexity.zig new file mode 100644 index 000000000..9798821a5 --- /dev/null +++ b/codes/zig/chapter_computational_complexity/space_complexity.zig @@ -0,0 +1,125 @@ +// File: space_complexity.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 函数 +fn function() i32 { + // do something + return 0; +} + +// 常数阶 +fn constant(n: i32) void { + // 常量、变量、对象占用 O(1) 空间 + const a: i32 = 0; + var b: i32 = 0; + var nums = [_]i32{0}**10000; + var node = inc.ListNode(i32){.val = 0}; + var i: i32 = 0; + // 循环中的变量占用 O(1) 空间 + while (i < n) : (i += 1) { + var c: i32 = 0; + _ = c; + } + // 循环中的函数占用 O(1) 空间 + i = 0; + while (i < n) : (i += 1) { + _ = function(); + } + _ = a; + _ = b; + _ = nums; + _ = node; +} + +// 线性阶 +fn linear(comptime n: i32) !void { + // 长度为 n 的数组占用 O(n) 空间 + var nums = [_]i32{0}**n; + // 长度为 n 的列表占用 O(n) 空间 + var nodes = std.ArrayList(i32).init(std.heap.page_allocator); + defer nodes.deinit(); + var i: i32 = 0; + while (i < n) : (i += 1) { + try nodes.append(i); + } + // 长度为 n 的哈希表占用 O(n) 空间 + var map = std.AutoArrayHashMap(i32, []const u8).init(std.heap.page_allocator); + defer map.deinit(); + var j: i32 = 0; + while (j < n) : (j += 1) { + const string = try std.fmt.allocPrint(std.heap.page_allocator, "{d}", .{j}); + defer std.heap.page_allocator.free(string); + try map.put(i, string); + } + _ = nums; +} + +// 线性阶(递归实现) +fn linearRecur(comptime n: i32) void { + std.debug.print("递归 n = {}\n", .{n}); + if (n == 1) return; + linearRecur(n - 1); +} + +// 平方阶 +fn quadratic(n: i32) !void { + // 二维列表占用 O(n^2) 空间 + var nodes = std.ArrayList(std.ArrayList(i32)).init(std.heap.page_allocator); + defer nodes.deinit(); + var i: i32 = 0; + while (i < n) : (i += 1) { + var tmp = std.ArrayList(i32).init(std.heap.page_allocator); + defer tmp.deinit(); + var j: i32 = 0; + while (j < n) : (j += 1) { + try tmp.append(0); + } + try nodes.append(tmp); + } +} + +// 平方阶(递归实现) +fn quadraticRecur(comptime n: i32) i32 { + if (n <= 0) return 0; + var nums = [_]i32{0}**n; + std.debug.print("递归 n = {} 中的 nums 长度 = {}\n", .{n, nums.len}); + return quadraticRecur(n - 1); +} + +// 指数阶(建立满二叉树) +fn buildTree(mem_allocator: std.mem.Allocator, n: i32) !?*inc.TreeNode(i32) { + if (n == 0) return null; + const root = try mem_allocator.create(inc.TreeNode(i32)); + root.init(0); + root.left = try buildTree(mem_allocator, n - 1); + root.right = try buildTree(mem_allocator, n - 1); + return root; +} + +// Driver Code +pub fn main() !void { + const n: i32 = 5; + // 常数阶 + constant(n); + // 线性阶 + try linear(n); + linearRecur(n); + // 平方阶 + try quadratic(n); + _ = quadraticRecur(n); + // 指数阶 + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer mem_arena.deinit(); + var root = blk_root: { + const mem_allocator = mem_arena.allocator(); + break :blk_root try buildTree(mem_allocator, n); + }; + try inc.PrintUtil.printTree(root, null, false); + + const getchar = try std.io.getStdIn().reader().readByte(); + _ = getchar; +} \ No newline at end of file diff --git a/codes/zig/include/ListNode.zig b/codes/zig/include/ListNode.zig new file mode 100644 index 000000000..8ce01d3ed --- /dev/null +++ b/codes/zig/include/ListNode.zig @@ -0,0 +1,20 @@ +// File: ListNode.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); + +// Definition for a singly-linked list node +pub fn ListNode(comptime T: type) type { + return struct { + const Self = @This(); + + val: T = 0, + next: ?*Self = null, + + // Initialize a list node with specific value + pub fn init(self: *Self, x: i32) void { + self.val = x; + } + }; +} \ No newline at end of file diff --git a/codes/zig/include/PrintUtil.zig b/codes/zig/include/PrintUtil.zig index 5adac2a6e..8a7dca7af 100644 --- a/codes/zig/include/PrintUtil.zig +++ b/codes/zig/include/PrintUtil.zig @@ -1,13 +1,72 @@ -// File: TreeNode.zig +// File: PrintUtil.zig // Created Time: 2023-01-07 // Author: sjinzh (sjinzh@gmail.com) const std = @import("std"); +const ListNode = @import("ListNode.zig").ListNode; +const TreeNode = @import("TreeNode.zig").TreeNode; -// Print an Array +// Print an array pub fn printArray(comptime T: type, nums: []T) void { std.debug.print("[", .{}); - for (nums) |num, j| { - std.debug.print("{}{s}", .{num, if (j == nums.len-1) "]\n" else ", " }); - } + if (nums.len > 0) { + for (nums) |num, j| { + std.debug.print("{}{s}", .{num, if (j == nums.len-1) "]\n" else ", " }); + } + } else { + std.debug.print("]", .{}); + std.debug.print("\n", .{}); + } } + +// This tree printer is borrowed from TECHIE DELIGHT +// https://www.techiedelight.com/c-program-print-binary-tree/ +const Trunk = struct { + prev: ?*Trunk = null, + str: []const u8 = undefined, + + pub fn init(self: *Trunk, prev: ?*Trunk, str: []const u8) void { + self.prev = prev; + self.str = str; + } +}; + +// Helper function to print branches of the binary tree +pub fn showTrunks(p: ?*Trunk) void { + if (p == null) return; + showTrunks(p.?.prev); + std.debug.print("{s}", .{p.?.str}); +} + +// The interface of the tree printer +// Print a binary tree +pub fn printTree(root: ?*TreeNode(i32), prev: ?*Trunk, isLeft: bool) !void { + if (root == null) { + return; + } + + var prev_str = " "; + var trunk = Trunk{.prev = prev, .str = prev_str}; + + try printTree(root.?.right, &trunk, true); + + if (prev == null) { + trunk.str = "———"; + } else if (isLeft) { + trunk.str = "/———"; + prev_str = " |"; + } else { + trunk.str = "\\———"; + prev.?.str = prev_str; + } + + showTrunks(&trunk); + std.debug.print(" {}\n", .{root.?.val}); + + if (prev) |_| { + prev.?.str = prev_str; + } + trunk.str = " |"; + + try printTree(root.?.left, &trunk, false); +} \ No newline at end of file diff --git a/codes/zig/include/TreeNode.zig b/codes/zig/include/TreeNode.zig new file mode 100644 index 000000000..9bd51ba3d --- /dev/null +++ b/codes/zig/include/TreeNode.zig @@ -0,0 +1,21 @@ +// File: TreeNode.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); + +// Definition for a binary tree node +pub fn TreeNode(comptime T: type) type { + return struct { + const Self = @This(); + + val: T = undefined, + left: ?*Self = null, + right: ?*Self = null, + + // Initialize a tree node with specific value + pub fn init(self: *Self, x: i32) void { + self.val = x; + } + }; +} \ No newline at end of file diff --git a/codes/zig/include/include.zig b/codes/zig/include/include.zig index b4f347821..20dfca90f 100644 --- a/codes/zig/include/include.zig +++ b/codes/zig/include/include.zig @@ -1,5 +1,7 @@ // File: include.zig -// Created Time: 2023-01-04 +// Created Time: 2023-01-07 // Author: sjinzh (sjinzh@gmail.com) -pub const PrintUtil = @import("PrintUtil.zig"); \ No newline at end of file +pub const PrintUtil = @import("PrintUtil.zig"); +pub const ListNode = @import("ListNode.zig").ListNode; +pub const TreeNode = @import("TreeNode.zig").TreeNode; \ No newline at end of file From aaa2ff29f93f6ea1cae4021241344276f006e854 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Mon, 9 Jan 2023 22:39:30 +0800 Subject: [PATCH 141/158] Fix all the ** (bolded symbols). --- docs/chapter_array_and_linkedlist/array.md | 6 ++-- docs/chapter_array_and_linkedlist/list.md | 6 ++-- .../performance_evaluation.md | 6 ++-- .../space_complexity.md | 2 +- .../classification_of_data_structure.md | 10 +++--- .../chapter_data_structure/data_and_memory.md | 2 +- docs/chapter_hashing/hash_collision.md | 14 ++++---- docs/chapter_hashing/hash_map.md | 8 ++--- docs/chapter_preface/about_the_book.md | 6 ++-- docs/chapter_preface/suggestions.md | 2 +- docs/chapter_searching/binary_search.md | 6 ++-- docs/chapter_searching/hashing_search.md | 4 +-- docs/chapter_searching/linear_search.md | 4 +-- docs/chapter_sorting/bubble_sort.md | 10 +++--- docs/chapter_sorting/insertion_sort.md | 12 +++---- docs/chapter_sorting/intro_to_sort.md | 4 +-- docs/chapter_sorting/merge_sort.md | 22 ++++++------- docs/chapter_sorting/quick_sort.md | 32 +++++++++---------- docs/chapter_tree/avl_tree.md | 4 +-- docs/chapter_tree/binary_search_tree.md | 26 +++++++-------- 20 files changed, 93 insertions(+), 93 deletions(-) diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 2cf096137..2251362d0 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -356,9 +356,9 @@ elementAddr = firtstElementAddr + elementLength * elementIndex **数组中插入或删除元素效率低下**。假设我们想要在数组中间某位置插入一个元素,由于数组元素在内存中是“紧挨着的”,它们之间没有空间再放任何数据。因此,我们不得不将此索引之后的所有元素都向后移动一位,然后再把元素赋值给该索引。删除元素也是类似,需要把此索引之后的元素都向前移动一位。总体看有以下缺点: -- **时间复杂度高:** 数组的插入和删除的平均时间复杂度均为 $O(N)$ ,其中 $N$ 为数组长度。 -- **丢失元素:** 由于数组的长度不可变,因此在插入元素后,超出数组长度范围的元素会被丢失。 -- **内存浪费:** 我们一般会初始化一个比较长的数组,只用前面一部分,这样在插入数据时,丢失的末尾元素都是我们不关心的,但这样做同时也会造成内存空间的浪费。 +- **时间复杂度高**:数组的插入和删除的平均时间复杂度均为 $O(N)$ ,其中 $N$ 为数组长度。 +- **丢失元素**:由于数组的长度不可变,因此在插入元素后,超出数组长度范围的元素会被丢失。 +- **内存浪费**:我们一般会初始化一个比较长的数组,只用前面一部分,这样在插入数据时,丢失的末尾元素都是我们不关心的,但这样做同时也会造成内存空间的浪费。 ![array_insert_remove_element](array.assets/array_insert_remove_element.png) diff --git a/docs/chapter_array_and_linkedlist/list.md b/docs/chapter_array_and_linkedlist/list.md index ca4774a04..a02ede876 100644 --- a/docs/chapter_array_and_linkedlist/list.md +++ b/docs/chapter_array_and_linkedlist/list.md @@ -599,9 +599,9 @@ comments: true 为了帮助加深对列表的理解,我们在此提供一个列表的简易版本的实现。需要关注三个核心点: -- **初始容量:** 选取一个合理的数组的初始容量 `initialCapacity` 。在本示例中,我们选择 10 作为初始容量。 -- **数量记录:** 需要声明一个变量 `size` ,用来记录列表当前有多少个元素,并随着元素插入与删除实时更新。根据此变量,可以定位列表的尾部,以及判断是否需要扩容。 -- **扩容机制:** 插入元素有可能导致超出列表容量,此时需要扩容列表,方法是建立一个更大的数组来替换当前数组。需要给定一个扩容倍数 `extendRatio` ,在本示例中,我们规定每次将数组扩容至之前的 2 倍。 +- **初始容量**:选取一个合理的数组的初始容量 `initialCapacity` 。在本示例中,我们选择 10 作为初始容量。 +- **数量记录**:需要声明一个变量 `size` ,用来记录列表当前有多少个元素,并随着元素插入与删除实时更新。根据此变量,可以定位列表的尾部,以及判断是否需要扩容。 +- **扩容机制**:插入元素有可能导致超出列表容量,此时需要扩容列表,方法是建立一个更大的数组来替换当前数组。需要给定一个扩容倍数 `extendRatio` ,在本示例中,我们规定每次将数组扩容至之前的 2 倍。 本示例是为了帮助读者对如何实现列表产生直观的认识。实际编程语言中,列表的实现远比以下代码复杂且标准,感兴趣的读者可以查阅源码学习。 diff --git a/docs/chapter_computational_complexity/performance_evaluation.md b/docs/chapter_computational_complexity/performance_evaluation.md index f75b4d8d4..9d6fd6628 100644 --- a/docs/chapter_computational_complexity/performance_evaluation.md +++ b/docs/chapter_computational_complexity/performance_evaluation.md @@ -13,8 +13,8 @@ comments: true 换言之,在可以解决问题的前提下,算法效率则是主要评价维度,包括: -- **时间效率** ,即算法的运行速度的快慢。 -- **空间效率** ,即算法占用的内存空间大小。 +- **时间效率**,即算法的运行速度的快慢。 +- **空间效率**,即算法占用的内存空间大小。 数据结构与算法追求“运行速度快、占用内存少”,而如何去评价算法效率则是非常重要的问题,因为只有知道如何评价算法,才能去做算法之间的对比分析,以及优化算法设计。 @@ -32,7 +32,7 @@ comments: true 既然实际测试具有很大的局限性,那么我们是否可以仅通过一些计算,就获知算法的效率水平呢?答案是肯定的,我们将此估算方法称为「复杂度分析 Complexity Analysis」或「渐近复杂度分析 Asymptotic Complexity Analysis」。 -**复杂度分析评估随着输入数据量的增长,算法的运行时间和占用空间的增长趋势** 。根据时间和空间两方面,复杂度可分为「时间复杂度 Time Complexity」和「空间复杂度 Space Complexity」。 +**复杂度分析评估随着输入数据量的增长,算法的运行时间和占用空间的增长趋势**。根据时间和空间两方面,复杂度可分为「时间复杂度 Time Complexity」和「空间复杂度 Space Complexity」。 **复杂度分析克服了实际测试方法的弊端**。一是独立于测试环境,分析结果适用于所有运行平台。二是可以体现不同数据量下的算法效率,尤其是可以反映大数据量下的算法性能。 diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index 6686aac50..e594bec8d 100644 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -4,7 +4,7 @@ comments: true # 空间复杂度 -「空间复杂度 Space Complexity」统计 **算法使用内存空间随着数据量变大时的增长趋势** 。这个概念与时间复杂度很类似。 +「空间复杂度 Space Complexity」统计 **算法使用内存空间随着数据量变大时的增长趋势**。这个概念与时间复杂度很类似。 ## 算法相关空间 diff --git a/docs/chapter_data_structure/classification_of_data_structure.md b/docs/chapter_data_structure/classification_of_data_structure.md index 749542eb9..202af50a2 100644 --- a/docs/chapter_data_structure/classification_of_data_structure.md +++ b/docs/chapter_data_structure/classification_of_data_structure.md @@ -12,8 +12,8 @@ comments: true 我们一般将逻辑结构分为「线性」和「非线性」两种。“线性”这个概念很直观,即表明数据在逻辑关系上是排成一条线的;而如果数据之间的逻辑关系是非线形的(例如是网状或树状的),那么就是非线性数据结构。 -- **线性数据结构:** 数组、链表、栈、队列、哈希表; -- **非线性数据结构:** 树、图、堆、哈希表; +- **线性数据结构**:数组、链表、栈、队列、哈希表; +- **非线性数据结构**:树、图、堆、哈希表; ![classification_logic_structure](classification_of_data_structure.assets/classification_logic_structure.png) @@ -25,7 +25,7 @@ comments: true 若感到阅读困难,建议先看完下个章节「数组与链表」,再回过头来理解物理结构的含义。 -**「物理结构」反映了数据在计算机内存中的存储方式**。从本质上看,分别是 **数组的连续空间存储** 和 **链表的离散空间存储** 。物理结构从底层上决定了数据的访问、更新、增删等操作方法,在时间效率和空间效率方面呈现出此消彼长的特性。 +**「物理结构」反映了数据在计算机内存中的存储方式**。从本质上看,分别是 **数组的连续空间存储** 和 **链表的离散空间存储**。物理结构从底层上决定了数据的访问、更新、增删等操作方法,在时间效率和空间效率方面呈现出此消彼长的特性。 ![classification_phisical_structure](classification_of_data_structure.assets/classification_phisical_structure.png) @@ -33,8 +33,8 @@ comments: true **所有数据结构都是基于数组、或链表、或两者组合实现的**。例如栈和队列,既可以使用数组实现、也可以使用链表实现,而例如哈希表,其实现同时包含了数组和链表。 -- **基于数组可实现:** 栈、队列、堆、哈希表、矩阵、张量(维度 $\geq 3$ 的数组)等; -- **基于链表可实现:** 栈、队列、堆、哈希表、树、图等; +- **基于数组可实现**:栈、队列、堆、哈希表、矩阵、张量(维度 $\geq 3$ 的数组)等; +- **基于链表可实现**:栈、队列、堆、哈希表、树、图等; 基于数组实现的数据结构也被称为「静态数据结构」,这意味着该数据结构在在被初始化后,长度不可变。相反地,基于链表实现的数据结构被称为「动态数据结构」,该数据结构在被初始化后,我们也可以在程序运行中修改其长度。 diff --git a/docs/chapter_data_structure/data_and_memory.md b/docs/chapter_data_structure/data_and_memory.md index 59cb74e45..93147fb65 100644 --- a/docs/chapter_data_structure/data_and_memory.md +++ b/docs/chapter_data_structure/data_and_memory.md @@ -42,7 +42,7 @@ comments: true **「基本数据类型」与「数据结构」之间的联系与区别** -我们知道,数据结构是在计算机中 **组织与存储数据的方式** ,它的主语是“结构”,而不是“数据”。比如,我们想要表示“一排数字”,自然应该使用「数组」这个数据结构。数组的存储方式使之可以表示数字的相邻关系、先后关系等一系列我们需要的信息,但至于其中存储的是整数 int ,还是小数 float ,或是字符 char ,**则与所谓的数据的结构无关了**。 +我们知道,数据结构是在计算机中 **组织与存储数据的方式**,它的主语是“结构”,而不是“数据”。比如,我们想要表示“一排数字”,自然应该使用「数组」这个数据结构。数组的存储方式使之可以表示数字的相邻关系、先后关系等一系列我们需要的信息,但至于其中存储的是整数 int ,还是小数 float ,或是字符 char ,**则与所谓的数据的结构无关了**。 === "Java" diff --git a/docs/chapter_hashing/hash_collision.md b/docs/chapter_hashing/hash_collision.md index 05dc20881..45a529970 100644 --- a/docs/chapter_hashing/hash_collision.md +++ b/docs/chapter_hashing/hash_collision.md @@ -24,9 +24,9 @@ comments: true 在原始哈希表中,一个桶地址只能存储一个元素(即键值对)。**考虑将桶地址内的单个元素转变成一个链表,将所有冲突元素都存储在一个链表中**,此时哈希表操作方法为: -- **查询元素:** 先将 key 输入到哈希函数得到桶地址(即访问链表头部),再遍历链表来确定对应的 value 。 -- **添加元素:** 先通过哈希函数访问链表头部,再将元素直接添加到链表头部即可。 -- **删除元素:** 同样先访问链表头部,再遍历链表查找对应元素,删除之即可。 +- **查询元素**:先将 key 输入到哈希函数得到桶地址(即访问链表头部),再遍历链表来确定对应的 value 。 +- **添加元素**:先通过哈希函数访问链表头部,再将元素直接添加到链表头部即可。 +- **删除元素**:同样先访问链表头部,再遍历链表查找对应元素,删除之即可。 (图) @@ -46,9 +46,9 @@ comments: true 「线性探测」使用固定步长的线性查找来解决哈希冲突。 -**插入元素:** 如果出现哈希冲突,则从冲突位置向后线性遍历(步长一般取 1 ),直到找到一个空位,则将元素插入到该空位中。 +**插入元素**:如果出现哈希冲突,则从冲突位置向后线性遍历(步长一般取 1 ),直到找到一个空位,则将元素插入到该空位中。 -**查找元素:** 若出现哈希冲突,则使用相同步长执行线性查找,会遇到两种情况: +**查找元素**:若出现哈希冲突,则使用相同步长执行线性查找,会遇到两种情况: 1. 找到对应元素,返回 value 即可; 2. 若遇到空位,则说明查找键值对不在哈希表中; @@ -64,9 +64,9 @@ comments: true 顾名思义,「多次哈希」的思路是基于多个哈希函数 $f_1(x)$ , $f_2(x)$ , $f_3(x)$ , $\cdots$ 进行探测。 -**插入元素:** 若哈希函数 $f_1(x)$ 出现冲突,则尝试 $f_2(x)$ ,以此类推……直到找到空位后插入元素。 +**插入元素**:若哈希函数 $f_1(x)$ 出现冲突,则尝试 $f_2(x)$ ,以此类推……直到找到空位后插入元素。 -**查找元素:** 以相同的哈希函数顺序查找,存在两种情况: +**查找元素**:以相同的哈希函数顺序查找,存在两种情况: 1. 找到目标元素,则返回之; 2. 到空位或已尝试所有哈希函数,说明哈希表中无此元素; diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index 2d8a66a5d..cbb4ba049 100644 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -16,10 +16,10 @@ comments: true 除了哈希表之外,还可以使用以下数据结构来实现上述查询功能: -1. **无序数组:** 每个元素为 `[学号, 姓名]` ; -2. **有序数组:** 将 `1.` 中的数组按照学号从小到大排序; -3. **链表:** 每个结点的值为 `[学号, 姓名]` ; -4. **二叉搜索树:** 每个结点的值为 `[学号, 姓名]` ,根据学号大小来构建树; +1. **无序数组**:每个元素为 `[学号, 姓名]` ; +2. **有序数组**:将 `1.` 中的数组按照学号从小到大排序; +3. **链表**:每个结点的值为 `[学号, 姓名]` ; +4. **二叉搜索树**:每个结点的值为 `[学号, 姓名]` ,根据学号大小来构建树; 使用上述方法,各项操作的时间复杂度如下表所示(在此不做赘述,详解可见 [二叉搜索树章节](https://www.hello-algo.com/chapter_tree/binary_search_tree/#_6))。无论是查找元素、还是增删元素,哈希表的时间复杂度都是 $O(1)$ ,全面胜出! diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index 2fba23502..c3209280c 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -44,13 +44,13 @@ comments: true 首先介绍数据结构与算法的评价维度、算法效率的评估方法,引出了计算复杂度概念。 -接下来,从 **函数渐近上界** 入手,分别介绍了 **时间复杂度** 和 **空间复杂度** ,包括推算方法、常见类型、示例等。同时,剖析了 **最差、最佳、平均** 时间复杂度的联系与区别。 +接下来,从 **函数渐近上界** 入手,分别介绍了 **时间复杂度** 和 **空间复杂度**,包括推算方法、常见类型、示例等。同时,剖析了 **最差、最佳、平均** 时间复杂度的联系与区别。 ### 数据结构 首先介绍了常用的 **基本数据类型** 、以及它们是如何在内存中存储的。 -接下来,介绍了两种 **数据结构分类方法** ,包括逻辑结构与物理结构。 +接下来,介绍了两种 **数据结构分类方法**,包括逻辑结构与物理结构。 后续展开介绍了 **数组、链表、栈、队列、散列表、树、堆、图** 等数据结构,关心以下内容: @@ -84,7 +84,7 @@ comments: true - 标题后标注 * 符号的是选读章节,如果你的时间有限,可以先跳过这些章节。 - 文章中的重要名词会用「」符号标注,例如「数组 Array」。名词混淆会导致不必要的歧义,因此最好可以记住这类名词(包括中文和英文),以便后续阅读文献时使用。 -- 重点内容、总起句、总结句会被 **加粗** ,此类文字值得特别关注。 +- 重点内容、总起句、总结句会被 **加粗**,此类文字值得特别关注。 - 专有名词和有特指含义的词句会使用 “ ” 标注,以避免歧义。 - 在工程应用中,每种语言都有注释规范;而本书放弃了一部分的注释规范性,以换取更加紧凑的内容排版。注释主要分为三种类型:标题注释、内容注释、多行注释。 diff --git a/docs/chapter_preface/suggestions.md b/docs/chapter_preface/suggestions.md index d4dbd24bc..b2e18905f 100644 --- a/docs/chapter_preface/suggestions.md +++ b/docs/chapter_preface/suggestions.md @@ -32,7 +32,7 @@ git clone https://github.com/krahets/hello-algo.git ### 运行源代码 -本书提供配套 Java, C++, Python 代码仓(后续可能拓展支持语言)。书中的代码栏上若标有 `*.java` , `*.cpp` , `*.py` ,则可在仓库 codes 文件夹中找到对应的 **代码源文件** 。 +本书提供配套 Java, C++, Python 代码仓(后续可能拓展支持语言)。书中的代码栏上若标有 `*.java` , `*.cpp` , `*.py` ,则可在仓库 codes 文件夹中找到对应的 **代码源文件**。 ![code_md_to_repo](suggestions.assets/code_md_to_repo.png) diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index 466e76658..2bd2d4396 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -9,7 +9,7 @@ comments: true 使用二分查找有两个前置条件: - **要求输入数据是有序的**,这样才能通过判断大小关系来排除一半的搜索区间; -- **二分查找仅适用于数组** ,而在链表中使用效率很低,因为其在循环中需要跳跃式(非连续地)访问元素。 +- **二分查找仅适用于数组**,而在链表中使用效率很低,因为其在循环中需要跳跃式(非连续地)访问元素。 ## 算法实现 @@ -480,9 +480,9 @@ $$ ## 复杂度分析 -**时间复杂度 $O(\log n)$ :** 其中 $n$ 为数组或链表长度;每轮排除一半的区间,因此循环轮数为 $\log_2 n$ ,使用 $O(\log n)$ 时间。 +**时间复杂度 $O(\log n)$** :其中 $n$ 为数组或链表长度;每轮排除一半的区间,因此循环轮数为 $\log_2 n$ ,使用 $O(\log n)$ 时间。 -**空间复杂度 $O(1)$ :** 指针 `i` , `j` 使用常数大小空间。 +**空间复杂度 $O(1)$** :指针 `i` , `j` 使用常数大小空间。 ## 优点与缺点 diff --git a/docs/chapter_searching/hashing_search.md b/docs/chapter_searching/hashing_search.md index 12746d2ef..076457303 100644 --- a/docs/chapter_searching/hashing_search.md +++ b/docs/chapter_searching/hashing_search.md @@ -193,9 +193,9 @@ comments: true ## 复杂度分析 -**时间复杂度:** $O(1)$ ,哈希表的查找操作使用 $O(1)$ 时间。 +**时间复杂度 $O(1)$** :哈希表的查找操作使用 $O(1)$ 时间。 -**空间复杂度:** $O(n)$ ,其中 $n$ 为数组或链表长度。 +**空间复杂度 $O(n)$** :其中 $n$ 为数组或链表长度。 ## 优点与缺点 diff --git a/docs/chapter_searching/linear_search.md b/docs/chapter_searching/linear_search.md index be98c013d..d9371ab2c 100644 --- a/docs/chapter_searching/linear_search.md +++ b/docs/chapter_searching/linear_search.md @@ -252,9 +252,9 @@ comments: true ## 复杂度分析 -**时间复杂度 $O(n)$ :** 其中 $n$ 为数组或链表长度。 +**时间复杂度 $O(n)$** :其中 $n$ 为数组或链表长度。 -**空间复杂度 $O(1)$ :** 无需使用额外空间。 +**空间复杂度 $O(1)$** :无需使用额外空间。 ## 优点与缺点 diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index 1dbc56475..e3e394b20 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -220,15 +220,15 @@ comments: true ## 算法特性 -**时间复杂度 $O(n^2)$ :** 各轮「冒泡」遍历的数组长度为 $n - 1$ , $n - 2$ , $\cdots$ , $2$ , $1$ 次,求和为 $\frac{(n - 1) n}{2}$ ,因此使用 $O(n^2)$ 时间。 +**时间复杂度 $O(n^2)$** :各轮「冒泡」遍历的数组长度为 $n - 1$ , $n - 2$ , $\cdots$ , $2$ , $1$ 次,求和为 $\frac{(n - 1) n}{2}$ ,因此使用 $O(n^2)$ 时间。 -**空间复杂度 $O(1)$ :** 指针 $i$ , $j$ 使用常数大小的额外空间。 +**空间复杂度 $O(1)$** :指针 $i$ , $j$ 使用常数大小的额外空间。 -**原地排序:** 指针变量仅使用常数大小额外空间。 +**原地排序**:指针变量仅使用常数大小额外空间。 -**稳定排序:** 不交换相等元素。 +**稳定排序**:不交换相等元素。 -**自适排序:** 引入 `flag` 优化后(见下文),最佳时间复杂度为 $O(N)$ 。 +**自适排序**:引入 `flag` 优化后(见下文),最佳时间复杂度为 $O(N)$ 。 ## 效率优化 diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index 679182f42..05ee7293e 100644 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -183,15 +183,15 @@ comments: true ## 算法特性 -**时间复杂度 $O(n^2)$ :** 最差情况下,各轮插入操作循环 $n - 1$ , $n-2$ , $\cdots$ , $2$ , $1$ 次,求和为 $\frac{(n - 1) n}{2}$ ,使用 $O(n^2)$ 时间。 +**时间复杂度 $O(n^2)$** :最差情况下,各轮插入操作循环 $n - 1$ , $n-2$ , $\cdots$ , $2$ , $1$ 次,求和为 $\frac{(n - 1) n}{2}$ ,使用 $O(n^2)$ 时间。 -**空间复杂度 $O(1)$ :** 指针 $i$ , $j$ 使用常数大小的额外空间。 +**空间复杂度 $O(1)$** :指针 $i$ , $j$ 使用常数大小的额外空间。 -**原地排序:** 指针变量仅使用常数大小额外空间。 +**原地排序**:指针变量仅使用常数大小额外空间。 -**稳定排序:** 不交换相等元素。 +**稳定排序**:不交换相等元素。 -**自适应排序:** 最佳情况下,时间复杂度为 $O(n)$ 。 +**自适应排序**:最佳情况下,时间复杂度为 $O(n)$ 。 ## 插入排序 vs 冒泡排序 @@ -199,7 +199,7 @@ comments: true 虽然「插入排序」和「冒泡排序」的时间复杂度皆为 $O(n^2)$ ,但实际运行速度却有很大差别,这是为什么呢? -回顾复杂度分析,两个方法的循环次数都是 $\frac{(n - 1) n}{2}$ 。但不同的是,「冒泡操作」是在做 **元素交换** ,需要借助一个临时变量实现,共 3 个单元操作;而「插入操作」是在做 **赋值** ,只需 1 个单元操作;因此,可以粗略估计出冒泡排序的计算开销约为插入排序的 3 倍。 +回顾复杂度分析,两个方法的循环次数都是 $\frac{(n - 1) n}{2}$ 。但不同的是,「冒泡操作」是在做 **元素交换**,需要借助一个临时变量实现,共 3 个单元操作;而「插入操作」是在做 **赋值**,只需 1 个单元操作;因此,可以粗略估计出冒泡排序的计算开销约为插入排序的 3 倍。 插入排序运行速度快,并且具有原地、稳定、自适应的优点,因此很受欢迎。实际上,包括 Java 在内的许多编程语言的排序库函数的实现都用到了插入排序。库函数的大致思路: diff --git a/docs/chapter_sorting/intro_to_sort.md b/docs/chapter_sorting/intro_to_sort.md index a3cb071dc..c4d50c535 100644 --- a/docs/chapter_sorting/intro_to_sort.md +++ b/docs/chapter_sorting/intro_to_sort.md @@ -7,7 +7,7 @@ comments: true 「排序算法 Sorting Algorithm」使得列表中的所有元素按照从小到大的顺序排列。 - 待排序的列表的 **元素类型** 可以是整数、浮点数、字符、或字符串; -- 排序算法可以根据需要设定 **判断规则** ,例如数字大小、字符 ASCII 码顺序、自定义规则; +- 排序算法可以根据需要设定 **判断规则**,例如数字大小、字符 ASCII 码顺序、自定义规则; ![sorting_examples](intro_to_sort.assets/sorting_examples.png) @@ -55,7 +55,7 @@ comments: true - 「自适应排序」的时间复杂度受输入数据影响,即最佳 / 最差 / 平均时间复杂度不相等。 - 「非自适应排序」的时间复杂度恒定,与输入数据无关。 -我们希望 **最差 = 平均** ,即不希望排序算法的运行效率在某些输入数据下发生劣化。 +我们希望 **最差 = 平均**,即不希望排序算法的运行效率在某些输入数据下发生劣化。 ### 比较类 diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index ae0cfc7c2..5895fcb8e 100644 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -6,8 +6,8 @@ comments: true 「归并排序 Merge Sort」是算法中“分治思想”的典型体现,其有「划分」和「合并」两个阶段: -1. **划分阶段:** 通过递归不断 **将数组从中点位置划分开**,将长数组的排序问题转化为短数组的排序问题; -2. **合并阶段:** 划分到子数组长度为 1 时,开始向上合并,不断将 **左、右两个短排序数组** 合并为 **一个长排序数组**,直至合并至原数组时完成排序; +1. **划分阶段**:通过递归不断 **将数组从中点位置划分开**,将长数组的排序问题转化为短数组的排序问题; +2. **合并阶段**:划分到子数组长度为 1 时,开始向上合并,不断将 **左、右两个短排序数组** 合并为 **一个长排序数组**,直至合并至原数组时完成排序; ![merge_sort_preview](merge_sort.assets/merge_sort_preview.png) @@ -15,14 +15,14 @@ comments: true ## 算法流程 -**「递归划分」** 从顶至底递归地 **将数组从中点切为两个子数组** ,直至长度为 1 ; +**「递归划分」** 从顶至底递归地 **将数组从中点切为两个子数组**,直至长度为 1 ; 1. 计算数组中点 `mid` ,递归划分左子数组(区间 `[left, mid]` )和右子数组(区间 `[mid + 1, right]` ); 2. 递归执行 `1.` 步骤,直至子数组区间长度为 1 时,终止递归划分; **「回溯合并」** 从底至顶地将左子数组和右子数组合并为一个 **有序数组** ; -需要注意,由于从长度为 1 的子数组开始合并,所以 **每个子数组都是有序的** 。因此,合并任务本质是要 **将两个有序子数组合并为一个有序数组** 。 +需要注意,由于从长度为 1 的子数组开始合并,所以 **每个子数组都是有序的**。因此,合并任务本质是要 **将两个有序子数组合并为一个有序数组**。 === "Step1" ![merge_sort_step1](merge_sort.assets/merge_sort_step1.png) @@ -56,8 +56,8 @@ comments: true 观察发现,归并排序的递归顺序就是二叉树的「后序遍历」。 -- **后序遍历:** 先递归左子树、再递归右子树、最后处理根结点。 -- **归并排序:** 先递归左子树、再递归右子树、最后处理合并。 +- **后序遍历**:先递归左子树、再递归右子树、最后处理根结点。 +- **归并排序**:先递归左子树、再递归右子树、最后处理合并。 === "Java" @@ -406,11 +406,11 @@ comments: true ## 算法特性 -- **时间复杂度 $O(n \log n)$ :** 划分形成高度为 $\log n$ 的递归树,每层合并的总操作数量为 $n$ ,总体使用 $O(n \log n)$ 时间。 -- **空间复杂度 $O(n)$ :** 需借助辅助数组实现合并,使用 $O(n)$ 大小的额外空间;递归深度为 $\log n$ ,使用 $O(\log n)$ 大小的栈帧空间。 -- **非原地排序:** 辅助数组需要使用 $O(n)$ 额外空间。 -- **稳定排序:** 在合并时可保证相等元素的相对位置不变。 -- **非自适应排序:** 对于任意输入数据,归并排序的时间复杂度皆相同。 +- **时间复杂度 $O(n \log n)$** :划分形成高度为 $\log n$ 的递归树,每层合并的总操作数量为 $n$ ,总体使用 $O(n \log n)$ 时间。 +- **空间复杂度 $O(n)$** :需借助辅助数组实现合并,使用 $O(n)$ 大小的额外空间;递归深度为 $\log n$ ,使用 $O(\log n)$ 大小的栈帧空间。 +- **非原地排序**:辅助数组需要使用 $O(n)$ 额外空间。 +- **稳定排序**:在合并时可保证相等元素的相对位置不变。 +- **非自适应排序**:对于任意输入数据,归并排序的时间复杂度皆相同。 ## 链表排序 * diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index 3eeac9c65..a712a1bff 100644 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -6,13 +6,13 @@ comments: true 「快速排序 Quick Sort」是一种基于“分治思想”的排序算法,速度很快、应用很广。 -快速排序的核心操作为「哨兵划分」,其目标为:选取数组某个元素为 **基准数** ,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。「哨兵划分」的实现流程为: +快速排序的核心操作为「哨兵划分」,其目标为:选取数组某个元素为 **基准数**,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。「哨兵划分」的实现流程为: 1. 以数组最左端元素作为基准数,初始化两个指针 `i` , `j` 指向数组两端; 2. 设置一个循环,每轮中使用 `i` / `j` 分别寻找首个比基准数大 / 小的元素,并交换此两元素; 3. 不断循环步骤 `2.` ,直至 `i` , `j` 相遇时跳出,最终把基准数交换至两个子数组的分界线; -「哨兵划分」执行完毕后,原数组被划分成两个部分,即 **左子数组** 和 **右子数组** ,且满足 **左子数组任意元素 < 基准数 < 右子数组任意元素**。因此,接下来我们只需要排序两个子数组即可。 +「哨兵划分」执行完毕后,原数组被划分成两个部分,即 **左子数组** 和 **右子数组**,且满足 **左子数组任意元素 < 基准数 < 右子数组任意元素**。因此,接下来我们只需要排序两个子数组即可。 === "Step 1" ![pivot_division_step1](quick_sort.assets/pivot_division_step1.png) @@ -235,9 +235,9 @@ comments: true ## 算法流程 -1. 首先,对数组执行一次「哨兵划分」,得到待排序的 **左子数组** 和 **右子数组** 。 +1. 首先,对数组执行一次「哨兵划分」,得到待排序的 **左子数组** 和 **右子数组**; 2. 接下来,对 **左子数组** 和 **右子数组** 分别 **递归执行**「哨兵划分」…… -3. 直至子数组长度为 1 时 **终止递归** ,即可完成对整个数组的排序。 +3. 直至子数组长度为 1 时 **终止递归**,即可完成对整个数组的排序; 观察发现,快速排序和「二分查找」的原理类似,都是以对数阶的时间复杂度来缩小处理区间。 @@ -373,31 +373,31 @@ comments: true ## 算法特性 -**平均时间复杂度 $O(n \log n)$ :** 平均情况下,哨兵划分的递归层数为 $\log n$ ,每层中的总循环数为 $n$ ,总体使用 $O(n \log n)$ 时间。 +**平均时间复杂度 $O(n \log n)$** :平均情况下,哨兵划分的递归层数为 $\log n$ ,每层中的总循环数为 $n$ ,总体使用 $O(n \log n)$ 时间。 -**最差时间复杂度 $O(n^2)$ :** 最差情况下,哨兵划分操作将长度为 $n$ 的数组划分为长度为 $0$ 和 $n - 1$ 的两个子数组,此时递归层数达到 $n$ 层,每层中的循环数为 $n$ ,总体使用 $O(n^2)$ 时间。 +**最差时间复杂度 $O(n^2)$** :最差情况下,哨兵划分操作将长度为 $n$ 的数组划分为长度为 $0$ 和 $n - 1$ 的两个子数组,此时递归层数达到 $n$ 层,每层中的循环数为 $n$ ,总体使用 $O(n^2)$ 时间。 -**空间复杂度 $O(n)$ :** 输入数组完全倒序下,达到最差递归深度 $n$ 。 +**空间复杂度 $O(n)$** :输入数组完全倒序下,达到最差递归深度 $n$ 。 -**原地排序:** 只在递归中使用 $O(\log n)$ 大小的栈帧空间。 +**原地排序**:只在递归中使用 $O(\log n)$ 大小的栈帧空间。 -**非稳定排序:** 哨兵划分操作可能改变相等元素的相对位置。 +**非稳定排序**:哨兵划分操作可能改变相等元素的相对位置。 -**自适应排序:** 最差情况下,时间复杂度劣化至 $O(n^2)$ 。 +**自适应排序**:最差情况下,时间复杂度劣化至 $O(n^2)$ 。 ## 快排为什么快? -从命名能够看出,快速排序在效率方面一定“有两把刷子”。快速排序的平均时间复杂度虽然与「归并排序」和「堆排序」一致,但实际 **效率更高** ,这是因为: +从命名能够看出,快速排序在效率方面一定“有两把刷子”。快速排序的平均时间复杂度虽然与「归并排序」和「堆排序」一致,但实际 **效率更高**,这是因为: -- **出现最差情况的概率很低:** 虽然快速排序的最差时间复杂度为 $O(n^2)$ ,不如归并排序,但绝大部分情况下,快速排序可以达到 $O(n \log n)$ 的复杂度。 -- **缓存使用效率高:** 哨兵划分操作时,将整个子数组加载入缓存中,访问元素效率很高。而诸如「堆排序」需要跳跃式访问元素,因此不具有此特性。 -- **复杂度的常数系数低:** 在提及的三种算法中,快速排序的 **比较**、**赋值**、**交换** 三种操作的总体数量最少(类似于「插入排序」快于「冒泡排序」的原因)。 +- **出现最差情况的概率很低**:虽然快速排序的最差时间复杂度为 $O(n^2)$ ,不如归并排序,但绝大部分情况下,快速排序可以达到 $O(n \log n)$ 的复杂度。 +- **缓存使用效率高**:哨兵划分操作时,将整个子数组加载入缓存中,访问元素效率很高。而诸如「堆排序」需要跳跃式访问元素,因此不具有此特性。 +- **复杂度的常数系数低**:在提及的三种算法中,快速排序的 **比较**、**赋值**、**交换** 三种操作的总体数量最少(类似于「插入排序」快于「冒泡排序」的原因)。 ## 基准数优化 -**普通快速排序在某些输入下的时间效率变差**。举个极端例子,假设输入数组是完全倒序的,由于我们选取最左端元素为基准数,那么在哨兵划分完成后,基准数被交换至数组最右端,从而 **左子数组长度为 $n - 1$ 、右子数组长度为 $0$** 。这样进一步递归下去,**每轮哨兵划分后的右子数组长度都为 $0$** ,分治策略失效,快速排序退化为「冒泡排序」了。 +**普通快速排序在某些输入下的时间效率变差**。举个极端例子,假设输入数组是完全倒序的,由于我们选取最左端元素为基准数,那么在哨兵划分完成后,基准数被交换至数组最右端,从而 **左子数组长度为 $n - 1$、右子数组长度为 $0$** 。这样进一步递归下去,**每轮哨兵划分后的右子数组长度都为 $0$** ,分治策略失效,快速排序退化为「冒泡排序」了。 -为了尽量避免这种情况发生,我们可以优化一下基准数的选取策略。首先,在哨兵划分中,我们可以 **随机选取一个元素作为基准数** 。但如果运气很差,每次都选择到比较差的基准数,那么效率依然不好。 +为了尽量避免这种情况发生,我们可以优化一下基准数的选取策略。首先,在哨兵划分中,我们可以 **随机选取一个元素作为基准数**。但如果运气很差,每次都选择到比较差的基准数,那么效率依然不好。 进一步地,我们可以在数组中选取 3 个候选元素(一般为数组的首、尾、中点元素),**并将三个候选元素的中位数作为基准数**,这样基准数“既不大也不小”的概率就大大提升了。当然,如果数组很长的话,我们也可以选取更多候选元素,来进一步提升算法的稳健性。采取该方法后,时间复杂度劣化至 $O(n^2)$ 的概率极低。 diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index 9944f29e8..6f6272645 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -106,7 +106,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit ``` -「结点高度」是最远叶结点到该结点的距离,即走过的「边」的数量。需要特别注意,**叶结点的高度为 0 ,空结点的高度为 -1** 。我们封装两个工具函数,分别用于获取与更新结点的高度。 +「结点高度」是最远叶结点到该结点的距离,即走过的「边」的数量。需要特别注意,**叶结点的高度为 0 ,空结点的高度为 -1**。我们封装两个工具函数,分别用于获取与更新结点的高度。 === "Java" @@ -310,7 +310,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 ### Case 1 - 右旋 -如下图所示(结点下方为「平衡因子」),从底至顶看,二叉树中首个失衡结点是 **结点 3** 。我们聚焦在以该失衡结点为根结点的子树上,将该结点记为 `node` ,将其左子节点记为 `child` ,执行「右旋」操作。完成右旋后,该子树已经恢复平衡,并且仍然为二叉搜索树。 +如下图所示(结点下方为「平衡因子」),从底至顶看,二叉树中首个失衡结点是 **结点 3**。我们聚焦在以该失衡结点为根结点的子树上,将该结点记为 `node` ,将其左子节点记为 `child` ,执行「右旋」操作。完成右旋后,该子树已经恢复平衡,并且仍然为二叉搜索树。 === "Step 1" ![right_rotate_step1](avl_tree.assets/right_rotate_step1.png) diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index a4c5a5d2a..a0322154e 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -202,8 +202,8 @@ comments: true 给定一个待插入元素 `num` ,为了保持二叉搜索树“左子树 < 根结点 < 右子树”的性质,插入操作分为两步: -1. **查找插入位置:** 与查找操作类似,我们从根结点出发,根据当前结点值和 `num` 的大小关系循环向下搜索,直到越过叶结点(遍历到 $\text{null}$ )时跳出循环; -2. **在该位置插入结点:** 初始化结点 `num` ,将该结点放到 $\text{null}$ 的位置 ; +1. **查找插入位置**:与查找操作类似,我们从根结点出发,根据当前结点值和 `num` 的大小关系循环向下搜索,直到越过叶结点(遍历到 $\text{null}$ )时跳出循环; +2. **在该位置插入结点**:初始化结点 `num` ,将该结点放到 $\text{null}$ 的位置 ; 二叉搜索树不允许存在重复结点,否则将会违背其定义。因此若待插入结点在树中已经存在,则不执行插入,直接返回即可。 @@ -442,15 +442,15 @@ comments: true 与插入结点一样,我们需要在删除操作后维持二叉搜索树的“左子树 < 根结点 < 右子树”的性质。首先,我们需要在二叉树中执行查找操作,获取待删除结点。接下来,根据待删除结点的子结点数量,删除操作需要分为三种情况: -**待删除结点的子结点数量 $= 0$ **。表明待删除结点是叶结点,直接删除即可。 +**当待删除结点的子结点数量 $= 0$ 时**,表明待删除结点是叶结点,直接删除即可。 ![bst_remove_case1](binary_search_tree.assets/bst_remove_case1.png) -**待删除结点的子结点数量 $= 1$ **。将待删除结点替换为其子结点。 +**当待删除结点的子结点数量 $= 1$ 时**,将待删除结点替换为其子结点即可。 ![bst_remove_case2](binary_search_tree.assets/bst_remove_case2.png) -**待删除结点的子结点数量 $= 2$ **。删除操作分为三步: +**当待删除结点的子结点数量 $= 2$ 时**,删除操作分为三步: 1. 找到待删除结点在 **中序遍历序列** 中的下一个结点,记为 `nex` ; 2. 在树中递归删除结点 `nex` ; @@ -830,17 +830,17 @@ comments: true 假设给定 $n$ 个数字,最常用的存储方式是「数组」,那么对于这串乱序的数字,常见操作的效率为: -- **查找元素:** 由于数组是无序的,因此需要遍历数组来确定,使用 $O(n)$ 时间; -- **插入元素:** 只需将元素添加至数组尾部即可,使用 $O(1)$ 时间; -- **删除元素:** 先查找元素,使用 $O(n)$ 时间,再在数组中删除该元素,使用 $O(n)$ 时间; -- **获取最小 / 最大元素:** 需要遍历数组来确定,使用 $O(n)$ 时间; +- **查找元素**:由于数组是无序的,因此需要遍历数组来确定,使用 $O(n)$ 时间; +- **插入元素**:只需将元素添加至数组尾部即可,使用 $O(1)$ 时间; +- **删除元素**:先查找元素,使用 $O(n)$ 时间,再在数组中删除该元素,使用 $O(n)$ 时间; +- **获取最小 / 最大元素**:需要遍历数组来确定,使用 $O(n)$ 时间; 为了得到先验信息,我们也可以预先将数组元素进行排序,得到一个「排序数组」,此时操作效率为: -- **查找元素:** 由于数组已排序,可以使用二分查找,平均使用 $O(\log n)$ 时间; -- **插入元素:** 先查找插入位置,使用 $O(\log n)$ 时间,再插入到指定位置,使用 $O(n)$ 时间; -- **删除元素:** 先查找元素,使用 $O(\log n)$ 时间,再在数组中删除该元素,使用 $O(n)$ 时间; -- **获取最小 / 最大元素:** 数组头部和尾部元素即是最小和最大元素,使用 $O(1)$ 时间; +- **查找元素**:由于数组已排序,可以使用二分查找,平均使用 $O(\log n)$ 时间; +- **插入元素**:先查找插入位置,使用 $O(\log n)$ 时间,再插入到指定位置,使用 $O(n)$ 时间; +- **删除元素**:先查找元素,使用 $O(\log n)$ 时间,再在数组中删除该元素,使用 $O(n)$ 时间; +- **获取最小 / 最大元素**:数组头部和尾部元素即是最小和最大元素,使用 $O(1)$ 时间; 观察发现,无序数组和有序数组中的各项操作的时间复杂度是“偏科”的,即有的快有的慢;**而二叉搜索树的各项操作的时间复杂度都是对数阶,在数据量 $n$ 很大时有巨大优势**。 From d6d6a16c7e24d9737d667507a729ec9e60cd1bda Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Tue, 10 Jan 2023 00:46:04 +0800 Subject: [PATCH 142/158] Update the access() function of linked_list --- .../chapter_array_and_linkedlist/linked_list.cpp | 2 +- .../chapter_array_and_linkedlist/linked_list.cs | 2 +- .../go/chapter_array_and_linkedlist/linked_list.go | 2 +- .../chapter_array_and_linkedlist/linked_list.java | 2 +- .../chapter_array_and_linkedlist/linked_list.py | 2 +- .../chapter_array_and_linkedlist/linked_list.swift | 2 +- docs/chapter_array_and_linkedlist/linked_list.md | 13 +++++++------ 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp b/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp index 13fa68691..5e976a89a 100644 --- a/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp +++ b/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp @@ -28,9 +28,9 @@ void remove(ListNode* n0) { /* 访问链表中索引为 index 的结点 */ ListNode* access(ListNode* head, int index) { for (int i = 0; i < index; i++) { - head = head->next; if (head == nullptr) return nullptr; + head = head->next; } return head; } diff --git a/codes/csharp/chapter_array_and_linkedlist/linked_list.cs b/codes/csharp/chapter_array_and_linkedlist/linked_list.cs index d700a5ae7..d765724ff 100644 --- a/codes/csharp/chapter_array_and_linkedlist/linked_list.cs +++ b/codes/csharp/chapter_array_and_linkedlist/linked_list.cs @@ -39,9 +39,9 @@ namespace hello_algo.chapter_array_and_linkedlist { for (int i = 0; i < index; i++) { - head = head.next; if (head == null) return null; + head = head.next; } return head; } diff --git a/codes/go/chapter_array_and_linkedlist/linked_list.go b/codes/go/chapter_array_and_linkedlist/linked_list.go index fa4538ab4..8a60fd18e 100644 --- a/codes/go/chapter_array_and_linkedlist/linked_list.go +++ b/codes/go/chapter_array_and_linkedlist/linked_list.go @@ -29,10 +29,10 @@ func removeNode(n0 *ListNode) { /* 访问链表中索引为 index 的结点 */ func access(head *ListNode, index int) *ListNode { for i := 0; i < index; i++ { - head = head.Next if head == nil { return nil } + head = head.Next } return head } diff --git a/codes/java/chapter_array_and_linkedlist/linked_list.java b/codes/java/chapter_array_and_linkedlist/linked_list.java index 570778ff5..0db8f6ae7 100644 --- a/codes/java/chapter_array_and_linkedlist/linked_list.java +++ b/codes/java/chapter_array_and_linkedlist/linked_list.java @@ -29,9 +29,9 @@ public class linked_list { /* 访问链表中索引为 index 的结点 */ static ListNode access(ListNode head, int index) { for (int i = 0; i < index; i++) { - head = head.next; if (head == null) return null; + head = head.next; } return head; } diff --git a/codes/python/chapter_array_and_linkedlist/linked_list.py b/codes/python/chapter_array_and_linkedlist/linked_list.py index dce110340..4fb6b1ba5 100644 --- a/codes/python/chapter_array_and_linkedlist/linked_list.py +++ b/codes/python/chapter_array_and_linkedlist/linked_list.py @@ -26,9 +26,9 @@ def remove(n0): """ 访问链表中索引为 index 的结点 """ def access(head, index): for _ in range(index): - head = head.next if not head: return None + head = head.next return head """ 在链表中查找值为 target 的首个结点 """ diff --git a/codes/swift/chapter_array_and_linkedlist/linked_list.swift b/codes/swift/chapter_array_and_linkedlist/linked_list.swift index 06837f750..90218b26b 100644 --- a/codes/swift/chapter_array_and_linkedlist/linked_list.swift +++ b/codes/swift/chapter_array_and_linkedlist/linked_list.swift @@ -29,10 +29,10 @@ func remove(n0: ListNode) { func access(head: ListNode, index: Int) -> ListNode? { var head: ListNode? = head for _ in 0 ..< index { - head = head?.next if head == nil { return nil } + head = head?.next } return head } diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index 7aa2754ca..8dd069788 100644 --- a/docs/chapter_array_and_linkedlist/linked_list.md +++ b/docs/chapter_array_and_linkedlist/linked_list.md @@ -400,6 +400,7 @@ comments: true n0.next = P; P.next = n1; } + /* 删除链表的结点 n0 之后的首个结点 */ function remove(n0: ListNode): void { if (!n0.next) { @@ -474,9 +475,9 @@ comments: true /* 访问链表中索引为 index 的结点 */ ListNode access(ListNode head, int index) { for (int i = 0; i < index; i++) { - head = head.next; if (head == null) return null; + head = head.next; } return head; } @@ -488,9 +489,9 @@ comments: true /* 访问链表中索引为 index 的结点 */ ListNode* access(ListNode* head, int index) { for (int i = 0; i < index; i++) { - head = head->next; if (head == nullptr) return nullptr; + head = head->next; } return head; } @@ -502,9 +503,9 @@ comments: true """ 访问链表中索引为 index 的结点 """ def access(head, index): for _ in range(index): - head = head.next if not head: return None + head = head.next return head ``` @@ -514,10 +515,10 @@ comments: true /* 访问链表中索引为 index 的结点 */ func access(head *ListNode, index int) *ListNode { for i := 0; i < index; i++ { - head = head.Next if head == nil { return nil } + head = head.Next } return head } @@ -566,9 +567,9 @@ comments: true { for (int i = 0; i < index; i++) { - head = head.next; if (head == null) return null; + head = head.next; } return head; } @@ -581,10 +582,10 @@ comments: true func access(head: ListNode, index: Int) -> ListNode? { var head: ListNode? = head for _ in 0 ..< index { - head = head?.next if head == nil { return nil } + head = head?.next } return head } From b5019b0494bfbe28d50c0e3c5442cb3ba62edc03 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Tue, 10 Jan 2023 01:14:44 +0800 Subject: [PATCH 143/158] Update "about the book". --- docs/chapter_preface/about_the_book.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index c3209280c..01f513f00 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -205,9 +205,6 @@ comments: true */ ``` -""" -在 Java, C, C++, C#, Go, JS, TS 的代码注释中,`/* ... */` 用于注释函数、类、测试样例等标题, `// ...` 用于解释代码内容;类似地,在 Python 中,`""" ... """` 用于注释标题, `# ...` 用于解释代码。 - ## 本书特点 * ??? abstract "默认折叠,可以跳过" From b7e09c4c1db1fd316d16f39453b9e8782a5d5579 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Tue, 10 Jan 2023 01:49:16 +0800 Subject: [PATCH 144/158] Unify the comment format of C# codes. --- .../chapter_array_and_linkedlist/array.cs | 30 +++++-------------- .../linked_list.cs | 18 ++++------- .../csharp/chapter_tree/binary_search_tree.cs | 6 +--- codes/csharp/chapter_tree/binary_tree_bfs.cs | 6 +--- codes/csharp/chapter_tree/binary_tree_dfs.cs | 15 ++-------- 5 files changed, 18 insertions(+), 57 deletions(-) diff --git a/codes/csharp/chapter_array_and_linkedlist/array.cs b/codes/csharp/chapter_array_and_linkedlist/array.cs index 95479574f..97a37f046 100644 --- a/codes/csharp/chapter_array_and_linkedlist/array.cs +++ b/codes/csharp/chapter_array_and_linkedlist/array.cs @@ -8,9 +8,7 @@ namespace hello_algo.chapter_array_and_linkedlist { public class Array { - /// - /// 随机返回一个数组元素 - /// + /* 随机返回一个数组元素 */ public static int RandomAccess(int[] nums) { Random random = new(); @@ -19,9 +17,7 @@ namespace hello_algo.chapter_array_and_linkedlist return randomNum; } - /// - /// 扩展数组长度 - /// + /* 扩展数组长度 */ public static int[] Extend(int[] nums, int enlarge) { // 初始化一个扩展长度后的数组 @@ -35,9 +31,7 @@ namespace hello_algo.chapter_array_and_linkedlist return res; } - /// - /// 在数组的索引 index 处插入元素 num - /// + /* 在数组的索引 index 处插入元素 num */ public static void Insert(int[] nums, int num, int index) { // 把索引 index 以及之后的所有元素向后移动一位 @@ -49,9 +43,7 @@ namespace hello_algo.chapter_array_and_linkedlist nums[index] = num; } - /// - /// 删除索引 index 处元素 - /// + /* 删除索引 index 处元素 */ public static void Remove(int[] nums, int index) { // 把索引 index 之后的所有元素向前移动一位 @@ -61,9 +53,7 @@ namespace hello_algo.chapter_array_and_linkedlist } } - /// - /// 遍历数组 - /// + /* 遍历数组 */ public static void Traverse(int[] nums) { int count = 0; @@ -79,9 +69,7 @@ namespace hello_algo.chapter_array_and_linkedlist } } - /// - /// 在数组中查找指定元素 - /// + /* 在数组中查找指定元素 */ public static int Find(int[] nums, int target) { for (int i = 0; i < nums.Length; i++) @@ -92,15 +80,13 @@ namespace hello_algo.chapter_array_and_linkedlist return -1; } - /// - /// 辅助函数,数组转字符串 - /// + /* 辅助函数,数组转字符串 */ public static string ToString(int[] nums) { return string.Join(",", nums); } - // Driver Code + [Test] public static void Test() { diff --git a/codes/csharp/chapter_array_and_linkedlist/linked_list.cs b/codes/csharp/chapter_array_and_linkedlist/linked_list.cs index d765724ff..81e1499cb 100644 --- a/codes/csharp/chapter_array_and_linkedlist/linked_list.cs +++ b/codes/csharp/chapter_array_and_linkedlist/linked_list.cs @@ -9,9 +9,7 @@ namespace hello_algo.chapter_array_and_linkedlist { public class linked_list { - /// - /// 在链表的结点 n0 之后插入结点 P - /// + /* 在链表的结点 n0 之后插入结点 P */ public static void Insert(ListNode n0, ListNode P) { ListNode? n1 = n0.next; @@ -19,9 +17,7 @@ namespace hello_algo.chapter_array_and_linkedlist P.next = n1; } - /// - /// 删除链表的结点 n0 之后的首个结点 - /// + /* 删除链表的结点 n0 之后的首个结点 */ public static void Remove(ListNode n0) { if (n0.next == null) @@ -32,9 +28,7 @@ namespace hello_algo.chapter_array_and_linkedlist n0.next = n1; } - /// - /// 访问链表中索引为 index 的结点 - /// + /* 访问链表中索引为 index 的结点 */ public static ListNode? Access(ListNode head, int index) { for (int i = 0; i < index; i++) @@ -46,9 +40,7 @@ namespace hello_algo.chapter_array_and_linkedlist return head; } - /// - /// 在链表中查找值为 target 的首个结点 - /// + /* 在链表中查找值为 target 的首个结点 */ public static int Find(ListNode head, int target) { int index = 0; @@ -62,7 +54,7 @@ namespace hello_algo.chapter_array_and_linkedlist return -1; } - // Driver Code + [Test] public void Test() { diff --git a/codes/csharp/chapter_tree/binary_search_tree.cs b/codes/csharp/chapter_tree/binary_search_tree.cs index 5164cf780..d2e5f95e6 100644 --- a/codes/csharp/chapter_tree/binary_search_tree.cs +++ b/codes/csharp/chapter_tree/binary_search_tree.cs @@ -35,11 +35,7 @@ namespace hello_algo.chapter_tree return root; } - /// - /// 查找结点 - /// - /// - /// + /* 查找结点 */ public TreeNode? search(int num) { TreeNode? cur = root; diff --git a/codes/csharp/chapter_tree/binary_tree_bfs.cs b/codes/csharp/chapter_tree/binary_tree_bfs.cs index f0c914acb..9a57dbc17 100644 --- a/codes/csharp/chapter_tree/binary_tree_bfs.cs +++ b/codes/csharp/chapter_tree/binary_tree_bfs.cs @@ -12,11 +12,7 @@ namespace hello_algo.chapter_tree public class binary_tree_bfs { - /// - /// 层序遍历 - /// - /// - /// + /* 层序遍历 */ public List hierOrder(TreeNode root) { // 初始化队列,加入根结点 diff --git a/codes/csharp/chapter_tree/binary_tree_dfs.cs b/codes/csharp/chapter_tree/binary_tree_dfs.cs index 0f89cb3b2..f8669d782 100644 --- a/codes/csharp/chapter_tree/binary_tree_dfs.cs +++ b/codes/csharp/chapter_tree/binary_tree_dfs.cs @@ -13,10 +13,7 @@ namespace hello_algo.chapter_tree { List list = new(); - /// - /// 前序遍历 - /// - /// + /* 前序遍历 */ void preOrder(TreeNode? root) { if (root == null) return; @@ -26,10 +23,7 @@ namespace hello_algo.chapter_tree preOrder(root.right); } - /// - /// 中序遍历 - /// - /// + /* 中序遍历 */ void inOrder(TreeNode? root) { if (root == null) return; @@ -39,10 +33,7 @@ namespace hello_algo.chapter_tree inOrder(root.right); } - /// - /// 后序遍历 - /// - /// + /* 后序遍历 */ void postOrder(TreeNode? root) { if (root == null) return; From 47f017177bad4faa8582c635772f9b64a2861584 Mon Sep 17 00:00:00 2001 From: nuomi1 Date: Mon, 9 Jan 2023 23:02:22 +0800 Subject: [PATCH 145/158] feat: add Swift codes for stack article --- codes/swift/Package.swift | 6 + .../chapter_stack_and_queue/array_stack.swift | 84 +++++++++++++ .../linkedlist_stack.swift | 93 ++++++++++++++ .../swift/chapter_stack_and_queue/stack.swift | 39 ++++++ docs/chapter_stack_and_queue/stack.md | 116 ++++++++++++++++++ 5 files changed, 338 insertions(+) create mode 100644 codes/swift/chapter_stack_and_queue/array_stack.swift create mode 100644 codes/swift/chapter_stack_and_queue/linkedlist_stack.swift create mode 100644 codes/swift/chapter_stack_and_queue/stack.swift diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index 3a5a18ed3..6d6eaa8a5 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -13,6 +13,9 @@ let package = Package( .executable(name: "linked_list", targets: ["linked_list"]), .executable(name: "list", targets: ["list"]), .executable(name: "my_list", targets: ["my_list"]), + .executable(name: "stack", targets: ["stack"]), + .executable(name: "linkedlist_stack", targets: ["linkedlist_stack"]), + .executable(name: "array_stack", targets: ["array_stack"]), ], targets: [ .target(name: "utils", path: "utils"), @@ -24,5 +27,8 @@ let package = Package( .executableTarget(name: "linked_list", dependencies: ["utils"], path: "chapter_array_and_linkedlist", sources: ["linked_list.swift"]), .executableTarget(name: "list", path: "chapter_array_and_linkedlist", sources: ["list.swift"]), .executableTarget(name: "my_list", path: "chapter_array_and_linkedlist", sources: ["my_list.swift"]), + .executableTarget(name: "stack", path: "chapter_stack_and_queue", sources: ["stack.swift"]), + .executableTarget(name: "linkedlist_stack", dependencies: ["utils"], path: "chapter_stack_and_queue", sources: ["linkedlist_stack.swift"]), + .executableTarget(name: "array_stack", path: "chapter_stack_and_queue", sources: ["array_stack.swift"]), ] ) diff --git a/codes/swift/chapter_stack_and_queue/array_stack.swift b/codes/swift/chapter_stack_and_queue/array_stack.swift new file mode 100644 index 000000000..faeeaa173 --- /dev/null +++ b/codes/swift/chapter_stack_and_queue/array_stack.swift @@ -0,0 +1,84 @@ +/** + * File: array_stack.swift + * Created Time: 2023-01-09 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* 基于数组实现的栈 */ +class ArrayStack { + private var stack: [Int] + + init() { + // 初始化列表(动态数组) + stack = [] + } + + /* 获取栈的长度 */ + func size() -> Int { + stack.count + } + + /* 判断栈是否为空 */ + func isEmpty() -> Bool { + stack.isEmpty + } + + /* 入栈 */ + func push(num: Int) { + stack.append(num) + } + + /* 出栈 */ + func pop() -> Int { + if stack.isEmpty { + fatalError("栈为空") + } + return stack.removeLast() + } + + /* 访问栈顶元素 */ + func peek() -> Int { + if stack.isEmpty { + fatalError("栈为空") + } + return stack.last! + } + + /* 将 List 转化为 Array 并返回 */ + func toArray() -> [Int] { + stack + } +} + +@main +enum _ArrayStack { + /* Driver Code */ + static func main() { + /* 初始化栈 */ + let stack = ArrayStack() + + /* 元素入栈 */ + stack.push(num: 1) + stack.push(num: 3) + stack.push(num: 2) + stack.push(num: 5) + stack.push(num: 4) + print("栈 stack = \(stack.toArray())") + + /* 访问栈顶元素 */ + let peek = stack.peek() + print("栈顶元素 peek = \(peek)") + + /* 元素出栈 */ + let pop = stack.pop() + print("出栈元素 pop = \(pop),出栈后 stack = \(stack.toArray())") + + /* 获取栈的长度 */ + let size = stack.size() + print("栈的长度 size = \(size)") + + /* 判断是否为空 */ + let isEmpty = stack.isEmpty() + print("栈是否为空 = \(isEmpty)") + } +} diff --git a/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift b/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift new file mode 100644 index 000000000..9d3c98b01 --- /dev/null +++ b/codes/swift/chapter_stack_and_queue/linkedlist_stack.swift @@ -0,0 +1,93 @@ +/** + * File: linkedlist_stack.swift + * Created Time: 2023-01-09 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +/* 基于链表实现的栈 */ +class LinkedListStack { + private var _peek: ListNode? // 将头结点作为栈顶 + private var _size = 0 // 栈的长度 + + init() {} + + /* 获取栈的长度 */ + func size() -> Int { + _size + } + + /* 判断栈是否为空 */ + func isEmpty() -> Bool { + _size == 0 + } + + /* 入栈 */ + func push(num: Int) { + let node = ListNode(x: num) + node.next = _peek + _peek = node + _size += 1 + } + + /* 出栈 */ + func pop() -> Int { + let num = peek() + _peek = _peek?.next + _size -= 1 + return num + } + + /* 访问栈顶元素 */ + func peek() -> Int { + if _size == 0 { + fatalError("栈为空") + } + return _peek!.val + } + + /* 将 List 转化为 Array 并返回 */ + func toArray() -> [Int] { + var node = _peek + var res = Array(repeating: 0, count: _size) + for i in sequence(first: res.count - 1, next: { $0 >= 0 + 1 ? $0 - 1 : nil }) { + res[i] = node!.val + node = node?.next + } + return res + } +} + +@main +enum _LinkedListStack { + /* Driver Code */ + static func main() { + /* 初始化栈 */ + let stack = LinkedListStack() + + /* 元素入栈 */ + stack.push(num: 1) + stack.push(num: 3) + stack.push(num: 2) + stack.push(num: 5) + stack.push(num: 4) + print("栈 stack = \(stack.toArray())") + + /* 访问栈顶元素 */ + let peek = stack.peek() + print("栈顶元素 peek = \(peek)") + + /* 元素出栈 */ + let pop = stack.pop() + print("出栈元素 pop = \(pop),出栈后 stack = \(stack.toArray())") + + /* 获取栈的长度 */ + let size = stack.size() + print("栈的长度 size = \(size)") + + /* 判断是否为空 */ + let isEmpty = stack.isEmpty() + print("栈是否为空 = \(isEmpty)") + } +} diff --git a/codes/swift/chapter_stack_and_queue/stack.swift b/codes/swift/chapter_stack_and_queue/stack.swift new file mode 100644 index 000000000..c6b8bc184 --- /dev/null +++ b/codes/swift/chapter_stack_and_queue/stack.swift @@ -0,0 +1,39 @@ +/** + * File: stack.swift + * Created Time: 2023-01-09 + * Author: nuomi1 (nuomi1@qq.com) + */ + +@main +enum Stack { + /* Driver Code */ + static func main() { + /* 初始化栈 */ + // Swift 没有内置的栈类,可以把 Array 当作栈来使用 + var stack: [Int] = [] + + /* 元素入栈 */ + stack.append(1) + stack.append(3) + stack.append(2) + stack.append(5) + stack.append(4) + print("栈 stack = \(stack)") + + /* 访问栈顶元素 */ + let peek = stack.last! + print("栈顶元素 peek = \(peek)") + + /* 元素出栈 */ + let pop = stack.removeLast() + print("出栈元素 pop = \(pop),出栈后 stack = \(stack)") + + /* 获取栈的长度 */ + let size = stack.count + print("栈的长度 size = \(size)") + + /* 判断是否为空 */ + let isEmpty = stack.isEmpty + print("栈是否为空 = \(isEmpty)") + } +} diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index d5a3f9164..04825695c 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -231,7 +231,28 @@ comments: true === "Swift" ```swift title="stack.swift" + /* 初始化栈 */ + // Swift 没有内置的栈类,可以把 Array 当作栈来使用 + var stack: [Int] = [] + /* 元素入栈 */ + stack.append(1) + stack.append(3) + stack.append(2) + stack.append(5) + stack.append(4) + + /* 访问栈顶元素 */ + let peek = stack.last! + + /* 元素出栈 */ + let pop = stack.removeLast() + + /* 获取栈的长度 */ + let size = stack.count + + /* 判断是否为空 */ + let isEmpty = stack.isEmpty ``` ## 栈的实现 @@ -606,7 +627,58 @@ comments: true === "Swift" ```swift title="linkedlist_stack.swift" + /* 基于链表实现的栈 */ + class LinkedListStack { + private var _peek: ListNode? // 将头结点作为栈顶 + private var _size = 0 // 栈的长度 + init() {} + + /* 获取栈的长度 */ + func size() -> Int { + _size + } + + /* 判断栈是否为空 */ + func isEmpty() -> Bool { + _size == 0 + } + + /* 入栈 */ + func push(num: Int) { + let node = ListNode(x: num) + node.next = _peek + _peek = node + _size += 1 + } + + /* 出栈 */ + func pop() -> Int { + let num = peek() + _peek = _peek?.next + _size -= 1 + return num + } + + /* 访问栈顶元素 */ + func peek() -> Int { + if _size == 0 { + fatalError("栈为空") + } + return _peek!.val + } + + /* 将 List 转化为 Array 并返回 */ + func toArray() -> [Int] { + var node = _peek + var res = Array(repeating: 0, count: _size) + for i in sequence(first: res.count - 1, next: { $0 >= 0 + 1 ? $0 - 1 : nil }) { + res[i] = node!.val + node = node?.next + } + return res + } + } ``` ### 基于数组的实现 @@ -897,7 +969,51 @@ comments: true === "Swift" ```swift title="array_stack.swift" + /* 基于数组实现的栈 */ + class ArrayStack { + private var stack: [Int] + init() { + // 初始化列表(动态数组) + stack = [] + } + + /* 获取栈的长度 */ + func size() -> Int { + stack.count + } + + /* 判断栈是否为空 */ + func isEmpty() -> Bool { + stack.isEmpty + } + + /* 入栈 */ + func push(num: Int) { + stack.append(num) + } + + /* 出栈 */ + func pop() -> Int { + if stack.isEmpty { + fatalError("栈为空") + } + return stack.removeLast() + } + + /* 访问栈顶元素 */ + func peek() -> Int { + if stack.isEmpty { + fatalError("栈为空") + } + return stack.last! + } + + /* 将 List 转化为 Array 并返回 */ + func toArray() -> [Int] { + stack + } + } ``` !!! tip From 96d54bff3a983373ffc2d9c18d8b8446472d780e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=9A=E5=9B=BD=E7=8E=AE?= Date: Tue, 10 Jan 2023 12:16:02 +0800 Subject: [PATCH 146/158] test(binary_search_tree): update test param all the language use param value 7, not 5. function test param value with param value in example picture as same. --- codes/cpp/chapter_tree/binary_search_tree.cpp | 2 +- codes/csharp/chapter_tree/binary_search_tree.cs | 2 +- codes/java/chapter_tree/binary_search_tree.java | 2 +- codes/javascript/chapter_tree/binary_search_tree.js | 2 +- codes/python/chapter_tree/binary_search_tree.py | 2 +- codes/typescript/chapter_tree/binary_search_tree.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/codes/cpp/chapter_tree/binary_search_tree.cpp b/codes/cpp/chapter_tree/binary_search_tree.cpp index 246bdbaa1..648e194ae 100644 --- a/codes/cpp/chapter_tree/binary_search_tree.cpp +++ b/codes/cpp/chapter_tree/binary_search_tree.cpp @@ -131,7 +131,7 @@ int main() { PrintUtil::printTree(bst->getRoot()); /* 查找结点 */ - TreeNode* node = bst->search(5); + TreeNode* node = bst->search(7); cout << endl << "查找到的结点对象为 " << node << ",结点值 = " << node->val << endl; /* 插入结点 */ diff --git a/codes/csharp/chapter_tree/binary_search_tree.cs b/codes/csharp/chapter_tree/binary_search_tree.cs index 8224317df..3cb991486 100644 --- a/codes/csharp/chapter_tree/binary_search_tree.cs +++ b/codes/csharp/chapter_tree/binary_search_tree.cs @@ -163,7 +163,7 @@ namespace hello_algo.chapter_tree PrintUtil.PrintTree(bst.getRoot()); /* 查找结点 */ - TreeNode? node = bst.search(5); + TreeNode? node = bst.search(7); Console.WriteLine("\n查找到的结点对象为 " + node + ",结点值 = " + node.val); /* 插入结点 */ diff --git a/codes/java/chapter_tree/binary_search_tree.java b/codes/java/chapter_tree/binary_search_tree.java index 0770c567d..1755df56b 100644 --- a/codes/java/chapter_tree/binary_search_tree.java +++ b/codes/java/chapter_tree/binary_search_tree.java @@ -131,7 +131,7 @@ public class binary_search_tree { PrintUtil.printTree(bst.getRoot()); /* 查找结点 */ - TreeNode node = bst.search(5); + TreeNode node = bst.search(7); System.out.println("\n查找到的结点对象为 " + node + ",结点值 = " + node.val); /* 插入结点 */ diff --git a/codes/javascript/chapter_tree/binary_search_tree.js b/codes/javascript/chapter_tree/binary_search_tree.js index 1e0d92738..345cff555 100644 --- a/codes/javascript/chapter_tree/binary_search_tree.js +++ b/codes/javascript/chapter_tree/binary_search_tree.js @@ -126,7 +126,7 @@ console.log("\n初始化的二叉树为\n"); printTree(getRoot()); /* 查找结点 */ -let node = search(5); +let node = search(7); console.log("\n查找到的结点对象为 " + node + ",结点值 = " + node.val); /* 插入结点 */ diff --git a/codes/python/chapter_tree/binary_search_tree.py b/codes/python/chapter_tree/binary_search_tree.py index 23e341854..811b3829d 100644 --- a/codes/python/chapter_tree/binary_search_tree.py +++ b/codes/python/chapter_tree/binary_search_tree.py @@ -145,7 +145,7 @@ if __name__ == "__main__": print_tree(bst.root) # 查找结点 - node = bst.search(5) + node = bst.search(7) print("\n查找到的结点对象为: {},结点值 = {}".format(node, node.val)) # 插入结点 diff --git a/codes/typescript/chapter_tree/binary_search_tree.ts b/codes/typescript/chapter_tree/binary_search_tree.ts index 6dad83421..1222b8d5d 100644 --- a/codes/typescript/chapter_tree/binary_search_tree.ts +++ b/codes/typescript/chapter_tree/binary_search_tree.ts @@ -150,7 +150,7 @@ console.log('\n初始化的二叉树为\n'); printTree(getRoot()); /* 查找结点 */ -let node = search(5); +let node = search(7); console.log('\n查找到的结点对象为 ' + node + ',结点值 = ' + node!.val); /* 插入结点 */ From 9c5eedeb8ce7332ddf93ae2c6a8bb381127d93ca Mon Sep 17 00:00:00 2001 From: Cavin Date: Tue, 10 Jan 2023 16:02:46 +0800 Subject: [PATCH 147/158] Update binary_tree.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化第三段中对“左子树”和“右子树”的描述 --- docs/chapter_tree/binary_tree.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 077c03e0d..1061e019d 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -114,7 +114,7 @@ comments: true 结点的两个指针分别指向「左子结点 Left Child Node」和「右子结点 Right Child Node」,并且称该结点为两个子结点的「父结点 Parent Node」。给定二叉树某结点,将左子结点以下的树称为该结点的「左子树 Left Subtree」,右子树同理。 -除了叶结点外,每个结点都有子结点和子树。例如,若将上图的「结点 2」看作父结点,那么其左子结点和右子结点分别为「结点 4」和「结点 5」,左子树和右子树分别为「结点 4 以下的树」和「结点 5 以下的树」。 +除了叶结点外,每个结点都有子结点和子树。例如,若将上图的「结点 2」看作父结点,那么其左子结点和右子结点分别为「结点 4」和「结点 5」,左子树和右子树分别为「结点 4 及其以下结点形成的树」和「结点 5 及其以下结点形成的树」。 ![binary_tree_definition](binary_tree.assets/binary_tree_definition.png) From 2572b83540edc08e2e64d9e0d2aed2489f371e7e Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Tue, 10 Jan 2023 17:19:21 +0800 Subject: [PATCH 148/158] update zig codes for Section 'Space Complexity' and 'Space Time Tradeoff' --- codes/zig/build.zig | 8 ++++---- .../chapter_computational_complexity/leetcode_two_sum.zig | 4 ++++ .../chapter_computational_complexity/space_complexity.zig | 4 ++++ .../chapter_computational_complexity/time_complexity.zig | 4 ++++ .../worst_best_time_complexity.zig | 4 ++++ codes/zig/include/ListNode.zig | 1 + codes/zig/include/PrintUtil.zig | 1 + codes/zig/include/TreeNode.zig | 1 + 8 files changed, 23 insertions(+), 4 deletions(-) diff --git a/codes/zig/build.zig b/codes/zig/build.zig index e72ca4a53..9584ba4b3 100644 --- a/codes/zig/build.zig +++ b/codes/zig/build.zig @@ -11,7 +11,7 @@ pub fn build(b: *std.build.Builder) void { const mode = b.standardReleaseOptions(); // Section: "Time Complexity" - // File: "chapter_computational_complexity/time_complexity.zig" + // Source File: "chapter_computational_complexity/time_complexity.zig" // Run Command: zig build run_time_complexity const exe_time_complexity = b.addExecutable("time_complexity", "chapter_computational_complexity/time_complexity.zig"); exe_time_complexity.addPackagePath("include", "include/include.zig"); @@ -24,7 +24,7 @@ pub fn build(b: *std.build.Builder) void { const run_step_time_complexity = b.step("run_time_complexity", "Run time_complexity"); run_step_time_complexity.dependOn(&run_cmd_time_complexity.step); - // File: "chapter_computational_complexity/worst_best_time_complexity.zig" + // Source File: "chapter_computational_complexity/worst_best_time_complexity.zig" // Run Command: zig build run_worst_best_time_complexity const exe_worst_best_time_complexity = b.addExecutable("worst_best_time_complexity", "chapter_computational_complexity/worst_best_time_complexity.zig"); exe_worst_best_time_complexity.addPackagePath("include", "include/include.zig"); @@ -38,7 +38,7 @@ pub fn build(b: *std.build.Builder) void { run_step_worst_best_time_complexity.dependOn(&run_cmd_worst_best_time_complexity.step); // Section: "Space Complexity" - // File: "chapter_computational_complexity/space_complexity.zig" + // Source File: "chapter_computational_complexity/space_complexity.zig" // Run Command: zig build run_space_complexity const exe_space_complexity = b.addExecutable("space_complexity", "chapter_computational_complexity/space_complexity.zig"); exe_space_complexity.addPackagePath("include", "include/include.zig"); @@ -52,7 +52,7 @@ pub fn build(b: *std.build.Builder) void { run_step_space_complexity.dependOn(&run_cmd_space_complexity.step); // Section: "Space Time Tradeoff" - // File: "chapter_computational_complexity/leetcode_two_sum.zig" + // Source File: "chapter_computational_complexity/leetcode_two_sum.zig" // Run Command: zig build run_leetcode_two_sum const exe_leetcode_two_sum = b.addExecutable("leetcode_two_sum", "chapter_computational_complexity/leetcode_two_sum.zig"); exe_leetcode_two_sum.addPackagePath("include", "include/include.zig"); diff --git a/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig b/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig index 66b96f95e..dc963b595 100644 --- a/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig +++ b/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig @@ -44,6 +44,10 @@ const SolutionHashMap = struct { // Driver Code pub fn main() !void { + // 查看本地CPU架构和操作系统信息 + var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); + std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); + // ======= Test Case ======= var nums = [_]i32{ 2, 7, 11, 15 }; var target: i32 = 9; diff --git a/codes/zig/chapter_computational_complexity/space_complexity.zig b/codes/zig/chapter_computational_complexity/space_complexity.zig index 9798821a5..3fe81a706 100644 --- a/codes/zig/chapter_computational_complexity/space_complexity.zig +++ b/codes/zig/chapter_computational_complexity/space_complexity.zig @@ -102,6 +102,10 @@ fn buildTree(mem_allocator: std.mem.Allocator, n: i32) !?*inc.TreeNode(i32) { // Driver Code pub fn main() !void { + // 查看本地CPU架构和操作系统信息 + var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); + std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); + const n: i32 = 5; // 常数阶 constant(n); diff --git a/codes/zig/chapter_computational_complexity/time_complexity.zig b/codes/zig/chapter_computational_complexity/time_complexity.zig index dab17034a..ef8d7fe65 100644 --- a/codes/zig/chapter_computational_complexity/time_complexity.zig +++ b/codes/zig/chapter_computational_complexity/time_complexity.zig @@ -139,6 +139,10 @@ fn factorialRecur(n: i32) i32 { // Driver Code pub fn main() void { + // 查看本地CPU架构和操作系统信息 + var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); + std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); + // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 const n: i32 = 8; std.debug.print("输入数据大小 n = {}\n", .{n}); diff --git a/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig b/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig index bd2a1464a..42961aafb 100644 --- a/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig +++ b/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig @@ -28,6 +28,10 @@ pub fn findOne(nums: []i32) i32 { // Driver Code pub fn main() void { + // 查看本地CPU架构和操作系统信息 + var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); + std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); + var i: i32 = 0; while (i < 10) : (i += 1) { const n: usize = 100; diff --git a/codes/zig/include/ListNode.zig b/codes/zig/include/ListNode.zig index 8ce01d3ed..ae0246c09 100644 --- a/codes/zig/include/ListNode.zig +++ b/codes/zig/include/ListNode.zig @@ -5,6 +5,7 @@ const std = @import("std"); // Definition for a singly-linked list node +// 编译期泛型 pub fn ListNode(comptime T: type) type { return struct { const Self = @This(); diff --git a/codes/zig/include/PrintUtil.zig b/codes/zig/include/PrintUtil.zig index 8a7dca7af..9e6b18b05 100644 --- a/codes/zig/include/PrintUtil.zig +++ b/codes/zig/include/PrintUtil.zig @@ -7,6 +7,7 @@ const ListNode = @import("ListNode.zig").ListNode; const TreeNode = @import("TreeNode.zig").TreeNode; // Print an array +// 编译期泛型 pub fn printArray(comptime T: type, nums: []T) void { std.debug.print("[", .{}); if (nums.len > 0) { diff --git a/codes/zig/include/TreeNode.zig b/codes/zig/include/TreeNode.zig index 9bd51ba3d..12af9e8f3 100644 --- a/codes/zig/include/TreeNode.zig +++ b/codes/zig/include/TreeNode.zig @@ -5,6 +5,7 @@ const std = @import("std"); // Definition for a binary tree node +// 编译期泛型 pub fn TreeNode(comptime T: type) type { return struct { const Self = @This(); From 51e52121dce697c1a9a883e63590472354d1c4c5 Mon Sep 17 00:00:00 2001 From: mellowrsa <87369532+mellowrsa@users.noreply.github.com> Date: Tue, 10 Jan 2023 18:05:52 +0800 Subject: [PATCH 149/158] Update time_complexity.md --- .../time_complexity.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 09aee7b79..ea523b46d 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -1434,11 +1434,14 @@ $$ for (int i = n - 1; i > 0; i--) { // 内循环:冒泡操作 for (int j = 0; j < i; j++) { - // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 + if (nums[j] > nums [j+1]) + { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } } } From afaf2de9f4f82a5452d7c290d2967c7db7378fd4 Mon Sep 17 00:00:00 2001 From: danielsss Date: Tue, 10 Jan 2023 21:46:03 +1100 Subject: [PATCH 150/158] removed: .editorconfig --- .editorconfig | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index f57b4f2fb..000000000 --- a/.editorconfig +++ /dev/null @@ -1,14 +0,0 @@ -# EditorConfig is awesome: https://EditorConfig.org - -# top-most EditorConfig file -root = true - -# Unix-style newlines with a newline ending every file -[*] -end_of_line = lf -insert_final_newline = true - -[codes/**.{java,js,ts,go,py,cpp,c,cs,swift,zig}] -charset = utf-8 -indent_style = space -indent_size = 4 From a667e71b2022a48553cc3f151e17b61f17835d53 Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Tue, 10 Jan 2023 19:29:57 +0800 Subject: [PATCH 151/158] fix bug --- .../time_complexity.c | 12 +++++++----- codes/zig/build.zig | 2 +- .../time_complexity.zig | 12 +++++++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/codes/c/chapter_computational_complexity/time_complexity.c b/codes/c/chapter_computational_complexity/time_complexity.c index b8821ec6a..ee827ed14 100644 --- a/codes/c/chapter_computational_complexity/time_complexity.c +++ b/codes/c/chapter_computational_complexity/time_complexity.c @@ -56,11 +56,13 @@ int bubbleSort(int *nums, int n) { for (int i = n - 1; i > 0; i--) { // 内循环:冒泡操作 for (int j = 0; j < i; j++) { - // 交换 nums[j] 与 nums[j + 1] - int tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } } } return count; diff --git a/codes/zig/build.zig b/codes/zig/build.zig index 9584ba4b3..1a9fb0fba 100644 --- a/codes/zig/build.zig +++ b/codes/zig/build.zig @@ -5,7 +5,7 @@ const std = @import("std"); // Zig Version: 0.10.0 -// Zig Codes Build Command: zig build +// Build Command: zig build pub fn build(b: *std.build.Builder) void { const target = b.standardTargetOptions(.{}); const mode = b.standardReleaseOptions(); diff --git a/codes/zig/chapter_computational_complexity/time_complexity.zig b/codes/zig/chapter_computational_complexity/time_complexity.zig index ef8d7fe65..3b45ed79f 100644 --- a/codes/zig/chapter_computational_complexity/time_complexity.zig +++ b/codes/zig/chapter_computational_complexity/time_complexity.zig @@ -59,11 +59,13 @@ fn bubbleSort(nums: []i32) i32 { var j: usize = 0; // 内循环:冒泡操作 while (j < i) : (j += 1) { - // 交换 nums[j] 与 nums[j + 1] - var tmp = nums[j]; - nums[j] = nums[j + 1]; - nums[j + 1] = tmp; - count += 3; // 元素交换包含 3 个单元操作 + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + var tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } } } return count; From 749570da0d63df16777dca82b31c7d1d15fd355b Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Tue, 10 Jan 2023 19:44:04 +0800 Subject: [PATCH 152/158] fix bug --- codes/zig/chapter_computational_complexity/time_complexity.zig | 2 +- .../worst_best_time_complexity.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codes/zig/chapter_computational_complexity/time_complexity.zig b/codes/zig/chapter_computational_complexity/time_complexity.zig index 3b45ed79f..40f609459 100644 --- a/codes/zig/chapter_computational_complexity/time_complexity.zig +++ b/codes/zig/chapter_computational_complexity/time_complexity.zig @@ -140,7 +140,7 @@ fn factorialRecur(n: i32) i32 { } // Driver Code -pub fn main() void { +pub fn main() !void { // 查看本地CPU架构和操作系统信息 var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); diff --git a/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig b/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig index 42961aafb..f9fcd2f67 100644 --- a/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig +++ b/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig @@ -27,7 +27,7 @@ pub fn findOne(nums: []i32) i32 { } // Driver Code -pub fn main() void { +pub fn main() !void { // 查看本地CPU架构和操作系统信息 var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); From 17de5fd5d17637d15ac837beec74983401927b4b Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Mon, 9 Jan 2023 17:25:27 +0800 Subject: [PATCH 153/158] feat(include): add c code --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c960a1bb3..ccb5e1b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ # Editor .vscode/ .idea/ +cmake-build-debug/ +CMakeLists.txt hello-algo.iml # mkdocs files From 24cdcd54df0b2f0c01a4cbf5ece35496a0b0cf08 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Wed, 11 Jan 2023 00:48:48 +0800 Subject: [PATCH 154/158] feat(codes/c): add C include/ codes and modify exist C codes --- .gitignore | 2 +- codes/c/CMakeLists.txt | 11 ++ .../CMakeLists.txt | 1 + codes/c/chapter_array_and_linkedlist/array.c | 10 +- .../CMakeLists.txt | 2 + .../worst_best_time_complexity.c | 3 +- codes/c/chapter_sorting/CMakeLists.txt | 2 + codes/c/chapter_sorting/bubble_sort.c | 4 +- codes/c/chapter_sorting/insertion_sort.c | 2 +- codes/c/include/CMakeLists.txt | 4 + codes/c/include/PrintUtil.h | 28 ---- codes/c/include/include.h | 19 ++- codes/c/include/include_test.c | 38 +++++ codes/c/include/list_node.h | 72 ++++++++++ codes/c/include/print_util.h | 135 ++++++++++++++++++ codes/c/include/tree_node.h | 131 +++++++++++++++++ 16 files changed, 423 insertions(+), 41 deletions(-) create mode 100644 codes/c/CMakeLists.txt create mode 100644 codes/c/chapter_array_and_linkedlist/CMakeLists.txt create mode 100644 codes/c/chapter_computational_complexity/CMakeLists.txt create mode 100644 codes/c/chapter_sorting/CMakeLists.txt create mode 100644 codes/c/include/CMakeLists.txt delete mode 100644 codes/c/include/PrintUtil.h create mode 100644 codes/c/include/include_test.c create mode 100644 codes/c/include/list_node.h create mode 100644 codes/c/include/print_util.h create mode 100644 codes/c/include/tree_node.h diff --git a/.gitignore b/.gitignore index ccb5e1b1e..52026b066 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,8 @@ .vscode/ .idea/ cmake-build-debug/ -CMakeLists.txt hello-algo.iml +*.dSYM/ # mkdocs files site/ diff --git a/codes/c/CMakeLists.txt b/codes/c/CMakeLists.txt new file mode 100644 index 000000000..dc9e56af3 --- /dev/null +++ b/codes/c/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.10) +project(hello_algo C) + +set(CMAKE_C_STANDARD 11) + +include_directories(./include) + +add_subdirectory(include) +add_subdirectory(chapter_computational_complexity) +add_subdirectory(chapter_array_and_linkedlist) +add_subdirectory(chapter_sorting) diff --git a/codes/c/chapter_array_and_linkedlist/CMakeLists.txt b/codes/c/chapter_array_and_linkedlist/CMakeLists.txt new file mode 100644 index 000000000..24658b301 --- /dev/null +++ b/codes/c/chapter_array_and_linkedlist/CMakeLists.txt @@ -0,0 +1 @@ +add_executable(array array.c) \ No newline at end of file diff --git a/codes/c/chapter_array_and_linkedlist/array.c b/codes/c/chapter_array_and_linkedlist/array.c index 33dc99903..11fcb67f9 100644 --- a/codes/c/chapter_array_and_linkedlist/array.c +++ b/codes/c/chapter_array_and_linkedlist/array.c @@ -74,11 +74,11 @@ int main() { int size = 5; int arr[5]; printf("数组 arr = "); - printArray(arr, size); + PrintArray(arr, size); int nums[5] = { 1, 3, 2, 5, 4 }; printf("数组 nums = "); - printArray(nums, size); + PrintArray(nums, size); /* 随机访问 */ int randomNum = randomAccess(nums, size); @@ -89,17 +89,17 @@ int main() { int* res = extend(nums, size, enlarge); size += enlarge; printf("将数组长度扩展至 8 ,得到 nums = "); - printArray(res, size); + PrintArray(res, size); /* 插入元素 */ insert(res, size, 6, 3); printf("在索引 3 处插入数字 6 ,得到 nums = "); - printArray(res, size); + PrintArray(res, size); /* 删除元素 */ removeItem(res, size, 2); printf("删除索引 2 处的元素,得到 nums = "); - printArray(res, size); + PrintArray(res, size); /* 遍历数组 */ traverse(res, size); diff --git a/codes/c/chapter_computational_complexity/CMakeLists.txt b/codes/c/chapter_computational_complexity/CMakeLists.txt new file mode 100644 index 000000000..d9e55dcf1 --- /dev/null +++ b/codes/c/chapter_computational_complexity/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(time_complexity time_complexity.c ) +add_executable(worst_best_time_complexity worst_best_time_complexity.c) \ No newline at end of file diff --git a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c index 2570ff3c3..531a76e51 100644 --- a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c +++ b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c @@ -41,7 +41,7 @@ int main(int argc, char *argv[]) { int *nums = randomNumbers(n); int index = findOne(nums, n); printf("\n数组 [ 1, 2, ..., n ] 被打乱后 = "); - printArray(nums, n); + PrintArray(nums, n); printf("数字 1 的索引为 %d\n", index); // 释放堆区内存 if (nums != NULL) { @@ -49,6 +49,5 @@ int main(int argc, char *argv[]) { nums = NULL; } } - getchar(); return 0; } diff --git a/codes/c/chapter_sorting/CMakeLists.txt b/codes/c/chapter_sorting/CMakeLists.txt new file mode 100644 index 000000000..192e1cb97 --- /dev/null +++ b/codes/c/chapter_sorting/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(bubble_sort bubble_sort.c) +add_executable(insertion_sort insertion_sort.c) \ No newline at end of file diff --git a/codes/c/chapter_sorting/bubble_sort.c b/codes/c/chapter_sorting/bubble_sort.c index 5c74d9e4e..4a2a24b44 100644 --- a/codes/c/chapter_sorting/bubble_sort.c +++ b/codes/c/chapter_sorting/bubble_sort.c @@ -50,14 +50,14 @@ void bubble_sort_with_flag(int nums[], int size) { /* Driver Code */ int main() { int nums[6] = {4, 1, 3, 1, 5, 2}; - printf("冒泡排序后:\n"); + printf("冒泡排序后: "); bubble_sort(nums, 6); for (int i = 0; i < 6; i++) { printf("%d ", nums[i]); } - printf("优化版冒泡排序后:\n"); + printf("\n优化版冒泡排序后: "); bubble_sort_with_flag(nums, 6); for (int i = 0; i < 6; i++) { diff --git a/codes/c/chapter_sorting/insertion_sort.c b/codes/c/chapter_sorting/insertion_sort.c index 80e8b127b..f81b8d43e 100644 --- a/codes/c/chapter_sorting/insertion_sort.c +++ b/codes/c/chapter_sorting/insertion_sort.c @@ -28,7 +28,7 @@ void insertionSort(int nums[], int size) { int main() { int nums[] = {4, 1, 3, 1, 5, 2}; insertionSort(nums, 6); - printf("插入排序完成后 nums = \n"); + printf("插入排序完成后 nums = "); for (int i = 0; i < 6; i++) { printf("%d ", nums[i]); diff --git a/codes/c/include/CMakeLists.txt b/codes/c/include/CMakeLists.txt new file mode 100644 index 000000000..4189ae334 --- /dev/null +++ b/codes/c/include/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(include + include_test.c + include.h print_util.h + list_node.h tree_node.h) \ No newline at end of file diff --git a/codes/c/include/PrintUtil.h b/codes/c/include/PrintUtil.h deleted file mode 100644 index 59a8eac1b..000000000 --- a/codes/c/include/PrintUtil.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * File: PrintUtil.h - * Created Time: 2022-12-21 - * Author: MolDum (moldum@163.com) - */ - -#include -#include -#include - -// #include "ListNode.h" -// #include "TreeNode.h" - -/** - * @brief Print an Array - * - * @param arr - * @param n - */ - -static void printArray(int* arr, int n) -{ - printf("["); - for (int i = 0; i < n - 1; i++) { - printf("%d, ", arr[i]); - } - printf("%d]\n", arr[n-1]); -} diff --git a/codes/c/include/include.h b/codes/c/include/include.h index 2c4fd9252..9f30b0529 100644 --- a/codes/c/include/include.h +++ b/codes/c/include/include.h @@ -1,13 +1,28 @@ /** * File: include.h * Created Time: 2022-12-20 - * Author: MolDuM (moldum@163.com) + * Author: MolDuM (moldum@163.com)、Reanon (793584285@qq.com) */ +#ifndef C_INCLUDE_H +#define C_INCLUDE_H + #include #include #include #include #include -#include "PrintUtil.h" +#include "list_node.h" +#include "tree_node.h" +#include "print_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // C_INCLUDE_H diff --git a/codes/c/include/include_test.c b/codes/c/include/include_test.c new file mode 100644 index 000000000..baa8e649d --- /dev/null +++ b/codes/c/include/include_test.c @@ -0,0 +1,38 @@ +/** + * File: include_test.c + * Created Time: 2023-01-10 + * Author: Reanon (793584285@qq.com) + */ + +#include "include.h" + +void testListNode() { + int nums[] = {2, 3, 5, 6, 7}; + int size = sizeof(nums) / sizeof(int); + ListNode *head = ArrayToLinkedList(nums, size); + PrintLinkedList(head); + + ListNode *node = GetListNode(head, 5); + printf("find node: %d\n", node->val); +} + +void testTreeNode() { + int nums[] = {1, 2, 3, NIL, 5, 6, NIL}; + int size = sizeof(nums) / sizeof(int); + TreeNode *root = ArrayToTree(nums, size); + + // print tree + PrintTree(root); + + // tree to arr + int *arr = TreeToArray(root); + PrintArray(arr, size); +} + +int main(int argc, char *argv[]) { + printf("==testListNode==\n"); + testListNode(); + printf("==testTreeNode==\n"); + testTreeNode(); + return 0; +} diff --git a/codes/c/include/list_node.h b/codes/c/include/list_node.h new file mode 100644 index 000000000..5597d2102 --- /dev/null +++ b/codes/c/include/list_node.h @@ -0,0 +1,72 @@ +/** + * File: list_node.h + * Created Time: 2023-01-09 + * Author: Reanon (793584285@qq.com) + */ +#ifndef LIST_NODE_H +#define LIST_NODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Definition for a singly-linked list node + * + */ +struct ListNode { + int val; // 结点值 + struct ListNode *next; // 指向下一结点的指针(引用) +}; + +// typedef 为 C 语言的关键字,作用是为一种数据类型定义一个新名字 +typedef struct ListNode ListNode; + +ListNode *NewListNode(int val) { + ListNode *node, *next; + node = (ListNode *) malloc(sizeof(ListNode)); + node->val = val; + node->next = NULL; + return node; +} + +/** + * @brief Generate a linked list with a vector + * + * @param list + * @return ListNode* + */ + +ListNode *ArrayToLinkedList(const int *arr, size_t size) { + if (size <= 0) { + return NULL; + } + + ListNode *dummy = NewListNode(0); + ListNode *node = dummy; + for (int i = 0; i < size; i++) { + node->next = NewListNode(arr[i]); + node = node->next; + } + return dummy->next; +} + +/** + * @brief Get a list node with specific value from a linked list + * + * @param head + * @param val + * @return ListNode* + */ +ListNode *GetListNode(ListNode *head, int val) { + while (head != NULL && head->val != val) { + head = head->next; + } + return head; +} + +#ifdef __cplusplus +} +#endif + +#endif // LIST_NODE_H \ No newline at end of file diff --git a/codes/c/include/print_util.h b/codes/c/include/print_util.h new file mode 100644 index 000000000..fcb0a4ca6 --- /dev/null +++ b/codes/c/include/print_util.h @@ -0,0 +1,135 @@ +/** + * File: print_util.h + * Created Time: 2022-12-21 + * Author: MolDum (moldum@163.com)、Reanon (793584285@qq.com) + */ + +#ifndef PRINT_UTIL_H +#define PRINT_UTIL_H + +#include +#include +#include + +#include "list_node.h" +#include "tree_node.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @brief Print an Array + * + * @param arr + * @param size + */ +static void PrintArray(int *arr, int size) { + printf("["); + for (int i = 0; i < size - 1; i++) { + if (arr[i] != NIL) { + printf("%d, ", arr[i]); + } else { + printf("NULL, "); + } + } + if (arr[size - 1] != NIL) { + printf("%d]\n", arr[size - 1]); + }else{ + printf("NULL]\n"); + } +} + +/** + * @brief Print a linked list + * + * @param head + */ +static void PrintLinkedList(ListNode *node) { + if (node == NULL) { + return; + } + while (node->next != NULL) { + printf("%d -> ", node->val); + node = node->next; + } + printf("%d\n", node->val); +} + +struct Trunk { + struct Trunk *prev; + char *str; +}; + +typedef struct Trunk Trunk; + +Trunk *newTrunk(Trunk *prev, char *str) { + Trunk *trunk = (Trunk *) malloc(sizeof(Trunk)); + trunk->prev = prev; + trunk->str = (char *) malloc(sizeof(char) * 10); + strcpy(trunk->str, str); + return trunk; +} + +/** + * @brief Helper function to print branches of the binary tree + * + * @param trunk + */ +void showTrunks(Trunk *trunk) { + if (trunk == NULL) { + return; + } + showTrunks(trunk->prev); + printf("%s", trunk->str); +} + +/** + * Help to print a binary tree, hide more details + * @param node + * @param prev + * @param isLeft + */ +static void printTreeHelper(TreeNode *node, Trunk *prev, bool isLeft) { + if (node == NULL) { + return; + } + char *prev_str = " "; + Trunk *trunk = newTrunk(prev, prev_str); + printTreeHelper(node->right, trunk, true); + if (prev == NULL) { + trunk->str = "———"; + } else if (isLeft) { + trunk->str = "/———"; + prev_str = " |"; + } else { + trunk->str = "\\———"; + prev->str = prev_str; + } + showTrunks(trunk); + printf("%d\n", node->val); + + if (prev != NULL) { + prev->str = prev_str; + } + trunk->str = " |"; + + printTreeHelper(node->left, trunk, false); +} + +/** + * @brief Print a binary tree + * + * @param head + */ +static void PrintTree(TreeNode *root) { + printTreeHelper(root, NULL, false); +} + + +#ifdef __cplusplus +} +#endif + +#endif // PRINT_UTIL_H diff --git a/codes/c/include/tree_node.h b/codes/c/include/tree_node.h new file mode 100644 index 000000000..4b0e70187 --- /dev/null +++ b/codes/c/include/tree_node.h @@ -0,0 +1,131 @@ +/** + * File: tree_node.h + * Created Time: 2023-01-09 + * Author: Reanon (793584285@qq.com) + */ + + +#ifndef TREE_NODE_H +#define TREE_NODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define NIL ('#') +#define MAX_NODE_SIZE 5000 + +struct TreeNode { + int val; + int height; + struct TreeNode *left; + struct TreeNode *right; +}; + +typedef struct TreeNode TreeNode; + + +TreeNode *NewTreeNode(int val) { + TreeNode *node; + + node = (TreeNode *) malloc(sizeof(TreeNode)); + node->val = val; + node->height = 0; + node->left = NULL; + node->right = NULL; + return node; +} + +/** + * @brief Generate a binary tree with an array + * + * @param arr + * @param size + * @return TreeNode * + */ +TreeNode *ArrayToTree(const int *arr, size_t size) { + if (size <= 0) { + return NULL; + } + + int front, rear, index; + TreeNode *root, *node; + TreeNode **queue; + + /* 根结点 */ + root = NewTreeNode(arr[0]); + /* 辅助队列 */ + queue = (TreeNode **) malloc(sizeof(TreeNode) * MAX_NODE_SIZE); + // 队列指针 + front = 0, rear = 0; + // 将根结点放入队尾 + queue[rear++] = root; + // 记录遍历数组的索引 + index = 0; + while (front < rear) { + // 取队列中的头结点,并让头结点出队 + node = queue[front++]; + index++; + if (index < size) { + if (arr[index] != NIL) { + node->left = NewTreeNode(arr[index]); + queue[rear++] = node->left; + } + } + index++; + if (index < size) { + if (arr[index] != NIL) { + node->right = NewTreeNode(arr[index]); + queue[rear++] = node->right; + } + } + } + return root; +} + + +/** + * @brief Generate a binary tree with an array + * + * @param arr + * @param size + * @return TreeNode * + */ +int *TreeToArray(TreeNode *root) { + if (root == NULL) { + return NULL; + } + int front, rear; + int index, *arr; + TreeNode *node; + TreeNode **queue; + /* 辅助队列 */ + queue = (TreeNode **) malloc(sizeof(TreeNode) * MAX_NODE_SIZE); + // 队列指针 + front = 0, rear = 0; + // 将根结点放入队尾 + queue[rear++] = root; + /* 辅助数组 */ + arr = (int *) malloc(sizeof(int) * MAX_NODE_SIZE); + // 数组指针 + index = 0; + while (front < rear) { + // 取队列中的头结点,并让头结点出队 + node = queue[front++]; + if (node != NULL) { + arr[index] = node->val; + queue[rear++] = node->left; + queue[rear++] = node->right; + } else { + arr[index] = NIL; + } + index++; + } + return arr; +} + +#ifdef __cplusplus +} +#endif + +#endif // TREE_NODE_H From 8723ca14694b36e00e729739aa1f594eef534814 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Wed, 11 Jan 2023 01:56:08 +0800 Subject: [PATCH 155/158] feat(tree): add C codes --- codes/c/CMakeLists.txt | 1 + codes/c/chapter_tree/CMakeLists.txt | 4 ++ codes/c/chapter_tree/binary_search_tree.c | 8 +++ codes/c/chapter_tree/binary_tree.c | 43 +++++++++++ codes/c/chapter_tree/binary_tree_bfs.c | 66 +++++++++++++++++ codes/c/chapter_tree/binary_tree_dfs.c | 72 +++++++++++++++++++ .../chapter_tree/binary_search_tree_test.go | 2 +- 7 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 codes/c/chapter_tree/CMakeLists.txt create mode 100644 codes/c/chapter_tree/binary_search_tree.c create mode 100644 codes/c/chapter_tree/binary_tree.c create mode 100644 codes/c/chapter_tree/binary_tree_bfs.c create mode 100644 codes/c/chapter_tree/binary_tree_dfs.c diff --git a/codes/c/CMakeLists.txt b/codes/c/CMakeLists.txt index dc9e56af3..1a208dd9d 100644 --- a/codes/c/CMakeLists.txt +++ b/codes/c/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(include) add_subdirectory(chapter_computational_complexity) add_subdirectory(chapter_array_and_linkedlist) add_subdirectory(chapter_sorting) +add_subdirectory(chapter_tree) diff --git a/codes/c/chapter_tree/CMakeLists.txt b/codes/c/chapter_tree/CMakeLists.txt new file mode 100644 index 000000000..779315b7b --- /dev/null +++ b/codes/c/chapter_tree/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(binary_search binary_tree.c) +add_executable(binary_tree_bfs binary_tree_bfs.c) +add_executable(binary_tree_dfs binary_tree_dfs.c) +add_executable(binary_search_tree binary_search_tree.c) diff --git a/codes/c/chapter_tree/binary_search_tree.c b/codes/c/chapter_tree/binary_search_tree.c new file mode 100644 index 000000000..f993faec3 --- /dev/null +++ b/codes/c/chapter_tree/binary_search_tree.c @@ -0,0 +1,8 @@ +/** + * File: binary_search_tree.c + * Created Time: 2023-01-11 + * Author: Reanon (793584285@qq.com) + */ + +#include "../include/include.h" + diff --git a/codes/c/chapter_tree/binary_tree.c b/codes/c/chapter_tree/binary_tree.c new file mode 100644 index 000000000..0a2f946a7 --- /dev/null +++ b/codes/c/chapter_tree/binary_tree.c @@ -0,0 +1,43 @@ +/** + * File: binary_tree.c + * Created Time: 2023-01-11 + * Author: Reanon (793584285@qq.com) + */ + +#include "../include/include.h" + +/* Driver Code */ +int main() { + /* 初始化二叉树 */ + // 初始化结点 + TreeNode* n1 = NewTreeNode(1); + TreeNode* n2 = NewTreeNode(2); + TreeNode* n3 = NewTreeNode(3); + TreeNode* n4 = NewTreeNode(4); + TreeNode* n5 = NewTreeNode(5); + // 构建引用指向(即指针) + n1->left = n2; + n1->right = n3; + n2->left = n4; + n2->right = n5; + printf("初始化二叉树\n"); + PrintTree(n1); + + /* 插入与删除结点 */ + TreeNode* P = NewTreeNode(0); + // 在 n1 -> n2 中间插入结点 P + n1->left = P; + P->left = n2; + printf("插入结点 P 后\n"); + PrintTree(n1); + + // 删除结点 P + n1->left = n2; + // 释放内存 + free(P); + printf("删除结点 P 后\n"); + PrintTree(n1); + + return 0; +} + diff --git a/codes/c/chapter_tree/binary_tree_bfs.c b/codes/c/chapter_tree/binary_tree_bfs.c new file mode 100644 index 000000000..abad3735f --- /dev/null +++ b/codes/c/chapter_tree/binary_tree_bfs.c @@ -0,0 +1,66 @@ +/** + * File: binary_tree_bfs.c + * Created Time: 2023-01-11 + * Author: Reanon (793584285@qq.com) + */ + +#include "../include/include.h" + +/* 层序遍历 */ +int *levelOrder(TreeNode *root, int *size) { + /* 辅助队列 */ + int front, rear; + int index, *arr; + TreeNode *node; + TreeNode **queue; + + /* 辅助队列 */ + queue = (TreeNode **) malloc(sizeof(TreeNode) * MAX_NODE_SIZE); + // 队列指针 + front = 0, rear = 0; + // 加入根结点 + queue[rear++] = root; + // 初始化一个列表,用于保存遍历序列 + /* 辅助数组 */ + arr = (int *) malloc(sizeof(int) * MAX_NODE_SIZE); + // 数组指针 + index = 0; + while (front < rear) { + // 队列出队 + node = queue[front++]; + // 保存结点 + arr[index++] = node->val; + if (node->left != NULL) { + // 左子结点入队 + queue[rear++] = node->left; + } + if (node->right != NULL) { + // 右子结点入队 + queue[rear++] = node->right; + } + } + // 更新数组长度的值 + *size = index; + arr = realloc(arr, sizeof(int) * (*size)); + return arr; +} + + +/* Driver Code */ +int main() { + /* 初始化二叉树 */ + // 这里借助了一个从数组直接生成二叉树的函数 + int nums[] = {1, 2, 3, NIL, 5, 6, NIL}; + int size = sizeof(nums) / sizeof(int); + TreeNode *root = ArrayToTree(nums, size); + printf("初始化二叉树\n"); + PrintTree(root); + + /* 层序遍历 */ + // 需要传入数组的长度 + int *arr = levelOrder(root, &size); + printf("层序遍历的结点打印序列 = "); + PrintArray(arr, size); + + return 0; +} \ No newline at end of file diff --git a/codes/c/chapter_tree/binary_tree_dfs.c b/codes/c/chapter_tree/binary_tree_dfs.c new file mode 100644 index 000000000..f72e6687b --- /dev/null +++ b/codes/c/chapter_tree/binary_tree_dfs.c @@ -0,0 +1,72 @@ +/** + * File: binary_tree_dfs.c + * Created Time: 2023-01-11 + * Author: Reanon (793584285@qq.com) + */ + +#include "../include/include.h" + +/* 辅助数组,用于存储遍历序列 */ +int *arr; + +/* 前序遍历 */ +void preOrder(TreeNode *root, int *size) { + + if (root == NULL) return; + // 访问优先级:根结点 -> 左子树 -> 右子树 + arr[(*size)++] = root->val; + preOrder(root->left, size); + preOrder(root->right, size); +} + +/* 中序遍历 */ +void inOrder(TreeNode *root, int *size) { + if (root == NULL) return; + // 访问优先级:左子树 -> 根结点 -> 右子树 + inOrder(root->left, size); + arr[(*size)++] = root->val; + inOrder(root->right, size); +} + +/* 后序遍历 */ +void postOrder(TreeNode *root, int *size) { + if (root == NULL) return; + // 访问优先级:左子树 -> 右子树 -> 根结点 + postOrder(root->left, size); + postOrder(root->right, size); + arr[(*size)++] = root->val; +} + + +/* Driver Code */ +int main() { + /* 初始化二叉树 */ + // 这里借助了一个从数组直接生成二叉树的函数 + int nums[] = {1, 2, 3, 4, 5, 6, 7}; + int size = sizeof(nums) / sizt ceof(int); + TreeNode *root = ArrayToTree(nums, size); + printf("初始化二叉树\n"); + PrintTree(root); + + /* 前序遍历 */ + // 初始化辅助数组 + arr = (int *) malloc(sizeof(int) * MAX_NODE_SIZE); + size = 0; + preOrder(root, &size); + printf("前序遍历的结点打印序列 = "); + PrintArray(arr, size); + + /* 中序遍历 */ + size = 0; + inOrder(root, &size); + printf("中序遍历的结点打印序列 = "); + PrintArray(arr, size); + + /* 后序遍历 */ + size = 0; + postOrder(root, &size); + printf("后序遍历的结点打印序列 = "); + PrintArray(arr, size); + + return 0; +} diff --git a/codes/go/chapter_tree/binary_search_tree_test.go b/codes/go/chapter_tree/binary_search_tree_test.go index 2109d5a44..2a864d138 100644 --- a/codes/go/chapter_tree/binary_search_tree_test.go +++ b/codes/go/chapter_tree/binary_search_tree_test.go @@ -20,7 +20,7 @@ func TestBinarySearchTree(t *testing.T) { fmt.Println("\n二叉树的根结点为:", node.Val) // 查找结点 - node = bst.Search(7) + node = bst.search(7) fmt.Println("查找到的结点对象为", node, ",结点值 =", node.Val) // 插入结点 From 483925686f7ac8103e88ec02c2f841d82e0b5153 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Wed, 11 Jan 2023 02:45:28 +0800 Subject: [PATCH 156/158] Update time_complexity.md --- docs/chapter_computational_complexity/time_complexity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index ea523b46d..def052c92 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -1434,7 +1434,7 @@ $$ for (int i = n - 1; i > 0; i--) { // 内循环:冒泡操作 for (int j = 0; j < i; j++) { - if (nums[j] > nums [j+1]) + if (nums[j] > nums [j + 1]) { // 交换 nums[j] 与 nums[j + 1] int tmp = nums[j]; From 3e9edf7115b87cad90077b47d185b37a337a7a03 Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Wed, 11 Jan 2023 12:34:54 +0800 Subject: [PATCH 157/158] delete system info output in zig codes --- .../zig/chapter_computational_complexity/leetcode_two_sum.zig | 4 ---- .../zig/chapter_computational_complexity/space_complexity.zig | 4 ---- .../zig/chapter_computational_complexity/time_complexity.zig | 4 ---- .../worst_best_time_complexity.zig | 4 ---- 4 files changed, 16 deletions(-) diff --git a/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig b/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig index dc963b595..66b96f95e 100644 --- a/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig +++ b/codes/zig/chapter_computational_complexity/leetcode_two_sum.zig @@ -44,10 +44,6 @@ const SolutionHashMap = struct { // Driver Code pub fn main() !void { - // 查看本地CPU架构和操作系统信息 - var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); - std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); - // ======= Test Case ======= var nums = [_]i32{ 2, 7, 11, 15 }; var target: i32 = 9; diff --git a/codes/zig/chapter_computational_complexity/space_complexity.zig b/codes/zig/chapter_computational_complexity/space_complexity.zig index 3fe81a706..9798821a5 100644 --- a/codes/zig/chapter_computational_complexity/space_complexity.zig +++ b/codes/zig/chapter_computational_complexity/space_complexity.zig @@ -102,10 +102,6 @@ fn buildTree(mem_allocator: std.mem.Allocator, n: i32) !?*inc.TreeNode(i32) { // Driver Code pub fn main() !void { - // 查看本地CPU架构和操作系统信息 - var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); - std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); - const n: i32 = 5; // 常数阶 constant(n); diff --git a/codes/zig/chapter_computational_complexity/time_complexity.zig b/codes/zig/chapter_computational_complexity/time_complexity.zig index 40f609459..047950052 100644 --- a/codes/zig/chapter_computational_complexity/time_complexity.zig +++ b/codes/zig/chapter_computational_complexity/time_complexity.zig @@ -141,10 +141,6 @@ fn factorialRecur(n: i32) i32 { // Driver Code pub fn main() !void { - // 查看本地CPU架构和操作系统信息 - var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); - std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); - // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 const n: i32 = 8; std.debug.print("输入数据大小 n = {}\n", .{n}); diff --git a/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig b/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig index f9fcd2f67..0cdd61ebc 100644 --- a/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig +++ b/codes/zig/chapter_computational_complexity/worst_best_time_complexity.zig @@ -28,10 +28,6 @@ pub fn findOne(nums: []i32) i32 { // Driver Code pub fn main() !void { - // 查看本地CPU架构和操作系统信息 - var native_target_info = try std.zig.system.NativeTargetInfo.detect(std.zig.CrossTarget{}); - std.debug.print("Native Info: CPU Arch = {}, OS = {}\n", .{native_target_info.target.cpu.arch, native_target_info.target.os.tag}); - var i: i32 = 0; while (i < 10) : (i += 1) { const n: usize = 100; From a482f8fd9b122873f831d8161a60eb59f02b57d1 Mon Sep 17 00:00:00 2001 From: JoseHung <1309716944@qq.com> Date: Wed, 11 Jan 2023 14:44:32 +0800 Subject: [PATCH 158/158] JS code identation of binary search --- docs/chapter_searching/binary_search.md | 52 ++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index 2bd2d4396..da60b3b43 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -141,20 +141,20 @@ $$ ```js title="binary_search.js" /* 二分查找(双闭区间) */ function binarySearch(nums, target) { - // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 - let i = 0, j = nums.length - 1; - // 循环,当搜索区间为空时跳出(当 i > j 时为空) - while (i <= j) { - let m = parseInt((i + j) / 2); // 计算中点索引 m ,在 JS 中需使用 parseInt 函数取整 - if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 - i = m + 1; - else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中 - j = m - 1; - else - return m; // 找到目标元素,返回其索引 + // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + let i = 0, j = nums.length - 1; + // 循环,当搜索区间为空时跳出(当 i > j 时为空) + while (i <= j) { + let m = parseInt((i + j) / 2); // 计算中点索引 m ,在 JS 中需使用 parseInt 函数取整 + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 + i = m + 1; + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中 + j = m - 1; + else + return m; // 找到目标元素,返回其索引 } - // 未找到目标元素,返回 -1 - return -1; + // 未找到目标元素,返回 -1 + return -1; } ``` @@ -311,20 +311,20 @@ $$ ```js title="binary_search.js" /* 二分查找(左闭右开) */ function binarySearch1(nums, target) { - // 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 - let i = 0, j = nums.length; - // 循环,当搜索区间为空时跳出(当 i = j 时为空) - while (i < j) { - let m = parseInt((i + j) / 2); // 计算中点索引 m ,在 JS 中需使用 parseInt 函数取整 - if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 - i = m + 1; - else if (nums[m] > target) // 此情况说明 target 在区间 [i, m] 中 - j = m; - else // 找到目标元素,返回其索引 - return m; + // 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + let i = 0, j = nums.length; + // 循环,当搜索区间为空时跳出(当 i = j 时为空) + while (i < j) { + let m = parseInt((i + j) / 2); // 计算中点索引 m ,在 JS 中需使用 parseInt 函数取整 + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 + i = m + 1; + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m] 中 + j = m; + else // 找到目标元素,返回其索引 + return m; } - // 未找到目标元素,返回 -1 - return -1; + // 未找到目标元素,返回 -1 + return -1; } ```