diff --git a/.gitattributes b/.gitattributes index 97582fc5..4f3f0df0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,12 +3,12 @@ *.py linguist-language=Python *.cpp linguist-language=C++ *.c linguist-language=C++ +*.zig linguist-language=C++ *.go linguist-language=Go *.js linguist-language=JavaScript *.ts linguist-language=JavaScript *.swift linguist-language=Swift -*.zig linguist-language=Other *.rs linguist-language=Other *.html linguist-detectable=false diff --git a/codes/c/CMakeLists.txt b/codes/c/CMakeLists.txt index 932d74fd..623a0c15 100644 --- a/codes/c/CMakeLists.txt +++ b/codes/c/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(chapter_array_and_linkedlist) add_subdirectory(chapter_sorting) add_subdirectory(chapter_tree) add_subdirectory(chapter_stack_and_queue) +add_subdirectory(chapter_heap) \ No newline at end of file diff --git a/codes/c/chapter_heap/CMakeLists.txt b/codes/c/chapter_heap/CMakeLists.txt new file mode 100644 index 00000000..c94ec9c1 --- /dev/null +++ b/codes/c/chapter_heap/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(my_heap my_heap.c) + diff --git a/codes/c/chapter_heap/my_heap.c b/codes/c/chapter_heap/my_heap.c new file mode 100644 index 00000000..43f4dc27 --- /dev/null +++ b/codes/c/chapter_heap/my_heap.c @@ -0,0 +1,185 @@ +/** + * File: my_heap.c + * Created Time: 2023-01-15 + * Author: Reanon (793584285@qq.com) + */ + +#include "../include/include.h" + +#define MAX_SIZE 5000 + +// 大顶堆 +typedef struct maxHeap { + // size 代表的是实际元素的个数 + int size; + // 使用预先分配内存的数组,避免扩容 + int data[MAX_SIZE]; +} maxHeap; + +void siftDown(maxHeap *h, int i); + +void siftUp(maxHeap *h, int i); + +/* 构造空堆 */ +maxHeap *newEmptyMaxHeap() { + // 所有元素入堆 + maxHeap *h = (maxHeap *) malloc(sizeof(maxHeap)); + h->size = 0; + return h; +} + +/* 构造函数,根据切片建堆 */ +maxHeap *newMaxHeap(int nums[], int size) { + // 所有元素入堆 + maxHeap *h = (maxHeap *) malloc(sizeof(maxHeap)); + h->size = size; + memcpy(h->data, nums, size * sizeof(int)); + for (int i = size - 1; i >= 0; i--) { + // 堆化除叶结点以外的其他所有结点 + siftDown(h, i); + } + return h; +} + +/* 获取左子结点索引 */ +int left(maxHeap *h, int i) { + return 2 * i + 1; +} + +/* 获取右子结点索引 */ +int right(maxHeap *h, int i) { + return 2 * i + 2; +} + +/* 获取父结点索引 */ +int parent(maxHeap *h, int i) { + return (i - 1) / 2; +} + +/* 交换元素 */ +int swap(maxHeap *h, int i, int j) { + int temp = h->data[i]; + h->data[i] = h->data[j]; + h->data[j] = temp; +} + +/* 获取堆大小 */ +int size(maxHeap *h) { + return h->size; +} + +/* 判断堆是否为空 */ +int isEmpty(maxHeap *h) { + return h->size == 0; +} + +/* 访问堆顶元素 */ +int peek(maxHeap *h) { + return h->data[0]; +} + +/* 元素入堆 */ +int push(maxHeap *h, int val) { + // 默认情况下,不应该添加这么多结点 + if (h->size == MAX_SIZE) { + printf("heap is full!"); + return NIL; + } + // 添加结点 + h->data[h->size] = val; + h->size++; + + // 从底至顶堆化 + siftUp(h, h->size - 1); +} + +/* 元素出堆 */ +int poll(maxHeap *h) { + // 判空处理 + if (isEmpty(h)) { + printf("heap is empty!"); + return NIL; + } + // 交换根结点与最右叶结点(即交换首元素与尾元素) + swap(h, 0, size(h) - 1); + // 删除结点 + int val = h->data[h->size - 1]; + h->size--; + // 从顶至底堆化 + siftDown(h, 0); + + // 返回堆顶元素 + return val; +} + + +/* 从结点 i 开始,从顶至底堆化 */ +void siftDown(maxHeap *h, int i) { + while (true) { + // 判断结点 i, l, r 中值最大的结点,记为 max + int l = left(h, i); + int r = right(h, i); + int max = i; + if (l < size(h) && h->data[l] > h->data[max]) { + max = l; + } + if (r < size(h) && h->data[r] > h->data[max]) { + max = r; + } + // 若结点 i 最大或索引 l, r 越界,则无需继续堆化,跳出 + if (max == i) { + break; + } + // 交换两结点 + swap(h, i, max); + // 循环向下堆化 + i = max; + } +} + +/* 从结点 i 开始,从底至顶堆化 */ +void siftUp(maxHeap *h, int i) { + while (true) { + // 获取结点 i 的父结点 + int p = parent(h, i); + // 当“越过根结点”或“结点无需修复”时,结束堆化 + if (p < 0 || h->data[i] <= h->data[p]) { + break; + } + // 交换两结点 + swap(h, i, p); + // 循环向上堆化 + i = p; + } +} + +int main() { + /* 初始化堆 */ + // 初始化大顶堆 + int nums[] = {9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}; + maxHeap *heap = newMaxHeap(nums, sizeof(nums) / sizeof(int)); + printf("输入数组并建堆后\n"); + printHeap(heap->data, heap->size); + + /* 获取堆顶元素 */ + printf("\n堆顶元素为 %d\n", peek(heap)); + + /* 元素入堆 */ + push(heap, 7); + printf("\n元素 7 入堆后\n"); + printHeap(heap->data, heap->size); + + /* 堆顶元素出堆 */ + int top = poll(heap); + printf("\n堆顶元素 %d 出堆后\n", top); + printHeap(heap->data, heap->size); + + /* 获取堆大小 */ + printf("\n堆元素数量为 %d\n", size(heap)); + + /* 判断堆是否为空 */ + printf("\n堆是否为空 %d\n", isEmpty(heap)); + + // 释放内存 + free(heap); +} \ No newline at end of file diff --git a/codes/c/chapter_tree/CMakeLists.txt b/codes/c/chapter_tree/CMakeLists.txt index 779315b7..1ff3b27b 100644 --- a/codes/c/chapter_tree/CMakeLists.txt +++ b/codes/c/chapter_tree/CMakeLists.txt @@ -1,3 +1,4 @@ +add_executable(avl_tree avl_tree.c) 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) diff --git a/codes/c/chapter_tree/avl_tree.c b/codes/c/chapter_tree/avl_tree.c new file mode 100644 index 00000000..74b40d3b --- /dev/null +++ b/codes/c/chapter_tree/avl_tree.c @@ -0,0 +1,261 @@ +/** + * File: avl_tree.c + * Created Time: 2023-01-15 + * Author: Reanon (793584285@qq.com) + */ + +#include "../include/include.h" + +/* AVL Tree */ +struct avlTree { + TreeNode *root; +}; + +typedef struct avlTree avlTree; + +/* 构建 AVL 树 */ +avlTree *newAVLTree() { + avlTree *tree = (avlTree *) malloc(sizeof(avlTree)); + tree->root = NULL; + return tree; +} + +int height(TreeNode *node) { + // 空结点高度为 -1 ,叶结点高度为 0 + if (node != NULL) { + return node->height; + } + return -1; +} + +/* 更新结点高度 */ +int updateHeight(TreeNode *node) { + int lh = height(node->left); + int rh = height(node->right); + // 结点高度等于最高子树高度 + 1 + if (lh > rh) { + node->height = lh + 1; + } else { + node->height = rh + 1; + } +} + +/* 获取平衡因子 */ +int balanceFactor(TreeNode *node) { + // 空结点平衡因子为 0 + if (node == NULL) { + return 0; + } + // 结点平衡因子 = 左子树高度 - 右子树高度 + return height(node->left) - height(node->right); +} + +/* 右旋操作 */ +TreeNode *rightRotate(TreeNode *node) { + TreeNode *child, *grandChild; + child = node->left; + grandChild = child->right; + // 以 child 为原点,将 node 向右旋转 + child->right = node; + node->left = grandChild; + // 更新结点高度 + updateHeight(node); + updateHeight(child); + // 返回旋转后子树的根节点 + return child; +} + +/* 左旋操作 */ +TreeNode *leftRotate(TreeNode *node) { + TreeNode *child, *grandChild; + child = node->right; + grandChild = child->left; + // 以 child 为原点,将 node 向左旋转 + child->left = node; + node->right = grandChild; + // 更新结点高度 + updateHeight(node); + updateHeight(child); + // 返回旋转后子树的根节点 + return child; +} + +/* 执行旋转操作,使该子树重新恢复平衡 */ +TreeNode *rotate(TreeNode *node) { + // 获取结点 node 的平衡因子 + int 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; +} + +/* 递归插入结点(辅助函数) */ +TreeNode *insertHelper(TreeNode *node, int val) { + if (node == NULL) { + 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; +} + + +/* 插入结点 */ +TreeNode *insert(avlTree *tree, int val) { + tree->root = insertHelper(tree->root, val); + return tree->root; +} + +/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ +TreeNode *getInOrderNext(TreeNode *node) { + if (node == NULL) { + return node; + } + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + while (node->left != NULL) { + node = node->left; + } + return node; +} + +/* 递归删除结点(辅助函数) */ +TreeNode *removeHelper(TreeNode *node, int val) { + TreeNode *child, *grandChild, *temp; + if (node == NULL) { + return NULL; + } + /* 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 == NULL || node->right == NULL) { + child = node->left; + if (node->right != NULL) { + child = node->right; + } + // 子结点数量 = 0 ,直接删除 node 并返回 + if (child == NULL) { + return NULL; + } 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; +} + +/* 删除结点 */ +// 由于引入了 stdio.h ,此处无法使用 remove 关键词 +TreeNode *removeNode(avlTree *tree, int val) { + TreeNode *root = removeHelper(tree->root, val); + return root; +} + +/* 查找结点 */ +TreeNode *search(avlTree *tree, int val) { + TreeNode *cur = tree->root; + // 循环查找,越过叶结点后跳出 + while (cur != NULL) { + if (cur->val < val) { + // 目标结点在 cur 的右子树中 + cur = cur->right; + } else if (cur->val > val) { + // 目标结点在 cur 的左子树中 + cur = cur->left; + } else { + // 找到目标结点,跳出循环 + break; + } + } + // 找到目标结点,跳出循环 + return cur; +} + +void testInsert(avlTree *tree, int val) { + insert(tree, val); + printf("\n插入结点 %d 后,AVL 树为 \n", val); + printTree(tree->root); +} + +void testRemove(avlTree *tree, int val) { + removeNode(tree, val); + printf("\n删除结点 %d 后,AVL 树为 \n", val); + printTree(tree->root); +} + +/* Driver Code */ +int main() { + /* 初始化空 AVL 树 */ + avlTree *tree = (avlTree *) 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 的结点 + + /* 查询结点 */ + TreeNode *node = search(tree, 7); + printf("\n查找到的结点对象结点值 = %d \n", node->val); +} \ No newline at end of file diff --git a/codes/c/chapter_tree/binary_search_tree.c b/codes/c/chapter_tree/binary_search_tree.c index 908216db..b8ddddf4 100644 --- a/codes/c/chapter_tree/binary_search_tree.c +++ b/codes/c/chapter_tree/binary_search_tree.c @@ -6,6 +6,187 @@ #include "../include/include.h" +/* 二叉搜索树 */ +struct binarySearchTree { + TreeNode *root; +}; + +typedef struct binarySearchTree binarySearchTree; + +int sortIntHelper(const void *a, const void *b) { + // 从小到大排序 + return (*(int *) a - *(int *) b); +} + +/* 构建二叉搜索树 */ +TreeNode *buildTree(int nums[], int i, int j) { + if (i > j) { + return NULL; + } + // 将数组中间结点作为根结点 + int mid = (i + j) / 2; + TreeNode *root = newTreeNode(nums[mid]); + // 递归建立左子树和右子树 + root->left = buildTree(nums, i, mid - 1); + root->right = buildTree(nums, mid + 1, j); + return root; +} + +binarySearchTree *newBinarySearchTree(int nums[], int size) { + binarySearchTree *bst = (binarySearchTree *) malloc(sizeof(binarySearchTree)); + TreeNode *root; + // 从小到大排序数组 + qsort(nums, size, sizeof(int), sortIntHelper); + // 构建二叉搜索树 + root = buildTree(nums, 0, size - 1); + bst->root = root; + return bst; +} + +/* 获取二叉树根结点 */ +TreeNode *getRoot(binarySearchTree *bst) { + return bst->root; +} + +/* 查找结点 */ +TreeNode *search(binarySearchTree *bst, int num) { + TreeNode *cur = bst->root; + // 循环查找,越过叶结点后跳出 + while (cur != NULL) { + if (cur->val < num) { + // 目标结点在 cur 的右子树中 + cur = cur->right; + } else if (cur->val > num) { + // 目标结点在 cur 的左子树中 + cur = cur->left; + } else { + // 找到目标结点,跳出循环 + break; + } + } + // 返回目标结点 + return cur; +} + +/* 插入结点 */ +TreeNode *insert(binarySearchTree *bst, int num) { + // 若树为空,直接提前返回 + if (bst->root == NULL) return NULL; + TreeNode *cur = bst->root, *pre = NULL; + // 循环查找,越过叶结点后跳出 + while (cur != NULL) { + // 找到重复结点,直接返回 + if (cur->val == num) { + return NULL; + } + pre = cur; + if (cur->val < num) { + // 插入位置在 cur 的右子树中 + cur = cur->right; + } else { + // 插入位置在 cur 的左子树中 + cur = cur->left; + } + } + // 插入结点 val + TreeNode *node = newTreeNode(num); + if (pre->val < num) { + pre->right = node; + } else { + pre->left = node; + } + return node; +} + +/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ +TreeNode *getInOrderNext(TreeNode *root) { + if (root == NULL) return root; + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + while (root->left != NULL) { + root = root->left; + } + return root; +} + +/* 删除结点 */ +// 由于引入了 stdio.h ,此处无法使用 remove 关键词 +TreeNode *removeNode(binarySearchTree *bst, int num) { + // 若树为空,直接提前返回 + if (bst->root == NULL) return NULL; + TreeNode *cur = bst->root, *pre = NULL; + // 循环查找,越过叶结点后跳出 + while (cur != NULL) { + // 找到待删除结点,跳出循环 + if (cur->val == num) break; + pre = cur; + if (cur->val < num) { + // 待删除结点在 root 的右子树中 + cur = cur->right; + } else { + // 待删除结点在 root 的左子树中 + cur = cur->left; + } + } + // 若无待删除结点,则直接返回 + if (cur == NULL) { + return NULL; + } + // 判断待删除结点是否存在子结点 + if (cur->left == NULL || cur->right == NULL) { + /* 子结点数量 = 0 or 1 */ + // 当子结点数量 = 0 / 1 时, child = nullptr / 该子结点 + TreeNode *child = cur->left != NULL ? cur->left : cur->right; + // 删除结点 cur + if (pre->left == cur) { + pre->left = child; + } else { + pre->right = child; + } + } else { + /* 子结点数量 = 2 */ + // 获取中序遍历中 cur 的下一个结点 + TreeNode *nex = getInOrderNext(cur->right); + int tmp = nex->val; + // 递归删除结点 nex + removeNode(bst, nex->val); + // 将 nex 的值复制给 cur + cur->val = tmp; + } + return cur; +} + +/* Driver Code */ int main() { + /* 初始化二叉搜索树 */ + int nums[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + binarySearchTree *bst = newBinarySearchTree(nums, sizeof(nums) / sizeof(int)); + printf("初始化的二叉树为\n"); + printTree(getRoot(bst)); + + /* 查找结点 */ + TreeNode *node = search(bst, 7); + printf("查找到的结点对象的结点值 = %d\n", node->val); + + + /* 插入结点 */ + insert(bst, 16); + printf("插入结点 16 后,二叉树为\n"); + printTree(getRoot(bst)); + + + /* 删除结点 */ + removeNode(bst, 1); + printf("删除结点 1 后,二叉树为\n"); + printTree(getRoot(bst)); + removeNode(bst, 2); + printf("删除结点 2 后,二叉树为\n"); + printTree(getRoot(bst)); + removeNode(bst, 4); + printf("删除结点 4 后,二叉树为\n"); + printTree(getRoot(bst)); + + // 释放内存 + free(bst); + return 0; -} \ No newline at end of file +} diff --git a/codes/c/include/print_util.h b/codes/c/include/print_util.h index a0c3a150..0328a73d 100644 --- a/codes/c/include/print_util.h +++ b/codes/c/include/print_util.h @@ -25,7 +25,7 @@ extern "C" { * @param arr * @param size */ -static void printArray(int *arr, int size) { +static void printArray(int arr[], int size) { printf("["); for (int i = 0; i < size - 1; i++) { if (arr[i] != NIL) { @@ -36,7 +36,7 @@ static void printArray(int *arr, int size) { } if (arr[size - 1] != NIL) { printf("%d]\n", arr[size - 1]); - }else{ + } else { printf("NULL]\n"); } } @@ -127,6 +127,21 @@ static void printTree(TreeNode *root) { printTreeHelper(root, NULL, false); } +/** + * @brief Print a Heap + * + * @param arr + * @param size + */ +static void printHeap(int arr[], int size) { + TreeNode * root; + printf("堆的数组表示:"); + printArray(arr, size); + printf("堆的树状表示:\n"); + root = arrToTree(arr, size); + printTree(root); +} + #ifdef __cplusplus } diff --git a/codes/cpp/chapter_tree/binary_search_tree.cpp b/codes/cpp/chapter_tree/binary_search_tree.cpp index cd6a712d..69acb87f 100644 --- a/codes/cpp/chapter_tree/binary_search_tree.cpp +++ b/codes/cpp/chapter_tree/binary_search_tree.cpp @@ -43,9 +43,9 @@ public: TreeNode* cur = root; // 循环查找,越过叶结点后跳出 while (cur != nullptr) { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 if (cur->val < num) cur = cur->right; - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 else if (cur->val > num) cur = cur->left; // 找到目标结点,跳出循环 else break; @@ -64,9 +64,9 @@ public: // 找到重复结点,直接返回 if (cur->val == num) return nullptr; pre = cur; - // 插入位置在 root 的右子树中 + // 插入位置在 cur 的右子树中 if (cur->val < num) cur = cur->right; - // 插入位置在 root 的左子树中 + // 插入位置在 cur 的左子树中 else cur = cur->left; } // 插入结点 val diff --git a/codes/csharp/chapter_array_and_linkedlist/array.cs b/codes/csharp/chapter_array_and_linkedlist/array.cs index 97a37f04..92221965 100644 --- a/codes/csharp/chapter_array_and_linkedlist/array.cs +++ b/codes/csharp/chapter_array_and_linkedlist/array.cs @@ -11,8 +11,10 @@ namespace hello_algo.chapter_array_and_linkedlist /* 随机返回一个数组元素 */ public static int RandomAccess(int[] nums) { - Random random = new(); + Random random=new(); + // 在区间 [0, nums.Length) 中随机抽取一个数字 int randomIndex = random.Next(nums.Length); + // 获取并返回随机元素 int randomNum = nums[randomIndex]; return randomNum; } diff --git a/codes/csharp/chapter_tree/avl_tree.cs b/codes/csharp/chapter_tree/avl_tree.cs index 6bcb559b..50a4ba16 100644 --- a/codes/csharp/chapter_tree/avl_tree.cs +++ b/codes/csharp/chapter_tree/avl_tree.cs @@ -193,10 +193,10 @@ namespace hello_algo.chapter_tree // 循环查找,越过叶结点后跳出 while (cur != null) { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 if (cur.val < val) cur = cur.right; - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 else if (cur.val > val) cur = cur.left; // 找到目标结点,跳出循环 diff --git a/codes/csharp/chapter_tree/binary_search_tree.cs b/codes/csharp/chapter_tree/binary_search_tree.cs index cb832769..2821b29b 100644 --- a/codes/csharp/chapter_tree/binary_search_tree.cs +++ b/codes/csharp/chapter_tree/binary_search_tree.cs @@ -42,9 +42,9 @@ namespace hello_algo.chapter_tree // 循环查找,越过叶结点后跳出 while (cur != null) { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 else if (cur.val > num) cur = cur.left; // 找到目标结点,跳出循环 else break; @@ -65,9 +65,9 @@ namespace hello_algo.chapter_tree // 找到重复结点,直接返回 if (cur.val == num) return null; pre = cur; - // 插入位置在 root 的右子树中 + // 插入位置在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 插入位置在 root 的左子树中 + // 插入位置在 cur 的左子树中 else cur = cur.left; } 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 466bde4f..76671d6a 100644 --- a/codes/go/chapter_array_and_linkedlist/linked_list_test.go +++ b/codes/go/chapter_array_and_linkedlist/linked_list_test.go @@ -11,7 +11,7 @@ import ( . "github.com/krahets/hello-algo/pkg" ) -func TestLikedList(t *testing.T) { +func TestLinkedList(t *testing.T) { /* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */ // 初始化各个结点 n0 := NewListNode(1) diff --git a/codes/go/chapter_tree/avl_tree.go b/codes/go/chapter_tree/avl_tree.go index e83b2882..74e681e9 100644 --- a/codes/go/chapter_tree/avl_tree.go +++ b/codes/go/chapter_tree/avl_tree.go @@ -195,11 +195,11 @@ func (t *avlTree) search(val int) *TreeNode { cur := t.root // 循环查找,越过叶结点后跳出 for cur != nil { - // 目标结点在 root 的右子树中 if cur.Val < val { + // 目标结点在 cur 的右子树中 cur = cur.Right } else if cur.Val > val { - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 cur = cur.Left } else { // 找到目标结点,跳出循环 diff --git a/codes/go/chapter_tree/avl_tree_test.go b/codes/go/chapter_tree/avl_tree_test.go index c4fc6b71..c02b8013 100644 --- a/codes/go/chapter_tree/avl_tree_test.go +++ b/codes/go/chapter_tree/avl_tree_test.go @@ -49,6 +49,6 @@ func testInsert(tree *avlTree, val int) { func testRemove(tree *avlTree, val int) { tree.remove(val) - fmt.Printf("\n删除结点 %d 后,AVL 树为 \n", val) + fmt.Printf("\n删除结点 %d 后,AVL 树为 \n", val) PrintTree(tree.root) } diff --git a/codes/go/chapter_tree/binary_search_tree.go b/codes/go/chapter_tree/binary_search_tree.go index 61ed400d..e4fd0810 100644 --- a/codes/go/chapter_tree/binary_search_tree.go +++ b/codes/go/chapter_tree/binary_search_tree.go @@ -46,10 +46,10 @@ func (bst *binarySearchTree) search(num int) *TreeNode { // 循环查找,越过叶结点后跳出 for node != nil { if node.Val < num { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 node = node.Right } else if node.Val > num { - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 node = node.Left } else { // 找到目标结点,跳出循环 diff --git a/codes/java/chapter_sorting/radix_sort.java b/codes/java/chapter_sorting/radix_sort.java new file mode 100644 index 00000000..47321832 --- /dev/null +++ b/codes/java/chapter_sorting/radix_sort.java @@ -0,0 +1,67 @@ +/** + * File: radix_sort.java + * Created Time: 2023-01-17 + * Author: Krahets (krahets@163.com) + */ + +package chapter_sorting; + +import java.util.*; + +public class radix_sort { + /* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ + static int digit(int num, int exp) { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num / exp) % 10; + } + + /* 计数排序(根据 nums 第 k 位排序) */ + static void countSort(int[] nums, int exp) { + // 十进制的各位数字范围为 0~9 ,因此需要长度为 10 的桶 + int[] bucket = new int[10]; + int n = nums.length; + // 借助桶来统计 0~9 各数字的出现次数 + for (int i = 0; i < n; i++) { + int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d + bucket[d]++; // 统计数字 d 的出现次数 + } + // 求前缀和,将“出现个数”转换为“数组索引” + for (int i = 1; i < 10; i++) { + bucket[i] += bucket[i - 1]; + } + // 倒序遍历,根据桶内统计结果,将各元素填入暂存数组 tmp + int[] tmp = new int[n]; + for (int i = n - 1; i >= 0; i--) { + int d = digit(nums[i], exp); + int j = bucket[d] - 1; // 获取 d 在数组中的索引 j + tmp[j] = nums[i]; // 将当前元素填入索引 j + bucket[d]--; // 将 d 的数量减 1 + } + // 将 tmp 复制到 nums + for (int i = 0; i < n; i++) + nums[i] = tmp[i]; + } + + /* 基数排序 */ + static void radixSort(int[] nums) { + // 获取数组的最大元素,用于判断最大位数 + int ma = Integer.MIN_VALUE; + for (int num : nums) + if (num > ma) ma = num; + // 按照从低位到高位的顺序遍历 + for (int exp = 1; ma >= exp; exp *= 10) + // 对数组元素的第 k 位执行「计数排序」 + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // k = 3 -> exp = 100 + // 即 exp = 10^(k-1) + countSort(nums, exp); + } + + public static void main(String[] args) { + /* 基数排序 */ + int[] nums = { 23, 12, 3, 4, 788, 192 }; + radixSort(nums); + System.out.println("基数排序完成后 nums = " + Arrays.toString(nums)); + } +} diff --git a/codes/java/chapter_tree/avl_tree.java b/codes/java/chapter_tree/avl_tree.java index 9120262f..2699bb60 100644 --- a/codes/java/chapter_tree/avl_tree.java +++ b/codes/java/chapter_tree/avl_tree.java @@ -165,10 +165,10 @@ class AVLTree { TreeNode cur = root; // 循环查找,越过叶结点后跳出 while (cur != null) { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 if (cur.val < val) cur = cur.right; - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 else if (cur.val > val) cur = cur.left; // 找到目标结点,跳出循环 diff --git a/codes/java/chapter_tree/binary_search_tree.java b/codes/java/chapter_tree/binary_search_tree.java index a0a554e3..7f13b903 100644 --- a/codes/java/chapter_tree/binary_search_tree.java +++ b/codes/java/chapter_tree/binary_search_tree.java @@ -40,9 +40,9 @@ class BinarySearchTree { TreeNode cur = root; // 循环查找,越过叶结点后跳出 while (cur != null) { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 else if (cur.val > num) cur = cur.left; // 找到目标结点,跳出循环 else break; @@ -61,9 +61,9 @@ class BinarySearchTree { // 找到重复结点,直接返回 if (cur.val == num) return null; pre = cur; - // 插入位置在 root 的右子树中 + // 插入位置在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 插入位置在 root 的左子树中 + // 插入位置在 cur 的左子树中 else cur = cur.left; } // 插入结点 val diff --git a/codes/javascript/chapter_tree/binary_search_tree.js b/codes/javascript/chapter_tree/binary_search_tree.js index d619ab9f..2fd9092a 100644 --- a/codes/javascript/chapter_tree/binary_search_tree.js +++ b/codes/javascript/chapter_tree/binary_search_tree.js @@ -37,9 +37,9 @@ function search(num) { let cur = root; // 循环查找,越过叶结点后跳出 while (cur !== null) { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 else if (cur.val > num) cur = cur.left; // 找到目标结点,跳出循环 else break; @@ -58,9 +58,9 @@ function insert(num) { // 找到重复结点,直接返回 if (cur.val === num) return null; pre = cur; - // 插入位置在 root 的右子树中 + // 插入位置在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 插入位置在 root 的左子树中 + // 插入位置在 cur 的左子树中 else cur = cur.left; } // 插入结点 val diff --git a/codes/python/chapter_array_and_linkedlist/array.py b/codes/python/chapter_array_and_linkedlist/array.py index 8933fb48..e5979b51 100644 --- a/codes/python/chapter_array_and_linkedlist/array.py +++ b/codes/python/chapter_array_and_linkedlist/array.py @@ -10,8 +10,8 @@ from include import * """ 随机访问元素 """ def randomAccess(nums): - # 在区间 [0, len(nums)) 中随机抽取一个数字 - random_index = random.randint(0, len(nums)) + # 在区间 [0, len(nums)-1] 中随机抽取一个数字 + random_index = random.randint(0, len(nums) - 1) # 获取并返回随机元素 random_num = nums[random_index] return random_num diff --git a/codes/python/chapter_sorting/bubble_sort.py b/codes/python/chapter_sorting/bubble_sort.py index 610e3186..30cb38f8 100644 --- a/codes/python/chapter_sorting/bubble_sort.py +++ b/codes/python/chapter_sorting/bubble_sort.py @@ -12,7 +12,7 @@ from include import * def bubble_sort(nums): n = len(nums) # 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for i in range(n - 1, -1, -1): + for i in range(n - 1, 0, -1): # 内循环:冒泡操作 for j in range(i): if nums[j] > nums[j + 1]: @@ -23,7 +23,7 @@ def bubble_sort(nums): def bubble_sort_with_flag(nums): n = len(nums) # 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for i in range(n - 1, -1, -1): + for i in range(n - 1, 0, -1): flag = False # 初始化标志位 # 内循环:冒泡操作 for j in range(i): diff --git a/codes/python/chapter_tree/avl_tree.py b/codes/python/chapter_tree/avl_tree.py index 83929981..79f2e81d 100644 --- a/codes/python/chapter_tree/avl_tree.py +++ b/codes/python/chapter_tree/avl_tree.py @@ -152,10 +152,10 @@ class AVLTree: cur = self.root # 循环查找,越过叶结点后跳出 while cur is not None: - # 目标结点在 root 的右子树中 + # 目标结点在 cur 的右子树中 if cur.val < val: cur = cur.right - # 目标结点在 root 的左子树中 + # 目标结点在 cur 的左子树中 elif cur.val > val: cur = cur.left # 找到目标结点,跳出循环 diff --git a/codes/python/chapter_tree/binary_search_tree.py b/codes/python/chapter_tree/binary_search_tree.py index fdedf6c9..c3058c93 100644 --- a/codes/python/chapter_tree/binary_search_tree.py +++ b/codes/python/chapter_tree/binary_search_tree.py @@ -37,10 +37,10 @@ class BinarySearchTree: cur = self.root # 循环查找,越过叶结点后跳出 while cur is not None: - # 目标结点在 root 的右子树中 + # 目标结点在 cur 的右子树中 if cur.val < num: cur = cur.right - # 目标结点在 root 的左子树中 + # 目标结点在 cur 的左子树中 elif cur.val > num: cur = cur.left # 找到目标结点,跳出循环 @@ -64,10 +64,11 @@ class BinarySearchTree: if cur.val == num: return None pre = cur - - if cur.val < num: # 插入位置在 root 的右子树中 + # 插入位置在 cur 的右子树中 + if cur.val < num: cur = cur.right - else: # 插入位置在 root 的左子树中 + # 插入位置在 cur 的左子树中 + else: cur = cur.left # 插入结点 val diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index c20c58f9..3652ac16 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -20,6 +20,8 @@ let package = Package( .executable(name: "linkedlist_queue", targets: ["linkedlist_queue"]), .executable(name: "array_queue", targets: ["array_queue"]), .executable(name: "deque", targets: ["deque"]), + .executable(name: "hash_map", targets: ["hash_map"]), + .executable(name: "array_hash_map", targets: ["array_hash_map"]), ], targets: [ .target(name: "utils", path: "utils"), @@ -38,5 +40,7 @@ let package = Package( .executableTarget(name: "linkedlist_queue", dependencies: ["utils"], path: "chapter_stack_and_queue", sources: ["linkedlist_queue.swift"]), .executableTarget(name: "array_queue", path: "chapter_stack_and_queue", sources: ["array_queue.swift"]), .executableTarget(name: "deque", path: "chapter_stack_and_queue", sources: ["deque.swift"]), + .executableTarget(name: "hash_map", dependencies: ["utils"], path: "chapter_hashing", sources: ["hash_map.swift"]), + .executableTarget(name: "array_hash_map", path: "chapter_hashing", sources: ["array_hash_map.swift"]), ] ) diff --git a/codes/swift/chapter_hashing/array_hash_map.swift b/codes/swift/chapter_hashing/array_hash_map.swift new file mode 100644 index 00000000..6e3b16a1 --- /dev/null +++ b/codes/swift/chapter_hashing/array_hash_map.swift @@ -0,0 +1,139 @@ +/** + * File: array_hash_map.swift + * Created Time: 2023-01-16 + * Author: nuomi1 (nuomi1@qq.com) + */ + +/* 键值对 int->String */ +class Entry { + var key: Int + var val: String + + init(key: Int, val: String) { + self.key = key + self.val = val + } +} + +/* 基于数组简易实现的哈希表 */ +class ArrayHashMap { + private var bucket: [Entry?] = [] + + init() { + // 初始化一个长度为 100 的桶(数组) + for _ in 0 ..< 100 { + bucket.append(nil) + } + } + + /* 哈希函数 */ + private func hashFunc(key: Int) -> Int { + let index = key % 100 + return index + } + + /* 查询操作 */ + func get(key: Int) -> String? { + let index = hashFunc(key: key) + let pair = bucket[index] + return pair?.val + } + + /* 添加操作 */ + func put(key: Int, val: String) { + let pair = Entry(key: key, val: val) + let index = hashFunc(key: key) + bucket[index] = pair + } + + /* 删除操作 */ + func remove(key: Int) { + let index = hashFunc(key: key) + // 置为 nil ,代表删除 + bucket[index] = nil + } + + /* 获取所有键值对 */ + func entrySet() -> [Entry] { + var entrySet: [Entry] = [] + for pair in bucket { + if let pair = pair { + entrySet.append(pair) + } + } + return entrySet + } + + /* 获取所有键 */ + func keySet() -> [Int] { + var keySet: [Int] = [] + for pair in bucket { + if let pair = pair { + keySet.append(pair.key) + } + } + return keySet + } + + /* 获取所有值 */ + func valueSet() -> [String] { + var valueSet: [String] = [] + for pair in bucket { + if let pair = pair { + valueSet.append(pair.val) + } + } + return valueSet + } + + /* 打印哈希表 */ + func print() { + for entry in entrySet() { + Swift.print("\(entry.key) -> \(entry.val)") + } + } +} + +@main +enum _ArrayHashMap { + /* Driver Code */ + static func main() { + /* 初始化哈希表 */ + let map = ArrayHashMap() + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map.put(key: 12836, val: "小哈") + map.put(key: 15937, val: "小啰") + map.put(key: 16750, val: "小算") + map.put(key: 13276, val: "小法") + map.put(key: 10583, val: "小鸭") + print("\n添加完成后,哈希表为\nKey -> Value") + map.print() + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + let name = map.get(key: 15937)! + print("\n输入学号 15937 ,查询到姓名 \(name)") + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.remove(key: 10583) + print("\n删除 10583 后,哈希表为\nKey -> Value") + map.print() + + /* 遍历哈希表 */ + print("\n遍历键值对 Key->Value") + for entry in map.entrySet() { + print("\(entry.key) -> \(entry.val)") + } + print("\n单独遍历键 Key") + for key in map.keySet() { + print(key) + } + print("\n单独遍历值 Value") + for val in map.valueSet() { + print(val) + } + } +} diff --git a/codes/swift/chapter_hashing/hash_map.swift b/codes/swift/chapter_hashing/hash_map.swift new file mode 100644 index 00000000..b74aed3f --- /dev/null +++ b/codes/swift/chapter_hashing/hash_map.swift @@ -0,0 +1,51 @@ +/** + * File: hash_map.swift + * Created Time: 2023-01-16 + * Author: nuomi1 (nuomi1@qq.com) + */ + +import utils + +@main +enum HashMap { + /* Driver Code */ + static func main() { + /* 初始化哈希表 */ + var map: [Int: String] = [:] + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map[12836] = "小哈" + map[15937] = "小啰" + map[16750] = "小算" + map[13276] = "小法" + map[10583] = "小鸭" + print("\n添加完成后,哈希表为\nKey -> Value") + PrintUtil.printHashMap(map: map) + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + let name = map[15937]! + print("\n输入学号 15937 ,查询到姓名 \(name)") + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.removeValue(forKey: 10583) + print("\n删除 10583 后,哈希表为\nKey -> Value") + PrintUtil.printHashMap(map: map) + + /* 遍历哈希表 */ + print("\n遍历键值对 Key->Value") + for (key, value) in map { + print("\(key) -> \(value)") + } + print("\n单独遍历键 Key") + for key in map.keys { + print(key) + } + print("\n单独遍历值 Value") + for value in map.values { + print(value) + } + } +} diff --git a/codes/swift/utils/PrintUtil.swift b/codes/swift/utils/PrintUtil.swift index cbeab122..5dc718df 100644 --- a/codes/swift/utils/PrintUtil.swift +++ b/codes/swift/utils/PrintUtil.swift @@ -68,4 +68,10 @@ public enum PrintUtil { showTrunks(p: p?.prev) print(p!.str, terminator: "") } + + public static func printHashMap(map: [K: V]) { + for (key, value) in map { + print("\(key) -> \(value)") + } + } } diff --git a/codes/typescript/chapter_tree/binary_search_tree.ts b/codes/typescript/chapter_tree/binary_search_tree.ts index a19ea32a..c210d3ca 100644 --- a/codes/typescript/chapter_tree/binary_search_tree.ts +++ b/codes/typescript/chapter_tree/binary_search_tree.ts @@ -40,9 +40,9 @@ function search(num: number): TreeNode | null { // 循环查找,越过叶结点后跳出 while (cur !== null) { if (cur.val < num) { - cur = cur.right; // 目标结点在 root 的右子树中 + cur = cur.right; // 目标结点在 cur 的右子树中 } else if (cur.val > num) { - cur = cur.left; // 目标结点在 root 的左子树中 + cur = cur.left; // 目标结点在 cur 的左子树中 } else { break; // 找到目标结点,跳出循环 } @@ -66,9 +66,9 @@ function insert(num: number): TreeNode | null { } pre = cur; if (cur.val < num) { - cur = cur.right as TreeNode; // 插入位置在 root 的右子树中 + cur = cur.right as TreeNode; // 插入位置在 cur 的右子树中 } else { - cur = cur.left as TreeNode; // 插入位置在 root 的左子树中 + cur = cur.left as TreeNode; // 插入位置在 cur 的左子树中 } } // 插入结点 val diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index fcbdf9ab..de25369a 100644 --- a/docs/chapter_array_and_linkedlist/array.md +++ b/docs/chapter_array_and_linkedlist/array.md @@ -111,6 +111,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex ```java title="array.java" /* 随机返回一个数组元素 */ int randomAccess(int[] nums) { + // 在区间 [0, nums.length) 中随机抽取一个数字 int randomIndex = ThreadLocalRandom.current(). nextInt(0, nums.length); int randomNum = nums[randomIndex]; @@ -136,8 +137,8 @@ elementAddr = firtstElementAddr + elementLength * elementIndex ```python title="array.py" """ 随机访问元素 """ def randomAccess(nums): - # 在区间 [0, len(nums)) 中随机抽取一个数字 - random_index = random.randint(0, len(nums)) + # 在区间 [0, len(nums)-1] 中随机抽取一个数字 + random_index = random.randint(0, len(nums) - 1) # 获取并返回随机元素 random_num = nums[random_index] return random_num @@ -195,7 +196,9 @@ elementAddr = firtstElementAddr + elementLength * elementIndex int RandomAccess(int[] nums) { Random random=new(); + // 在区间 [0, nums.Length) 中随机抽取一个数字 int randomIndex = random.Next(nums.Length); + // 获取并返回随机元素 int randomNum = nums[randomIndex]; return randomNum; } diff --git a/docs/chapter_data_structure/classification_of_data_structure.md b/docs/chapter_data_structure/classification_of_data_structure.md index 202af50a..b38ced9e 100644 --- a/docs/chapter_data_structure/classification_of_data_structure.md +++ b/docs/chapter_data_structure/classification_of_data_structure.md @@ -10,7 +10,7 @@ comments: true **「逻辑结构」反映了数据之间的逻辑关系**。数组和链表的数据按照顺序依次排列,反映了数据间的线性关系;树从顶至底按层级排列,反映了祖先与后代之间的派生关系;图由结点和边组成,反映了复杂网络关系。 -我们一般将逻辑结构分为「线性」和「非线性」两种。“线性”这个概念很直观,即表明数据在逻辑关系上是排成一条线的;而如果数据之间的逻辑关系是非线形的(例如是网状或树状的),那么就是非线性数据结构。 +我们一般将逻辑结构分为「线性」和「非线性」两种。“线性”这个概念很直观,即表明数据在逻辑关系上是排成一条线的;而如果数据之间的逻辑关系是非线性的(例如是网状或树状的),那么就是非线性数据结构。 - **线性数据结构**:数组、链表、栈、队列、哈希表; - **非线性数据结构**:树、图、堆、哈希表; diff --git a/docs/chapter_data_structure/data_and_memory.md b/docs/chapter_data_structure/data_and_memory.md index 93147fb6..45c7c8a1 100644 --- a/docs/chapter_data_structure/data_and_memory.md +++ b/docs/chapter_data_structure/data_and_memory.md @@ -84,13 +84,17 @@ comments: true === "JavaScript" ```js title="" - + /* JavaScript 的数组可以自由存储各种基本数据类型和对象 */ + const array = [0, 0.0, 'a', false]; ``` === "TypeScript" ```typescript title="" - + /* 使用多种「基本数据类型」来初始化「数组」 */ + const numbers: number[] = []; + const characters: string[] = []; + const booleans: boolean[] = []; ``` === "C" @@ -126,7 +130,7 @@ comments: true ## 计算机内存 -在计算机中,内存和硬盘是两种主要的存储硬件设备。「硬盘」主要用于长期存储数据,容量较大(通常可达到 TB 级别)、速度较慢。「内存」用于运行程序时暂存数据,速度更快,但容量较小(通常为 GB 级别)。 +在计算机中,内存和硬盘是两种主要的存储硬件设备。「硬盘」主要用于长期存储数据,容量较大(通常可达到 TB 级别)、速度较慢。「内存」用于运行程序时暂存数据,速度较快,但容量较小(通常为 GB 级别)。 **算法运行中,相关数据都被存储在内存中**。下图展示了一个计算机内存条,其中每个黑色方块都包含一块内存空间。我们可以将内存想象成一个巨大的 Excel 表格,其中每个单元格都可以存储 1 byte 的数据,在算法运行时,所有数据都被存储在这些单元格中。 diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index cbb4ba04..f3afdcd5 100644 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -210,7 +210,24 @@ comments: true === "Swift" ```swift title="hash_map.swift" + /* 初始化哈希表 */ + var map: [Int: String] = [:] + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map[12836] = "小哈" + map[15937] = "小啰" + map[16750] = "小算" + map[13276] = "小法" + map[10583] = "小鸭" + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + let name = map[15937]! + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.removeValue(forKey: 10583) ``` 遍历哈希表有三种方式,即 **遍历键值对、遍历键、遍历值**。 @@ -348,7 +365,19 @@ comments: true === "Swift" ```swift title="hash_map.swift" - + /* 遍历哈希表 */ + // 遍历键值对 Key->Value + for (key, value) in map { + print("\(key) -> \(value)") + } + // 单独遍历键 Key + for key in map.keys { + print(key) + } + // 单独遍历值 Value + for value in map.values { + print(value) + } ``` ## 哈希函数 @@ -771,7 +800,55 @@ $$ === "Swift" ```swift title="array_hash_map.swift" + /* 键值对 int->String */ + class Entry { + var key: Int + var val: String + init(key: Int, val: String) { + self.key = key + self.val = val + } + } + + /* 基于数组简易实现的哈希表 */ + class ArrayHashMap { + private var bucket: [Entry?] = [] + + init() { + // 初始化一个长度为 100 的桶(数组) + for _ in 0 ..< 100 { + bucket.append(nil) + } + } + + /* 哈希函数 */ + private func hashFunc(key: Int) -> Int { + let index = key % 100 + return index + } + + /* 查询操作 */ + func get(key: Int) -> String? { + let index = hashFunc(key: key) + let pair = bucket[index] + return pair?.val + } + + /* 添加操作 */ + func put(key: Int, val: String) { + let pair = Entry(key: key, val: val) + let index = hashFunc(key: key) + bucket[index] = pair + } + + /* 删除操作 */ + func remove(key: Int) { + let index = hashFunc(key: key) + // 置为 nil ,代表删除 + bucket[index] = nil + } + } ``` ## 哈希冲突 diff --git a/docs/chapter_preface/about_the_book.assets/mindmap.png b/docs/chapter_preface/about_the_book.assets/mindmap.png new file mode 100644 index 00000000..131251da Binary files /dev/null and b/docs/chapter_preface/about_the_book.assets/mindmap.png differ diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index 01f513f0..78133f7b 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -36,7 +36,7 @@ comments: true 本书主要内容分为复杂度分析、数据结构、算法三个部分。 -![mindmap](index.assets/mindmap.png) +![mindmap](about_the_book.assets/mindmap.png)

