diff --git a/codes/c/chapter_sorting/bubble_sort.c b/codes/c/chapter_sorting/bubble_sort.c new file mode 100644 index 00000000..6b67bd72 --- /dev/null +++ b/codes/c/chapter_sorting/bubble_sort.c @@ -0,0 +1,72 @@ +/** + * File: bubble_sort.c + * Created Time: 2022-12-26 + * Author: Listening (https://github.com/L-Super) + */ + +#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; + } +} + + +/* Driver Code */ +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 00000000..44843254 --- /dev/null +++ b/codes/c/include/include.h @@ -0,0 +1,2 @@ +#include +#include \ No newline at end of file diff --git a/codes/cpp/chapter_sorting/bubble_sort.cpp b/codes/cpp/chapter_sorting/bubble_sort.cpp index 07182745..87ca305d 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/codes/cpp/chapter_tree/binary_tree_bfs.cpp b/codes/cpp/chapter_tree/binary_tree_bfs.cpp index eeec2a18..30c2d600 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/avl_tree.py b/codes/python/chapter_tree/avl_tree.py new file mode 100644 index 00000000..d0ff48cb --- /dev/null +++ b/codes/python/chapter_tree/avl_tree.py @@ -0,0 +1,208 @@ +""" +File: avl_tree.py +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 AVLTree: + def __init__(self, root: typing.Optional[TreeNode] = None): + self.root = root + + """ 获取结点高度 """ + 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: 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: + return 0 + # 结点平衡因子 = 左子树高度 - 右子树高度 + return self.height(node.left) - self.height(node.right) + + """ 右旋操作 """ + def __right_rotate(self, node: TreeNode) -> TreeNode: + 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: TreeNode) -> TreeNode: + 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: TreeNode) -> TreeNode: + # 获取结点 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) -> 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) + # 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): + 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 + # 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[TreeNode]) -> typing.Optional[TreeNode]: + 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: + # 目标结点在 root 的右子树中 + if cur.val < val: + cur = cur.right + # 目标结点在 root 的左子树中 + elif cur.val > val: + cur = cur.left + # 找到目标结点,跳出循环 + else: + break + # 返回目标结点 + return cur + + +""" Driver Code """ +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 0f855f27..23e34185 100644 --- a/codes/python/chapter_tree/binary_search_tree.py +++ b/codes/python/chapter_tree/binary_search_tree.py @@ -1,10 +1,167 @@ """ 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: 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 + + # 将数组中间结点作为根结点 + mid = (start_index + end_index) // 2 + root = TreeNode(nums[mid]) + # 递归建立左子树和右子树 + 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 + + @property + def root(self) -> typing.Optional[TreeNode]: + return self.__root + + """ 查找结点 """ + def search(self, num: int) -> typing.Optional[TreeNode]: + cur = self.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: int) -> typing.Optional[TreeNode]: + root = self.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: int) -> typing.Optional[TreeNode]: + root = self.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: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: + if root is None: + return root + + # 循环访问左子结点,直到叶结点时为最小结点,跳出 + while root.left is not None: + root = root.left + return root + + +""" Driver Code """ +if __name__ == "__main__": + # 初始化二叉搜索树 + nums = list(range(1, 16)) + bst = BinarySearchTree(nums=nums) + print("\n初始化的二叉树为\n") + print_tree(bst.root) + + # 查找结点 + node = bst.search(5) + print("\n查找到的结点对象为: {},结点值 = {}".format(node, node.val)) + + # 插入结点 + ndoe = bst.insert(16) + print("\n插入结点 16 后,二叉树为\n") + print_tree(bst.root) + + # 删除结点 + bst.remove(1) + print("\n删除结点 1 后,二叉树为\n") + print_tree(bst.root) + + bst.remove(2) + print("\n删除结点 2 后,二叉树为\n") + print_tree(bst.root) + + bst.remove(4) + print("\n删除结点 4 后,二叉树为\n") + print_tree(bst.root) diff --git a/codes/python/chapter_tree/binary_tree.py b/codes/python/chapter_tree/binary_tree.py index 81828b5c..d9902635 100644 --- a/codes/python/chapter_tree/binary_tree.py +++ b/codes/python/chapter_tree/binary_tree.py @@ -1,10 +1,40 @@ """ 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 + 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("\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"); + print_tree(n1) diff --git a/codes/python/chapter_tree/binary_tree_bfs.py b/codes/python/chapter_tree/binary_tree_bfs.py index 9ae1b359..0320a08d 100644 --- a/codes/python/chapter_tree/binary_tree_bfs.py +++ b/codes/python/chapter_tree/binary_tree_bfs.py @@ -1,10 +1,42 @@ """ 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 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 + + +""" 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) + + # 层序遍历 + 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 094bd4cb..11ee8339 100644 --- a/codes/python/chapter_tree/binary_tree_dfs.py +++ b/codes/python/chapter_tree/binary_tree_dfs.py @@ -1,10 +1,68 @@ """ 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 * + +res = [] + +""" 前序遍历 """ +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) + + +""" 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) + + # 前序遍历 + res.clear() + pre_order(root) + print("\n前序遍历的结点打印序列 = ", res) + assert res == [1, 2, 4, 5, 3, 6, 7] + + # 中序遍历 + res.clear() + in_order(root) + print("\n中序遍历的结点打印序列 = ", res) + assert res == [4, 2, 5, 1, 6, 3, 7] + + # 后序遍历 + res.clear() + post_order(root) + print("\n后序遍历的结点打印序列 = ", res) + assert res == [4, 5, 2, 6, 7, 3, 1] diff --git a/codes/python/include/binary_tree.py b/codes/python/include/binary_tree.py index de2569e4..670fd18c 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=0, left=None, right=None): - self.val = val - self.left = left - self.right = right + self.val = val # 结点值 + self.height = 0 # 结点高度 + 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) + + __repr__ = __str__ + def list_to_tree(arr): """Generate a binary tree with a list 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 00000000..38bac4fa --- /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 00000000..73db6954 --- /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 8841a613..aafb0d89 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 我们在实际应用中很少使用「最佳时间复杂度」,因为往往只有很小概率下才能达到,会带来一定的误导性。反之,「最差时间复杂度」最为实用,因为它给出了一个“效率安全值”,让我们可以放心地使用算法。 diff --git a/docs/chapter_preface/installation.md b/docs/chapter_preface/installation.md index b58457c7..c6c1b2ee 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)。 diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index f44b1192..336f3c2d 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -85,9 +85,8 @@ 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() 函数 + swap(nums[j], nums[j + 1]); } } } @@ -170,7 +169,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 +266,8 @@ 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() 函数 + swap(nums[j], nums[j + 1]); flag = true; // 记录交换元素 } } @@ -352,7 +367,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(!flag) break; + } + } ``` === "C#" diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index 2b1ad08f..e9a656be 100644 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -48,7 +48,13 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Python" ```python title="avl_tree.py" - + """ AVL 树结点类 """ + class TreeNode: + def __init__(self, val=None, left=None, right=None): + self.val = val # 结点值 + self.height = 0 # 结点高度 + self.left = left # 左子结点引用 + self.right = right # 右子结点引用 ``` === "Go" @@ -115,7 +121,17 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Python" ```python title="avl_tree.py" - + """ 获取结点高度 """ + 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: TreeNode): + # 结点高度等于最高子树高度 + 1 + node.height = max([self.height(node.left), self.height(node.right)]) + 1 ``` === "Go" @@ -185,7 +201,13 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "Python" ```python title="avl_tree.py" - + """ 获取平衡因子 """ + def balance_factor(self, node: TreeNode) -> int: + # 空结点平衡因子为 0 + if node is None: + return 0 + # 结点平衡因子 = 左子树高度 - 右子树高度 + return self.height(node.left) - self.height(node.right) ``` === "Go" @@ -281,7 +303,18 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - + """ 右旋操作 """ + def __right_rotate(self, node: TreeNode) -> TreeNode: + 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 ``` === "Go" @@ -363,7 +396,18 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - + """ 左旋操作 """ + def __left_rotate(self, node: TreeNode) -> TreeNode: + 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 ``` === "Go" @@ -485,7 +529,30 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - + """ 执行旋转操作,使该子树重新恢复平衡 """ + def __rotate(self, node: TreeNode) -> TreeNode: + # 获取结点 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 ``` === "Go" @@ -597,7 +664,27 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - + """ 插入结点 """ + 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) + # 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) ``` === "Go" @@ -717,7 +804,46 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "Python" ```python title="avl_tree.py" - + """ 删除结点 """ + 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 + # 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[TreeNode]) -> typing.Optional[TreeNode]: + 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 c2551e74..b341e3dc 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -82,7 +82,21 @@ comments: true === "Python" ```python title="binary_search_tree.py" - + """ 查找结点 """ + def search(self, num: int) -> typing.Optional[TreeNode]: + cur = self.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 ``` === "Go" @@ -244,7 +258,35 @@ comments: true === "Python" ```python title="binary_search_tree.py" + """ 插入结点 """ + def insert(self, num: int) -> typing.Optional[TreeNode]: + root = self.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" @@ -525,7 +567,60 @@ comments: true === "Python" ```python title="binary_search_tree.py" + """ 删除结点 """ + def remove(self, num: int) -> typing.Optional[TreeNode]: + root = self.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: typing.Optional[TreeNode]) -> typing.Optional[TreeNode]: + if root is None: + return root + + # 循环访问左子结点,直到叶结点时为最小结点,跳出 + while root.left is not None: + root = root.left + return root ``` === "Go" @@ -761,14 +856,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$ 很大时有巨大优势**。 diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 22478b84..07e31155 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -35,7 +35,7 @@ comments: true ```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 # 右子结点指针 @@ -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, @@ -175,7 +175,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" @@ -284,7 +295,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" @@ -437,7 +454,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 971d24cc..f4634841 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