diff --git a/.gitignore b/.gitignore index c960a1bb3..52026b066 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,9 @@ # Editor .vscode/ .idea/ +cmake-build-debug/ 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..1a208dd9d --- /dev/null +++ b/codes/c/CMakeLists.txt @@ -0,0 +1,12 @@ +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) +add_subdirectory(chapter_tree) 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_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/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/c/chapter_computational_complexity/worst_best_time_complexity.c b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c index 2570ff3c3..e5cee821f 100644 --- a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c +++ b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c @@ -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..51bab6c64 100644 --- a/codes/c/chapter_sorting/bubble_sort.c +++ b/codes/c/chapter_sorting/bubble_sort.c @@ -7,7 +7,7 @@ #include "../include/include.h" /* 冒泡排序 */ -void bubble_sort(int nums[], int size) { +void bubbleSort(int nums[], int size) { // 外循环:待排序元素数量为 n-1, n-2, ..., 1 for (int i = 0; i < size - 1; i++) { @@ -25,7 +25,7 @@ void bubble_sort(int nums[], int size) { } /* 冒泡排序(标志优化)*/ -void bubble_sort_with_flag(int nums[], int size) { +void bubbleSortWithFlag(int nums[], int size) { // 外循环:待排序元素数量为 n-1, n-2, ..., 1 for (int i = 0; i < size - 1; i++) { @@ -50,15 +50,15 @@ void bubble_sort_with_flag(int nums[], int size) { /* Driver Code */ int main() { int nums[6] = {4, 1, 3, 1, 5, 2}; - printf("冒泡排序后:\n"); - bubble_sort(nums, 6); + printf("冒泡排序后: "); + bubbleSort(nums, 6); for (int i = 0; i < 6; i++) { printf("%d ", nums[i]); } - printf("优化版冒泡排序后:\n"); - bubble_sort_with_flag(nums, 6); + printf("\n优化版冒泡排序后: "); + bubbleSortWithFlag(nums, 6); for (int i = 0; i < 6; i++) { printf("%d ", nums[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/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..1eb8e846a --- /dev/null +++ b/codes/c/chapter_tree/binary_tree.c @@ -0,0 +1,42 @@ +/** + * 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..c7b83b912 --- /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 = arrToTree(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..bafc90fd1 --- /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) / sizeof(int); + TreeNode *root = arrToTree(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/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..602670d68 --- /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 = arrToLinkedList(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 = arrToTree(nums, size); + + // print tree + printTree(root); + + // tree to arr + int *arr = treeToArr(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..1845ffafd --- /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 *arrToLinkedList(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..a0c3a1500 --- /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..c0438c437 --- /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 *arrToTree(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 *treeToArr(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 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/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 diff --git a/codes/cpp/chapter_tree/binary_search_tree.cpp b/codes/cpp/chapter_tree/binary_search_tree.cpp index 78e797746..006d7d308 100644 --- a/codes/cpp/chapter_tree/binary_search_tree.cpp +++ b/codes/cpp/chapter_tree/binary_search_tree.cpp @@ -133,7 +133,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/cpp/include/include.hpp b/codes/cpp/include/include.hpp index 8e4a8070d..03bf9b503 100644 --- a/codes/cpp/include/include.hpp +++ b/codes/cpp/include/include.hpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "ListNode.hpp" #include "TreeNode.hpp" 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 d700a5ae7..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,23 +28,19 @@ 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++) { - head = head.next; if (head == null) return null; + head = head.next; } 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..e12cdd425 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; @@ -163,7 +159,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/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; 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/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/avl_tree.go b/codes/go/chapter_tree/avl_tree.go new file mode 100644 index 000000000..e83b2882b --- /dev/null +++ b/codes/go/chapter_tree/avl_tree.go @@ -0,0 +1,211 @@ +// 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/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..2a864d138 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) - fmt.Println("\n查找到的结点对象为", node, ",结点值 =", node.Val) + node = bst.search(7) + fmt.Println("查找到的结点对象为", 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/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/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 +} diff --git a/codes/go/pkg/tree_node.go b/codes/go/pkg/tree_node.go index b1e630e67..6baed50eb 100644 --- a/codes/go/pkg/tree_node.go +++ b/codes/go/pkg/tree_node.go @@ -9,25 +9,28 @@ 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, } } // 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 +40,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 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/java/chapter_tree/binary_search_tree.java b/codes/java/chapter_tree/binary_search_tree.java index 4a7f3194e..a3b0f7584 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_searching/hashing_search.js b/codes/javascript/chapter_searching/hashing_search.js new file mode 100644 index 000000000..ebe6a139f --- /dev/null +++ b/codes/javascript/chapter_searching/hashing_search.js @@ -0,0 +1,51 @@ +/** + * File: hashing_search.js + * Created Time: 2022-12-29 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +const PrintUtil = require("../include/PrintUtil"); +const ListNode = require("../include/ListNode"); + + +/* 哈希查找(数组) */ +function hashingSearch(map, target) { + // 哈希表的 key: 目标元素,value: 索引 + // 若哈希表中无此 key ,返回 -1 + return map.has(target) ? map.get(target) : -1; +} + +/* 哈希查找(链表) */ +function hashingSearch1(map, target) { + // 哈希表的 key: 目标结点值,value: 结点对象 + // 若哈希表中无此 key ,返回 null + return map.has(target) ? map.get(target) : null; +} + +function main() { + const target = 3; + + /* 哈希查找(数组) */ + const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; + // 初始化哈希表 + const map = new Map(); + for (let i = 0; i < nums.length; i++) { + map.set(nums[i], i); // key: 元素,value: 索引 + } + const index = hashingSearch(map, target); + console.log("目标元素 3 的索引 = " + index); + + /* 哈希查找(链表) */ + let head = new ListNode().arrToLinkedList(nums) + // 初始化哈希表 + const map1 = new Map(); + while (head != null) { + map1.set(head.val, head); // key: 结点值,value: 结点 + head = head.next; + } + const node = hashingSearch1(map1, target); + console.log("目标结点值 3 的对应结点对象为" ); + PrintUtil.printLinkedList(node); +} + +main(); diff --git a/codes/javascript/chapter_tree/binary_search_tree.js b/codes/javascript/chapter_tree/binary_search_tree.js index c00d8bea8..9c808aaae 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_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/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..8817d4a5a 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 # 循环访问左子结点,直到叶结点时为最小结点,跳出 @@ -144,7 +142,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/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/codes/rust/chapter_computational_complexity/time_complexity.rs b/codes/rust/chapter_computational_complexity/time_complexity.rs new file mode 100644 index 000000000..a6aa60b31 --- /dev/null +++ b/codes/rust/chapter_computational_complexity/time_complexity.rs @@ -0,0 +1,163 @@ +#![allow(unused_variables)] + +/* 常数阶 */ +fn constant(n: i32) -> i32 { + let mut count = 0; + let size = 100000; + for _ in 0..size { + count += 1 + } + count +} + +fn linear(n: i32) -> i32 { + let mut count = 0; + for _ in 0..n { + count += 1; + } + count +} + +/* 线性阶(遍历数组) */ +fn array_traversal(nums: &[i32]) -> i32 { + let mut count = 0; + // 循环次数与数组长度成正比 + for _ in nums { + count += 1; + } + count +} + +fn quadratic(n: i32) -> i32 { + let mut count = 0; + // 循环次数与数组长度成平方关系 + for _ in 0..n { + for _ in 0..n { + count += 1; + } + } + count +} + +/* 平方阶(冒泡排序) */ +fn bubble_sort(nums: &mut [i32]) -> i32 { + let mut count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for i in (1..nums.len()).rev() { + // 内循环:冒泡操作 + 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 个单元操作 + } + } + } + count +} + +/* 指数阶(循环实现) */ +fn exponential(n: i32) -> i32 { + let mut count = 0; + let mut 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 + count +} + +/* 指数阶(递归实现) */ +fn exp_recur(n: i32) -> i32 { + if n == 1 { + return 1; + } + exp_recur(n - 1) + exp_recur(n - 1) + 1 +} + +/* 对数阶(循环实现) */ +fn logarithmic(mut n: i32) -> i32 { + let mut count = 0; + + while n > 1 { + n = n / 2; + count += 1; + } + count +} + +fn log_recur(n: i32) -> i32 { + if n <= 1 { + return 0; + } + log_recur(n / 2) + 1 +} + +/* 线性对数阶 */ +fn linear_log_recur(n: f64) -> i32 { + if n <= 1.0 { + return 1; + } + let mut count = linear_log_recur(n / 2.0) + linear_log_recur(n / 2.0); + for _ in 0 ..n as i32 { + count += 1; + } + return count +} + +/* 阶乘阶(递归实现) */ +fn factorial_recur(n: i32) -> i32 { + if n == 0 { + return 1; + } + let mut count = 0; + // 从 1 个分裂出 n 个 + for _ in 0..n { + count += factorial_recur(n - 1); + } + count +} + +/* Driver Code */ +fn main() { + // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 + let n: i32 = 8; + println!("输入数据大小 n = {}", n); + + let mut count = constant(n); + println!("常数阶的计算操作数量 = {}", count); + + count = linear(n); + println!("线性阶的计算操作数量 = {}", count); + count = array_traversal(&vec![0; n as usize]); + println!("线性阶(遍历数组)的计算操作数量 = {}", count); + + count = quadratic(n); + println!("平方阶的计算操作数量 = {}", count); + + let mut nums = (1..=n).rev().collect::>(); // [n,n-1,...,2,1] + count = bubble_sort(&mut nums); + println!("平方阶(冒泡排序)的计算操作数量 = {}", count); + + count = exponential(n); + println!("指数阶(循环实现)的计算操作数量 = {}", count); + count = exp_recur(n); + println!("指数阶(递归实现)的计算操作数量 = {}", count); + + count = logarithmic(n); + println!("对数阶(循环实现)的计算操作数量 = {}", count); + count = log_recur(n); + println!("对数阶(递归实现)的计算操作数量 = {}", count); + + count = linear_log_recur(n.into()); + println!("线性对数阶(递归实现)的计算操作数量 = {}", count); + + count = factorial_recur(n); + println!("阶乘阶(递归实现)的计算操作数量 = {}", count); +} \ No newline at end of file diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index d9368354b..6d6eaa8a5 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -10,6 +10,12 @@ 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"]), + .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"), @@ -18,5 +24,11 @@ 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"]), + .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_array_and_linkedlist/linked_list.swift b/codes/swift/chapter_array_and_linkedlist/linked_list.swift new file mode 100644 index 000000000..90218b26b --- /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 { + if head == nil { + return nil + } + head = head?.next + } + 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/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/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..1b1027ef2 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,11 +45,11 @@ func quadratic(n: Int) -> Int { 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 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] { @@ -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,39 +133,40 @@ func factorialRecur(n: Int) -> Int { @main enum TimeComplexity { + /* Driver Code */ 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) - var nums = Array(sequence(first: n, next: { $0 > 0 ? $0 - 1 : nil })) // [n,n-1,...,2,1] + print("平方阶的计算操作数量 = \(count)") + var nums = Array(sequence(first: n, next: { $0 > 0 + 1 ? $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 09d7de1f4..3dac661d2 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,14 +25,14 @@ func findOne(nums: [Int]) -> Int { @main enum WorstBestTimeComplexity { - // Driver Code + /* 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) + print("数组 [ 1, 2, ..., n ] 被打乱后 = \(nums)") + print("数字 1 的索引为 \(index)") } } } 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/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/codes/typescript/chapter_searching/hashing_search.ts b/codes/typescript/chapter_searching/hashing_search.ts new file mode 100644 index 000000000..7f93d7178 --- /dev/null +++ b/codes/typescript/chapter_searching/hashing_search.ts @@ -0,0 +1,51 @@ +/** + * File: hashing_search.js + * Created Time: 2022-12-29 + * Author: Zhuo Qinyue (1403450829@qq.com) + */ + +import { printLinkedList } from "../module/PrintUtil"; +import ListNode from "../module/ListNode"; + + +/* 哈希查找(数组) */ +function hashingSearch(map: Map, target: number): number { + // 哈希表的 key: 目标元素,value: 索引 + // 若哈希表中无此 key ,返回 -1 + return map.has(target) ? map.get(target) as number : -1; +} + +/* 哈希查找(链表) */ +function hashingSearch1(map: Map, target: number): ListNode | null { + // 哈希表的 key: 目标结点值,value: 结点对象 + // 若哈希表中无此 key ,返回 null + return map.has(target) ? map.get(target) as ListNode : null; +} + +function main() { + const target = 3; + + /* 哈希查找(数组) */ + const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; + // 初始化哈希表 + const map = new Map(); + for (let i = 0; i < nums.length; i++) { + map.set(nums[i], i); // key: 元素,value: 索引 + } + const index = hashingSearch(map, target); + console.log("目标元素 3 的索引 = " + index); + + /* 哈希查找(链表) */ + let head = new ListNode().arrToLinkedList(nums) + // 初始化哈希表 + const map1 = new Map(); + while (head != null) { + map1.set(head.val, head); // key: 结点值,value: 结点 + head = head.next; + } + const node = hashingSearch1(map1, target); + console.log("目标结点值 3 的对应结点对象为"); + printLinkedList(node); +} + +main(); diff --git a/codes/typescript/chapter_searching/linear_search.ts b/codes/typescript/chapter_searching/linear_search.ts new file mode 100644 index 000000000..f0de33f78 --- /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/chapter_tree/binary_search_tree.ts b/codes/typescript/chapter_tree/binary_search_tree.ts index a99e6ba26..9adf8cd15 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); /* 插入结点 */ diff --git a/codes/typescript/module/ListNode.ts b/codes/typescript/module/ListNode.ts index 17fdbb955..fae30d48a 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 + */ + arrToLinkedList(arr: number[]): ListNode | null { + const dum: ListNode = new ListNode(0); + let head = dum; + for (const val of arr) { + head.next = new ListNode(val); + head = head.next; + } + return dum.next; + } } 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..1a9fb0fba --- /dev/null +++ b/codes/zig/build.zig @@ -0,0 +1,67 @@ +// File: build.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); + +// Zig Version: 0.10.0 +// Build Command: zig build +pub fn build(b: *std.build.Builder) void { + const target = b.standardTargetOptions(.{}); + const mode = b.standardReleaseOptions(); + + // Section: "Time Complexity" + // 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"); + 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); + + // 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"); + 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); + + // Section: "Space Complexity" + // 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"); + 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" + // 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"); + 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/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/chapter_computational_complexity/time_complexity.zig b/codes/zig/chapter_computational_complexity/time_complexity.zig index dab17034a..047950052 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; @@ -138,7 +140,7 @@ fn factorialRecur(n: i32) i32 { } // Driver Code -pub fn main() void { +pub fn main() !void { // 可以修改 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 dacf45952..0cdd61ebc 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 { @@ -33,10 +34,8 @@ pub fn main() !void { 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/ListNode.zig b/codes/zig/include/ListNode.zig new file mode 100644 index 000000000..ae0246c09 --- /dev/null +++ b/codes/zig/include/ListNode.zig @@ -0,0 +1,21 @@ +// 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 new file mode 100644 index 000000000..9e6b18b05 --- /dev/null +++ b/codes/zig/include/PrintUtil.zig @@ -0,0 +1,73 @@ +// 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 +// 编译期泛型 +pub fn printArray(comptime T: type, nums: []T) void { + std.debug.print("[", .{}); + 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..12af9e8f3 --- /dev/null +++ b/codes/zig/include/TreeNode.zig @@ -0,0 +1,22 @@ +// 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 new file mode 100644 index 000000000..20dfca90f --- /dev/null +++ b/codes/zig/include/include.zig @@ -0,0 +1,7 @@ +// File: include.zig +// Created Time: 2023-01-07 +// Author: sjinzh (sjinzh@gmail.com) + +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 diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md index 461cdcf37..2251362d0 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) @@ -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) @@ -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_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md index aa9c93ec7..8dd069788 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 ``` ## 链表优点 @@ -381,6 +400,7 @@ comments: true n0.next = P; P.next = n1; } + /* 删除链表的结点 n0 之后的首个结点 */ function remove(n0: ListNode): void { if (!n0.next) { @@ -425,7 +445,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 + } ``` ## 链表缺点 @@ -438,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; } @@ -452,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; } @@ -466,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 ``` @@ -478,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 } @@ -530,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; } @@ -541,7 +578,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 { + if head == nil { + return nil + } + head = head?.next + } + return head + } ``` **链表的内存占用多**。链表以结点为单位,每个结点除了保存值外,还需额外保存指针(引用)。这意味着同样数据量下,链表比数组需要占用更多内存空间。 @@ -674,7 +721,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 +852,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) diff --git a/docs/chapter_array_and_linkedlist/list.md b/docs/chapter_array_and_linkedlist/list.md index d1109d65b..1a47a0d13 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,16 +626,17 @@ comments: true === "Swift" ```swift title="list.swift" - + /* 排序列表 */ + list.sort() // 排序后,列表元素从小到大排列 ``` ## 列表简易实现 * 为了帮助加深对列表的理解,我们在此提供一个列表的简易版本的实现。需要关注三个核心点: -- **初始容量:** 选取一个合理的数组的初始容量 `initialCapacity` 。在本示例中,我们选择 10 作为初始容量。 -- **数量记录:** 需要声明一个变量 `size` ,用来记录列表当前有多少个元素,并随着元素插入与删除实时更新。根据此变量,可以定位列表的尾部,以及判断是否需要扩容。 -- **扩容机制:** 插入元素有可能导致超出列表容量,此时需要扩容列表,方法是建立一个更大的数组来替换当前数组。需要给定一个扩容倍数 `extendRatio` ,在本示例中,我们规定每次将数组扩容至之前的 2 倍。 +- **初始容量**:选取一个合理的数组的初始容量 `initialCapacity` 。在本示例中,我们选择 10 作为初始容量。 +- **数量记录**:需要声明一个变量 `size` ,用来记录列表当前有多少个元素,并随着元素插入与删除实时更新。根据此变量,可以定位列表的尾部,以及判断是否需要扩容。 +- **扩容机制**:插入元素有可能导致超出列表容量,此时需要扩容列表,方法是建立一个更大的数组来替换当前数组。需要给定一个扩容倍数 `extendRatio` ,在本示例中,我们规定每次将数组扩容至之前的 2 倍。 本示例是为了帮助读者对如何实现列表产生直观的认识。实际编程语言中,列表的实现远比以下代码复杂且标准,感兴趣的读者可以查阅源码学习。 @@ -864,7 +899,7 @@ comments: true ```go title="my_list.go" /* 列表类简易实现 */ - type MyList struct { + type myList struct { numsCapacity int nums []int numsSize int @@ -872,8 +907,8 @@ comments: true } /* 构造函数 */ - func newMyList() *MyList { - return &MyList{ + func newMyList() *myList { + return &myList{ numsCapacity: 10, // 列表容量 nums: make([]int, 10), // 数组(存储列表元素) numsSize: 0, // 列表长度(即当前元素数量) @@ -882,17 +917,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 +936,7 @@ comments: true } /* 更新元素 */ - func (l *MyList) set(num, index int) { + func (l *myList) set(num, index int) { if index >= l.numsSize { panic("索引越界") } @@ -909,7 +944,7 @@ comments: true } /* 尾部添加元素 */ - func (l *MyList) add(num int) { + func (l *myList) add(num int) { // 元素数量超出容量时,触发扩容机制 if l.numsSize == l.numsCapacity { l.extendCapacity() @@ -920,7 +955,7 @@ comments: true } /* 中间插入元素 */ - func (l *MyList) insert(num, index int) { + func (l *myList) insert(num, index int) { if index >= l.numsSize { panic("索引越界") } @@ -938,20 +973,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))...) // 更新列表容量 @@ -1260,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 + } + } ``` - 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 1756f2e24..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」统计 **算法使用内存空间随着数据量变大时的增长趋势**。这个概念与时间复杂度很类似。 ## 算法相关空间 @@ -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} } /* 函数 */ @@ -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 @@ -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)) } @@ -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 @@ -1108,7 +1108,7 @@ $$ ```go title="space_complexity.go" /* 指数阶(建立满二叉树) */ - func buildTree(n int) *TreeNode { + func buildTree(n int) *treeNode { if n == 0 { return nil } @@ -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..def052c92 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 // 循环次数与数组长度成平方关系 @@ -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 个单元操作 + } } } @@ -1477,11 +1480,11 @@ $$ === "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 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] { @@ -1656,7 +1659,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 指数阶(循环实现) + /* 指数阶(循环实现) */ func exponential(n: Int) -> Int { var count = 0 var base = 1 @@ -1764,7 +1767,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 指数阶(递归实现) + /* 指数阶(递归实现) */ func expRecur(n: Int) -> Int { if n == 1 { return 1 @@ -1896,7 +1899,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 对数阶(循环实现) + /* 对数阶(循环实现) */ func logarithmic(n: Int) -> Int { var count = 0 var n = n @@ -1999,7 +2002,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 对数阶(递归实现) + /* 对数阶(递归实现) */ func logRecur(n: Int) -> Int { if n <= 1 { return 0 @@ -2137,7 +2140,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 线性对数阶 + /* 线性对数阶 */ func linearLogRecur(n: Double) -> Int { if n <= 1 { return 1 @@ -2288,7 +2291,7 @@ $$ === "Swift" ```swift title="time_complexity.swift" - // 阶乘阶(递归实现) + /* 阶乘阶(递归实现) */ func factorialRecur(n: Int) -> Int { if n == 0 { return 1 @@ -2658,7 +2661,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 +2670,7 @@ $$ return nums } - // 查找数组 nums 中数字 1 所在索引 + /* 查找数组 nums 中数字 1 所在索引 */ func findOne(nums: [Int]) -> Int { for i in nums.indices { if nums[i] == 1 { @@ -2677,14 +2680,14 @@ $$ return -1 } - // Driver Code + /* 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) + print("数组 [ 1, 2, ..., n ] 被打乱后 = \(nums)") + print("数字 1 的索引为 \(index)") } } ``` 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 3a11fdf36..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" @@ -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) 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 3a69d88fc..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)$ ,全面胜出! @@ -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_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index 2fba23502..01f513f00 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」。名词混淆会导致不必要的歧义,因此最好可以记住这类名词(包括中文和英文),以便后续阅读文献时使用。 -- 重点内容、总起句、总结句会被 **加粗** ,此类文字值得特别关注。 +- 重点内容、总起句、总结句会被 **加粗**,此类文字值得特别关注。 - 专有名词和有特指含义的词句会使用 “ ” 标注,以避免歧义。 - 在工程应用中,每种语言都有注释规范;而本书放弃了一部分的注释规范性,以换取更加紧凑的内容排版。注释主要分为三种类型:标题注释、内容注释、多行注释。 @@ -205,9 +205,6 @@ comments: true */ ``` -""" -在 Java, C, C++, C#, Go, JS, TS 的代码注释中,`/* ... */` 用于注释函数、类、测试样例等标题, `// ...` 用于解释代码内容;类似地,在 Python 中,`""" ... """` 用于注释标题, `# ...` 用于解释代码。 - ## 本书特点 * ??? abstract "默认折叠,可以跳过" diff --git a/docs/chapter_preface/installation.md b/docs/chapter_preface/installation.md index c6c1b2ee9..828365edf 100644 --- a/docs/chapter_preface/installation.md +++ b/docs/chapter_preface/installation.md @@ -46,3 +46,8 @@ comments: true 1. 下载并安装 [Swift](https://www.swift.org/download/); 2. 在 VSCode 的插件市场中搜索 `swift`,安装 [Swift for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=sswg.swift-lang)。 + +## Rust 环境 + +1. 下载并安装 [Rust](https://www.rust-lang.org/tools/install); +2. 在 VSCode 的插件市场中搜索 `rust`,安装 [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)。 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..da60b3b43 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -9,7 +9,7 @@ comments: true 使用二分查找有两个前置条件: - **要求输入数据是有序的**,这样才能通过判断大小关系来排除一半的搜索区间; -- **二分查找仅适用于数组** ,而在链表中使用效率很低,因为其在循环中需要跳跃式(非连续地)访问元素。 +- **二分查找仅适用于数组**,而在链表中使用效率很低,因为其在循环中需要跳跃式(非连续地)访问元素。 ## 算法实现 @@ -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; } ``` @@ -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..9f3d74c0c 100644 --- a/docs/chapter_searching/hashing_search.md +++ b/docs/chapter_searching/hashing_search.md @@ -68,13 +68,23 @@ comments: true === "JavaScript" ```js title="hashing_search.js" - + /* 哈希查找(数组) */ + function hashingSearch(map, target) { + // 哈希表的 key: 目标元素,value: 索引 + // 若哈希表中无此 key ,返回 -1 + return map.has(target) ? map.get(target) : -1; + } ``` === "TypeScript" ```typescript title="hashing_search.ts" - + /* 哈希查找(数组) */ + function hashingSearch(map: Map, target: number): number { + // 哈希表的 key: 目标元素,value: 索引 + // 若哈希表中无此 key ,返回 -1 + return map.has(target) ? map.get(target) as number : -1; + } ``` === "C" @@ -151,19 +161,29 @@ comments: true } else { return nil } - } + } ``` === "JavaScript" ```js title="hashing_search.js" - + /* 哈希查找(链表) */ + function hashingSearch1(map, target) { + // 哈希表的 key: 目标结点值,value: 结点对象 + // 若哈希表中无此 key ,返回 null + return map.has(target) ? map.get(target) : null; + } ``` === "TypeScript" ```typescript title="hashing_search.ts" - + /* 哈希查找(链表) */ + function hashingSearch1(map: Map, target: number): ListNode | null { + // 哈希表的 key: 目标结点值,value: 结点对象 + // 若哈希表中无此 key ,返回 null + return map.has(target) ? map.get(target) as ListNode : null; + } ``` === "C" @@ -193,9 +213,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..a42ccc365 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" @@ -216,7 +227,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" @@ -252,9 +275,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..3bb0bc994 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -170,7 +170,7 @@ comments: true ```c title="bubble_sort.c" /* 冒泡排序 */ - void bubble_sort(int nums[], int size) { + void bubbleSort(int nums[], int size) { // 外循环:待排序元素数量为 n-1, n-2, ..., 1 for (int i = 0; i < size - 1; i++) { @@ -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)$ 。 ## 效率优化 @@ -373,7 +373,7 @@ comments: true ```c title="bubble_sort.c" /* 冒泡排序 */ - void bubble_sort(int nums[], int size) { + void bubbleSortWithFlag(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 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 396baa089..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" @@ -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 { @@ -407,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 f8dcc3238..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) @@ -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 // 返回基准数的索引 } ``` @@ -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_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index 65ce56f07..1d1b09d36 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 e5f67371a..88b335fb0 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 ``` ## 栈的实现 @@ -378,43 +399,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 } ``` @@ -600,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 + } + } ``` ### 基于数组的实现 @@ -716,41 +794,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] @@ -885,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 @@ -894,5 +1022,5 @@ comments: true ## 栈典型应用 -- **浏览器中的后退与前进、软件中的撤销与反撤销**。每当我们打开新的网页,浏览器就讲上一个网页执行入栈,这样我们就可以通过「后退」操作来回到上一页面,后退操作实际上是在执行出栈。如果要同时支持后退和前进,那么则需要两个栈来配合实现。 +- **浏览器中的后退与前进、软件中的撤销与反撤销**。每当我们打开新的网页,浏览器就将上一个网页执行入栈,这样我们就可以通过「后退」操作来回到上一页面,后退操作实际上是在执行出栈。如果要同时支持后退和前进,那么则需要两个栈来配合实现。 - **程序内存管理**。每当调用函数时,系统就会在栈顶添加一个栈帧,用来记录函数的上下文信息。在递归函数中,向下递推会不断执行入栈,向上回溯阶段时出栈。 diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index 43e0d9f45..6f6272645 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" @@ -100,7 +106,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit ``` -「结点高度」是最远叶结点到该结点的距离,即走过的「边」的数量。需要特别注意,**叶结点的高度为 0 ,空结点的高度为 -1** 。我们封装两个工具函数,分别用于获取与更新结点的高度。 +「结点高度」是最远叶结点到该结点的距离,即走过的「边」的数量。需要特别注意,**叶结点的高度为 0 ,空结点的高度为 -1**。我们封装两个工具函数,分别用于获取与更新结点的高度。 === "Java" @@ -128,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 ``` @@ -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" @@ -214,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 @@ -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" @@ -277,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) @@ -322,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 向右旋转 @@ -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" @@ -425,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 向左旋转 @@ -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" @@ -564,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) # 左偏树 @@ -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" @@ -710,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. 查找插入位置,并插入结点 @@ -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" @@ -846,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. 查找结点,并删除之 @@ -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" diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index 979612852..a0322154e 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: @@ -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 { @@ -202,8 +202,8 @@ comments: true 给定一个待插入元素 `num` ,为了保持二叉搜索树“左子树 < 根结点 < 右子树”的性质,插入操作分为两步: -1. **查找插入位置:** 与查找操作类似,我们从根结点出发,根据当前结点值和 `num` 的大小关系循环向下搜索,直到越过叶结点(遍历到 $\text{null}$ )时跳出循环; -2. **在该位置插入结点:** 初始化结点 `num` ,将该结点放到 $\text{null}$ 的位置 ; +1. **查找插入位置**:与查找操作类似,我们从根结点出发,根据当前结点值和 `num` 的大小关系循环向下搜索,直到越过叶结点(遍历到 $\text{null}$ )时跳出循环; +2. **在该位置插入结点**:初始化结点 `num` ,将该结点放到 $\text{null}$ 的位置 ; 二叉搜索树不允许存在重复结点,否则将会违背其定义。因此若待插入结点在树中已经存在,则不执行插入,直接返回即可。 @@ -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: @@ -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 { @@ -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` ; @@ -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: @@ -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 } @@ -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$ 很大时有巨大优势**。 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) 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 # 访问优先级:左子树 -> 右子树 -> 根结点