Fig. 知识点思维导图

@@ -94,7 +94,7 @@ comments: true /* 标题注释,用于标注函数、类、测试样例等 */ // 内容注释,用于详解代码 - + /** * 多行 * 注释 @@ -107,7 +107,7 @@ comments: true /* 标题注释,用于标注函数、类、测试样例等 */ // 内容注释,用于详解代码 - + /** * 多行 * 注释 @@ -120,7 +120,7 @@ comments: true """ 标题注释,用于标注函数、类、测试样例等 """ # 内容注释,用于详解代码 - + """ 多行 注释 @@ -133,7 +133,7 @@ comments: true /* 标题注释,用于标注函数、类、测试样例等 */ // 内容注释,用于详解代码 - + /** * 多行 * 注释 @@ -146,7 +146,7 @@ comments: true /* 标题注释,用于标注函数、类、测试样例等 */ // 内容注释,用于详解代码 - + /** * 多行 * 注释 @@ -159,7 +159,7 @@ comments: true /* 标题注释,用于标注函数、类、测试样例等 */ // 内容注释,用于详解代码 - + /** * 多行 * 注释 @@ -172,7 +172,7 @@ comments: true /* 标题注释,用于标注函数、类、测试样例等 */ // 内容注释,用于详解代码 - + /** * 多行 * 注释 @@ -185,7 +185,7 @@ comments: true /* 标题注释,用于标注函数、类、测试样例等 */ // 内容注释,用于详解代码 - + /** * 多行 * 注释 @@ -198,7 +198,7 @@ comments: true /* 标题注释,用于标注函数、类、测试样例等 */ // 内容注释,用于详解代码 - + /** * 多行 * 注释 diff --git a/docs/chapter_preface/index.assets/learning_route.png b/docs/chapter_preface/index.assets/learning_route.png deleted file mode 100644 index 7423808d..00000000 Binary files a/docs/chapter_preface/index.assets/learning_route.png and /dev/null differ diff --git a/docs/chapter_preface/index.assets/mindmap.png b/docs/chapter_preface/index.assets/mindmap.png deleted file mode 100644 index a1c83838..00000000 Binary files a/docs/chapter_preface/index.assets/mindmap.png and /dev/null differ diff --git a/docs/chapter_preface/installation.assets/image-20221117201957848.png b/docs/chapter_preface/installation.assets/image-20221117201957848.png deleted file mode 100644 index fdb2c2f6..00000000 Binary files a/docs/chapter_preface/installation.assets/image-20221117201957848.png and /dev/null differ diff --git a/docs/chapter_preface/installation.assets/image-20221118013006841.png b/docs/chapter_preface/installation.assets/image-20221118013006841.png deleted file mode 100644 index ae6f9aa2..00000000 Binary files a/docs/chapter_preface/installation.assets/image-20221118013006841.png and /dev/null differ diff --git a/docs/chapter_preface/installation.assets/image-20221118013751773.png b/docs/chapter_preface/installation.assets/image-20221118013751773.png deleted file mode 100644 index 3bd4cf97..00000000 Binary files a/docs/chapter_preface/installation.assets/image-20221118013751773.png and /dev/null differ diff --git a/docs/chapter_preface/installation.assets/vscode_installation.png b/docs/chapter_preface/installation.assets/vscode_installation.png deleted file mode 100644 index 77bd06d7..00000000 Binary files a/docs/chapter_preface/installation.assets/vscode_installation.png and /dev/null differ diff --git a/docs/chapter_preface/suggestions.assets/algorithm_animation.gif b/docs/chapter_preface/suggestions.assets/algorithm_animation.gif deleted file mode 100644 index 5db93eee..00000000 Binary files a/docs/chapter_preface/suggestions.assets/algorithm_animation.gif and /dev/null differ diff --git a/docs/chapter_preface/suggestions.assets/animation.gif b/docs/chapter_preface/suggestions.assets/animation.gif new file mode 100644 index 00000000..60f53f20 Binary files /dev/null and b/docs/chapter_preface/suggestions.assets/animation.gif differ diff --git a/docs/chapter_preface/suggestions.assets/comment.gif b/docs/chapter_preface/suggestions.assets/comment.gif index d64787ac..e7a74cf9 100644 Binary files a/docs/chapter_preface/suggestions.assets/comment.gif and b/docs/chapter_preface/suggestions.assets/comment.gif differ diff --git a/docs/chapter_preface/suggestions.assets/running_code.gif b/docs/chapter_preface/suggestions.assets/running_code.gif index 7377773c..dfe17531 100644 Binary files a/docs/chapter_preface/suggestions.assets/running_code.gif and b/docs/chapter_preface/suggestions.assets/running_code.gif differ diff --git a/docs/chapter_preface/suggestions.md b/docs/chapter_preface/suggestions.md index b2e18905..4c774356 100644 --- a/docs/chapter_preface/suggestions.md +++ b/docs/chapter_preface/suggestions.md @@ -10,7 +10,7 @@ comments: true 在阅读本书的过程中,若发现某段内容提供了动画或图解,**建议你以图为主线**,将文字内容(一般在图的上方)对齐到图中内容,综合来理解。 -![algorithm_animation](suggestions.assets/algorithm_animation.gif) +![animation](suggestions.assets/animation.gif) ## 代码实践学 diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index 07984764..0ed94b15 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -100,7 +100,7 @@ comments: true def bubble_sort(nums): n = len(nums) # 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for i in range(n - 1, -1, -1): + for i in range(n - 1, 0, -1): # 内循环:冒泡操作 for j in range(i): if nums[j] > nums[j + 1]: @@ -288,7 +288,7 @@ comments: true def bubble_sort_with_flag(nums): n = len(nums) # 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for i in range(n - 1, -1, -1): + for i in range(n - 1, 0, -1): flag = False # 初始化标志位 # 内循环:冒泡操作 for j in range(i): diff --git a/docs/chapter_stack_and_queue/stack.assets/stack_operations.png b/docs/chapter_stack_and_queue/stack.assets/stack_operations.png index 59acdacb..585af8ea 100644 Binary files a/docs/chapter_stack_and_queue/stack.assets/stack_operations.png and b/docs/chapter_stack_and_queue/stack.assets/stack_operations.png differ diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index 393221b7..75ffb678 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -43,9 +43,9 @@ comments: true TreeNode cur = root; // 循环查找,越过叶结点后跳出 while (cur != null) { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 else if (cur.val > num) cur = cur.left; // 找到目标结点,跳出循环 else break; @@ -63,9 +63,9 @@ comments: true TreeNode* cur = root; // 循环查找,越过叶结点后跳出 while (cur != nullptr) { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 if (cur->val < num) cur = cur->right; - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 else if (cur->val > num) cur = cur->left; // 找到目标结点,跳出循环 else break; @@ -83,10 +83,10 @@ comments: true cur = self.root # 循环查找,越过叶结点后跳出 while cur is not None: - # 目标结点在 root 的右子树中 + # 目标结点在 cur 的右子树中 if cur.val < num: cur = cur.right - # 目标结点在 root 的左子树中 + # 目标结点在 cur 的左子树中 elif cur.val > num: cur = cur.left # 找到目标结点,跳出循环 @@ -104,10 +104,10 @@ comments: true // 循环查找,越过叶结点后跳出 for node != nil { if node.Val < num { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 node = node.Right } else if node.Val > num { - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 node = node.Left } else { // 找到目标结点,跳出循环 @@ -127,9 +127,9 @@ comments: true let cur = root; // 循环查找,越过叶结点后跳出 while (cur !== null) { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 else if (cur.val > num) cur = cur.left; // 找到目标结点,跳出循环 else break; @@ -148,9 +148,9 @@ comments: true // 循环查找,越过叶结点后跳出 while (cur !== null) { if (cur.val < num) { - cur = cur.right; // 目标结点在 root 的右子树中 + cur = cur.right; // 目标结点在 cur 的右子树中 } else if (cur.val > num) { - cur = cur.left; // 目标结点在 root 的左子树中 + cur = cur.left; // 目标结点在 cur 的左子树中 } else { break; // 找到目标结点,跳出循环 } @@ -176,9 +176,9 @@ comments: true // 循环查找,越过叶结点后跳出 while (cur != null) { - // 目标结点在 root 的右子树中 + // 目标结点在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 目标结点在 root 的左子树中 + // 目标结点在 cur 的左子树中 else if (cur.val > num) cur = cur.left; // 找到目标结点,跳出循环 else break; @@ -218,9 +218,9 @@ comments: true // 找到重复结点,直接返回 if (cur.val == num) return null; pre = cur; - // 插入位置在 root 的右子树中 + // 插入位置在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 插入位置在 root 的左子树中 + // 插入位置在 cur 的左子树中 else cur = cur.left; } // 插入结点 val @@ -244,9 +244,9 @@ comments: true // 找到重复结点,直接返回 if (cur->val == num) return nullptr; pre = cur; - // 插入位置在 root 的右子树中 + // 插入位置在 cur 的右子树中 if (cur->val < num) cur = cur->right; - // 插入位置在 root 的左子树中 + // 插入位置在 cur 的左子树中 else cur = cur->left; } // 插入结点 val @@ -276,10 +276,11 @@ comments: true if cur.val == num: return None pre = cur - - if cur.val < num: # 插入位置在 root 的右子树中 + # 插入位置在 cur 的右子树中 + if cur.val < num: cur = cur.right - else: # 插入位置在 root 的左子树中 + # 插入位置在 cur 的左子树中 + else: cur = cur.left # 插入结点 val @@ -339,9 +340,9 @@ comments: true // 找到重复结点,直接返回 if (cur.val === num) return null; pre = cur; - // 插入位置在 root 的右子树中 + // 插入位置在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 插入位置在 root 的左子树中 + // 插入位置在 cur 的左子树中 else cur = cur.left; } // 插入结点 val @@ -370,9 +371,9 @@ comments: true } pre = cur; if (cur.val < num) { - cur = cur.right as TreeNode; // 插入位置在 root 的右子树中 + cur = cur.right as TreeNode; // 插入位置在 cur 的右子树中 } else { - cur = cur.left as TreeNode; // 插入位置在 root 的左子树中 + cur = cur.left as TreeNode; // 插入位置在 cur 的左子树中 } } // 插入结点 val @@ -407,9 +408,9 @@ comments: true // 找到重复结点,直接返回 if (cur.val == num) return null; pre = cur; - // 插入位置在 root 的右子树中 + // 插入位置在 cur 的右子树中 if (cur.val < num) cur = cur.right; - // 插入位置在 root 的左子树中 + // 插入位置在 cur 的左子树中 else cur = cur.left; }