diff --git a/codes/c/chapter_array_and_linkedlist/array.c b/codes/c/chapter_array_and_linkedlist/array.c new file mode 100644 index 000000000..300124a70 --- /dev/null +++ b/codes/c/chapter_array_and_linkedlist/array.c @@ -0,0 +1,112 @@ +/* + * File: array.c + * Created Time: 2022-12-20 + * Author: MolDuM (moldum@163.com) + */ + +#include "../include/include.h" + +/* 随机返回一个数组元素 */ +int randomAccess(int* nums, int size) { + // 在区间 [0, size) 中随机抽取一个数字 + int randomIndex = rand() % size; + // 获取并返回随机元素 + int randomNum = nums[randomIndex]; + return randomNum; +} + +/* 扩展数组长度 */ +int* extend(int* nums, int size, int enlarge) { + // 初始化一个扩展长度后的数组 + int* res = (int *)malloc(sizeof(int) * (size + enlarge)); + // 将原数组中的所有元素复制到新数组 + for (int i = 0; i < size; i++) { + res[i] = nums[i]; + } + // 初始化扩展后的空间 + for (int i = size; i < size + enlarge; i++) { + res[i] = 0; + } + // 返回扩展后的新数组 + return res; +} + +/* 在数组的索引 index 处插入元素 num */ +void insert(int* nums, int size, int num, int index) { + // 把索引 index 以及之后的所有元素向后移动一位 + for (int i = size - 1; i > index; i--) { + nums[i] = nums[i - 1]; + } + // 将 num 赋给 index 处元素 + nums[index] = num; +} + +/* 删除索引 index 处元素 */ +void removeItem(int* nums, int size, int index) { + // 把索引 index 之后的所有元素向前移动一位 + for (int i = index; i < size - 1; i++) { + nums[i] = nums[i + 1]; + } +} + +/* 遍历数组 */ +void traverse(int* nums, int size) { + int count = 0; + // 通过索引遍历数组 + for (int i = 0; i < size; i++) { + count++; + } +} + +/* 在数组中查找指定元素 */ +int find(int* nums, int size, int target) { + for (int i = 0; i < size; i++) { + if (nums[i] == target) + return i; + } + return -1; +} + + +/* Driver Code */ +int main() { + /* 初始化数组 */ + int size = 5; + int arr[5]; + printf("数组 arr = "); + printArray(arr, size); + + int nums[5] = { 1, 3, 2, 5, 4 }; + printf("数组 nums = "); + printArray(nums, size); + + /* 随机访问 */ + int randomNum = randomAccess(nums, size); + printf("在 nums 中获取随机元素 %d", randomNum); + + /* 长度扩展 */ + int enlarge = 3; + int* res = extend(nums, size, enlarge); + size += enlarge; + printf("将数组长度扩展至 8 ,得到 nums = "); + printArray(res, size); + + /* 插入元素 */ + insert(res, size, 6, 3); + printf("在索引 3 处插入数字 6 ,得到 nums = "); + printArray(res, size); + + /* 删除元素 */ + removeItem(res, size, 2); + printf("删除索引 2 处的元素,得到 nums = "); + printArray(res, size); + + /* 遍历数组 */ + traverse(res, size); + + /* 查找元素 */ + int index = find(res, size, 3); + printf("在 res 中查找元素 3 ,得到索引 = %d\n", index); + + return 0; +} diff --git a/codes/c/chapter_computational_complexity/time_complexity.c b/codes/c/chapter_computational_complexity/time_complexity.c index 4355ea3ad..8df06a756 100644 --- a/codes/c/chapter_computational_complexity/time_complexity.c +++ b/codes/c/chapter_computational_complexity/time_complexity.c @@ -7,33 +7,30 @@ #include "../include/include.h" /* 常数阶 */ -int constant(int n) -{ +int constant(int n) { int count = 0; int size = 100000; int i = 0; - for(int i = 0; i < size; i++){ + for (int i = 0; i < size; i++) { count ++; } return count; } /* 线性阶 */ -int linear(int n) -{ +int linear(int n) { int count = 0; - for(int i = 0; i < n; i++){ + for (int i = 0; i < n; i++) { count ++; } return count; } /* 线性阶(遍历数组) */ -int arrayTraversal(int *nums, int n) -{ +int arrayTraversal(int *nums, int n) { int count = 0; // 循环次数与数组长度成正比 - for(int i = 0; i < n; i++){ + for (int i = 0; i < n; i++) { count ++; } return count; @@ -44,8 +41,8 @@ int quadratic(int n) { int count = 0; // 循环次数与数组长度成平方关系 - for(int i = 0; i < n; i++){ - for(int j = 0; j < n; j++){ + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { count ++; } } @@ -53,35 +50,29 @@ int quadratic(int n) } /* 平方阶(冒泡排序) */ -int bubbleSort(int *nums, int n) -{ +int bubbleSort(int *nums, int n) { int count = 0; // 计数器 // 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for(int i = n - 1; i > 0; i--){ + for (int i = n - 1; i > 0; i--) { // 内循环:冒泡操作 - for (int j = 0; j < i; j++) - { + for (int j = 0; j < i; j++) { // 交换 nums[j] 与 nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = tmp; count += 3; // 元素交换包含 3 个单元操作 } - } return count; } /* 指数阶(循环实现) */ -int exponential(int n) -{ +int exponential(int n) { int count = 0; int bas = 1; // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (int i = 0; i < n; i++) - { - for (int j = 0; j < bas; j++) - { + for (int i = 0; i < n; i++) { + for (int j = 0; j < bas; j++) { count++; } bas *= 2; @@ -91,18 +82,15 @@ int exponential(int n) } /* 指数阶(递归实现) */ -int expRecur(int n) -{ +int expRecur(int n) { if (n == 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } /* 对数阶(循环实现) */ -int logarithmic(float n) -{ +int logarithmic(float n) { int count = 0; - while (n > 1) - { + while (n > 1) { n = n / 2; count++; } @@ -110,40 +98,34 @@ int logarithmic(float n) } /* 对数阶(递归实现) */ -int logRecur(float n) -{ +int logRecur(float n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } /* 线性对数阶 */ -int linearLogRecur(float n) -{ +int linearLogRecur(float n) { if (n <= 1) return 1; int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { count ++; } return count; } /* 阶乘阶(递归实现) */ -int factorialRecur(int n) -{ +int factorialRecur(int n) { if (n == 0) return 1; int count = 0; - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { count += factorialRecur(n - 1); } return count; } /* Driver Code */ -int main(int argc, char *argv[]) - { +int main(int argc, char *argv[]) { // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 int n = 8; printf("输入数据大小 n = %d\n", n); @@ -160,7 +142,7 @@ int main(int argc, char *argv[]) count = quadratic(n); printf("平方阶的计算操作数量 = %d\n", count); - for(int i = 0; i < n; i++){ + for (int i = 0; i < n; i++) { nums[i] = n - i; // [n,n-1,...,2,1] } count = bubbleSort(nums, n); @@ -183,7 +165,7 @@ int main(int argc, char *argv[]) printf("阶乘阶(递归实现)的计算操作数量 = %d\n", count); // 释放堆区内存 - if(nums != NULL){ + if (nums != NULL) { free(nums); nums = NULL; } 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 fc37ae0f0..88ed4dd2d 100644 --- a/codes/c/chapter_computational_complexity/worst_best_time_complexity.c +++ b/codes/c/chapter_computational_complexity/worst_best_time_complexity.c @@ -7,18 +7,15 @@ #include "../include/include.h" /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ -int *randomNumbers(int n) -{ +int *randomNumbers(int n) { // 分配堆区内存(创建一维可变长数组:数组中元素数量为n,元素类型为int) int *nums = (int *)malloc(n * sizeof(int)); // 生成数组 nums = { 1, 2, 3, ..., n } - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { nums[i] = i + 1; } // 随机打乱数组元素 - for (int i = n - 1; i > 0; i--) - { + for (int i = n - 1; i > 0; i--) { int j = rand() % (i + 1); int temp = nums[i]; nums[i] = nums[j]; @@ -28,33 +25,26 @@ int *randomNumbers(int n) } /* 查找数组 nums 中数字 1 所在索引 */ -int findOne(int *nums, int n) -{ - for (int i = 0; i < n; i++) - { +int findOne(int *nums, int n) { + for (int i = 0; i < n; i++) { if (nums[i] == 1) return i; } return -1; } /* Driver Code */ -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { // 初始化随机数种子 srand((unsigned int)time(NULL)); - for (int i = 0; i < 10; i++) - { + for (int i = 0; i < 10; i++) { int n = 100; int *nums = randomNumbers(n); int index = findOne(nums, n); printf("\n数组 [ 1, 2, ..., n ] 被打乱后 = "); - for (int j = 0; j < n; j++) - { - printf("%d%s", nums[j], (j == n-1)? "" : "," ); - } - printf("\n数字 1 的索引为 %d\n", index); + printArray(nums, n); + printf("数字 1 的索引为 %d\n", index); // 释放堆区内存 - if(nums != NULL){ + if (nums != NULL) { free(nums); nums = NULL; } diff --git a/codes/c/include/PrintUtil.h b/codes/c/include/PrintUtil.h new file mode 100644 index 000000000..e471f0388 --- /dev/null +++ b/codes/c/include/PrintUtil.h @@ -0,0 +1,30 @@ +/* + * File: PrintUtil.h + * Created Time: 2022-12-21 + * Author: MolDum (moldum@163.com) + */ + +#include +#include +#include + +// #include "ListNode.h" +// #include "TreeNode.h" + +/** + * @brief Print an Array + * + * @param arr + * @param n + */ + +static void printArray(int* arr, int n) +{ + printf("["); + for (int i = 0; i < n - 1; i++) { + printf("%d, ", arr[i]); + } + printf("%d]\n", arr[n-1]); +} + + diff --git a/codes/c/include/include.h b/codes/c/include/include.h index 8ce104cd2..c567aa2ec 100644 --- a/codes/c/include/include.h +++ b/codes/c/include/include.h @@ -1,4 +1,15 @@ +/* + * File: include.h + * Created Time: 2022-12-20 + * Author: MolDuM (moldum@163.com) + */ + #include -#include #include -#include \ No newline at end of file +#include +#include +#include + +#include "PrintUtil.h" + + diff --git a/codes/cpp/chapter_stack_and_queue/array_queue.cpp b/codes/cpp/chapter_stack_and_queue/array_queue.cpp index 387ffd142..8e87da501 100644 --- a/codes/cpp/chapter_stack_and_queue/array_queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/array_queue.cpp @@ -50,11 +50,10 @@ public: } /* 出队 */ - int poll() { + void poll() { int num = peek(); // 队头指针向后移动一位,若越过尾部则返回到数组头部 front = (front + 1) % capacity(); - return num; } /* 访问队首元素 */ @@ -98,8 +97,8 @@ int main() { cout << "队首元素 peek = " << peek << endl; /* 元素出队 */ - int poll = queue->poll(); - cout << "出队元素 poll = " << poll << ",出队后 queue = "; + queue->poll(); + cout << "出队元素 poll = " << peek << ",出队后 queue = "; PrintUtil::printVector(queue->toVector()); /* 获取队列的长度 */ diff --git a/codes/cpp/chapter_stack_and_queue/array_stack.cpp b/codes/cpp/chapter_stack_and_queue/array_stack.cpp index 517a18a4d..e9ecae2a8 100644 --- a/codes/cpp/chapter_stack_and_queue/array_stack.cpp +++ b/codes/cpp/chapter_stack_and_queue/array_stack.cpp @@ -28,10 +28,9 @@ public: } /* 出栈 */ - int pop() { + void pop() { int oldTop = top(); stack.pop_back(); - return oldTop; } /* 访问栈顶元素 */ @@ -67,8 +66,8 @@ int main() { cout << "栈顶元素 top = " << top << endl; /* 元素出栈 */ - int pop = stack->pop(); - cout << "出栈元素 pop = " << pop << ",出栈后 stack = "; + stack->pop(); + cout << "出栈元素 pop = " << top << ",出栈后 stack = "; PrintUtil::printVector(stack->toVector()); /* 获取栈的长度 */ diff --git a/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp b/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp index dcaac225f..10cba0aaf 100644 --- a/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp +++ b/codes/cpp/chapter_stack_and_queue/linkedlist_queue.cpp @@ -47,7 +47,7 @@ public: } /* 出队 */ - int poll() { + void poll() { int num = peek(); // 删除头结点 ListNode *tmp = front; @@ -55,7 +55,6 @@ public: // 释放内存 delete tmp; queSize--; - return num; } /* 访问队首元素 */ @@ -97,8 +96,8 @@ int main() { cout << "队首元素 peek = " << peek << endl; /* 元素出队 */ - int poll = queue->poll(); - cout << "出队元素 poll = " << poll << ",出队后 queue = "; + queue->poll(); + cout << "出队元素 poll = " << peek << ",出队后 queue = "; PrintUtil::printVector(queue->toVector()); /* 获取队列的长度 */ diff --git a/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp b/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp index 312b75135..cf6f6ab3d 100644 --- a/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp +++ b/codes/cpp/chapter_stack_and_queue/linkedlist_stack.cpp @@ -37,14 +37,13 @@ public: } /* 出栈 */ - int pop() { + void pop() { int num = top(); ListNode *tmp = stackTop; stackTop = stackTop->next; // 释放内存 delete tmp; stkSize--; - return num; } /* 访问栈顶元素 */ @@ -86,8 +85,8 @@ int main() { cout << "栈顶元素 top = " << top << endl; /* 元素出栈 */ - int pop = stack->pop(); - cout << "出栈元素 pop = " << pop << ",出栈后 stack = "; + stack->pop(); + cout << "出栈元素 pop = " << top << ",出栈后 stack = "; PrintUtil::printVector(stack->toVector()); /* 获取栈的长度 */ diff --git a/codes/swift/Package.swift b/codes/swift/Package.swift index 250c09af1..a292d9fd3 100644 --- a/codes/swift/Package.swift +++ b/codes/swift/Package.swift @@ -8,11 +8,13 @@ let package = Package( .executable(name: "time_complexity", targets: ["time_complexity"]), .executable(name: "worst_best_time_complexity", targets: ["worst_best_time_complexity"]), .executable(name: "space_complexity", targets: ["space_complexity"]), + .executable(name: "leetcode_two_sum", targets: ["leetcode_two_sum"]), ], targets: [ .target(name: "utils", path: "utils"), .executableTarget(name: "time_complexity", path: "chapter_computational_complexity", sources: ["time_complexity.swift"]), .executableTarget(name: "worst_best_time_complexity", path: "chapter_computational_complexity", sources: ["worst_best_time_complexity.swift"]), .executableTarget(name: "space_complexity", dependencies: ["utils"], path: "chapter_computational_complexity", sources: ["space_complexity.swift"]), + .executableTarget(name: "leetcode_two_sum", path: "chapter_computational_complexity", sources: ["leetcode_two_sum.swift"]), ] ) diff --git a/codes/swift/chapter_computational_complexity/leetcode_two_sum.swift b/codes/swift/chapter_computational_complexity/leetcode_two_sum.swift new file mode 100644 index 000000000..6ed0e580c --- /dev/null +++ b/codes/swift/chapter_computational_complexity/leetcode_two_sum.swift @@ -0,0 +1,46 @@ +/* + * File: leetcode_two_sum.swift + * Created Time: 2023-01-03 + * Author: nuomi1 (nuomi1@qq.com) + */ + +func twoSumBruteForce(nums: [Int], target: Int) -> [Int] { + // 两层循环,时间复杂度 O(n^2) + for i in nums.indices.dropLast() { + for j in nums.indices.dropFirst(i + 1) { + if nums[i] + nums[j] == target { + return [i, j] + } + } + } + return [0] +} + +func twoSumHashTable(nums: [Int], target: Int) -> [Int] { + // 辅助哈希表,空间复杂度 O(n) + var dic: [Int: Int] = [:] + // 单层循环,时间复杂度 O(n) + for i in nums.indices { + if let j = dic[target - nums[i]] { + return [j, i] + } + dic[nums[i]] = i + } + return [0] +} + +@main +enum LeetcodeTwoSum { + static func main() { + // ======= Test Case ======= + let nums = [2, 7, 11, 15] + let target = 9 + // ====== Driver Code ====== + // 方法一 + var res = twoSumBruteForce(nums: nums, target: target) + print("方法一 res = \(res)") + // 方法二 + res = twoSumHashTable(nums: nums, target: target) + print("方法二 res = \(res)") + } +} diff --git a/docs/chapter_array_and_linkedlist/summary.md b/docs/chapter_array_and_linkedlist/summary.md index 35e4e17c8..60fe604f0 100644 --- a/docs/chapter_array_and_linkedlist/summary.md +++ b/docs/chapter_array_and_linkedlist/summary.md @@ -4,7 +4,7 @@ comments: true # 小结 -- 数组和链表是两种基本数据结构,代表了数据在计算机内存中的两种存储方式,即连续空间存储和离散空间存储。两者的优缺点呈现出此消彼长的关系。 +- 数组和链表是两种基本数据结构,代表了数据在计算机内存中的两种存储方式,即连续空间存储和离散空间存储。两者的优点与缺点呈现出此消彼长的关系。 - 数组支持随机访问、内存空间占用小;但插入与删除元素效率低,且初始化后长度不可变。 - 链表可通过更改指针实现高效的结点插入与删除,并且可以灵活地修改长度;但结点访问效率低、占用内存多。常见的链表类型有单向链表、循环链表、双向链表。 - 列表又称动态数组,是基于数组实现的一种数据结构,其保存了数组的优势,且可以灵活改变长度。列表的出现大大提升了数组的实用性,但副作用是会造成部分内存空间浪费。 diff --git a/docs/chapter_computational_complexity/space_time_tradeoff.md b/docs/chapter_computational_complexity/space_time_tradeoff.md index 651aff148..0b86c11ca 100644 --- a/docs/chapter_computational_complexity/space_time_tradeoff.md +++ b/docs/chapter_computational_complexity/space_time_tradeoff.md @@ -149,6 +149,22 @@ comments: true } ``` +=== "Swift" + + ```swift title="leetcode_two_sum.swift" + func twoSumBruteForce(nums: [Int], target: Int) -> [Int] { + // 两层循环,时间复杂度 O(n^2) + for i in nums.indices.dropLast() { + for j in nums.indices.dropFirst(i + 1) { + if nums[i] + nums[j] == target { + return [i, j] + } + } + } + return [0] + } + ``` + ### 方法二:辅助哈希表 时间复杂度 $O(N)$ ,空间复杂度 $O(N)$ ,属于「空间换时间」。 @@ -294,3 +310,20 @@ comments: true } } ``` + +=== "Swift" + + ```swift title="leetcode_two_sum.swift" + func twoSumHashTable(nums: [Int], target: Int) -> [Int] { + // 辅助哈希表,空间复杂度 O(n) + var dic: [Int: Int] = [:] + // 单层循环,时间复杂度 O(n) + for i in nums.indices { + if let j = dic[target - nums[i]] { + return [j, i] + } + dic[nums[i]] = i + } + return [0] + } + ``` diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 5d61dc005..42bf2c256 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -92,8 +92,7 @@ $$ ```c title="" // 在某运行平台下 - void algorithm(int n) - { + void algorithm(int n) { int a = 2; // 1 ns a = a + 1; // 1 ns a = a * 2; // 10 ns @@ -243,20 +242,17 @@ $$ ```c title="" // 算法 A 时间复杂度:常数阶 - void algorithm_A(int n) - { + void algorithm_A(int n) { printf("%d", 0); } // 算法 B 时间复杂度:线性阶 - void algorithm_B(int n) - { + void algorithm_B(int n) { for (int i = 0; i < n; i++) { printf("%d", 0); } } // 算法 C 时间复杂度:常数阶 - void algorithm_C(int n) - { + void algorithm_C(int n) { for (int i = 0; i < 1000000; i++) { printf("%d", 0); } @@ -401,8 +397,7 @@ $$ === "C" ```c title="" - void algorithm(int n) - { + void algorithm(int n) { int a = 1; // +1 a = a + 1; // +1 a = a * 2; // +1 @@ -579,8 +574,7 @@ $$ === "C" ```c title="" - void algorithm(int n) - { + void algorithm(int n) { int a = 1; // +0(技巧 1) a = a + n; // +0(技巧 1) // +n(技巧 2) @@ -749,12 +743,11 @@ $$ ```c title="time_complexity.c" /* 常数阶 */ - int constant(int n) - { + int constant(int n) { int count = 0; int size = 100000; int i = 0; - for(int i = 0; i < size; i++){ + for (int i = 0; i < size; i++) { count ++; } return count; @@ -857,10 +850,9 @@ $$ ```c title="time_complexity.c" /* 线性阶 */ - int linear(int n) - { + int linear(int n) { int count = 0; - for(int i = 0; i < n; i++){ + for (int i = 0; i < n; i++) { count ++; } return count; @@ -969,11 +961,10 @@ $$ ```c title="time_complexity.c" /* 线性阶(遍历数组) */ - int arrayTraversal(int *nums, int n) - { + int arrayTraversal(int *nums, int n) { int count = 0; // 循环次数与数组长度成正比 - for(int i = 0; i < n; i++){ + for (int i = 0; i < n; i++) { count ++; } return count; @@ -1091,12 +1082,11 @@ $$ ```c title="time_complexity.c" /* 平方阶 */ - int quadratic(int n) - { + int quadratic(int n) { int count = 0; // 循环次数与数组长度成平方关系 - for(int i = 0; i < n; i++){ - for(int j = 0; j < n; j++){ + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { count ++; } } @@ -1253,14 +1243,12 @@ $$ ```c title="time_complexity.c" /* 平方阶(冒泡排序) */ - int bubbleSort(int *nums, int n) - { + int bubbleSort(int *nums, int n) { int count = 0; // 计数器 // 外循环:待排序元素数量为 n-1, n-2, ..., 1 - for(int i = n - 1; i > 0; i--){ + for (int i = n - 1; i > 0; i--) { // 内循环:冒泡操作 - for (int j = 0; j < i; j++) - { + for (int j = 0; j < i; j++) { // 交换 nums[j] 与 nums[j + 1] int tmp = nums[j]; nums[j] = nums[j + 1]; @@ -1417,15 +1405,12 @@ $$ ```c title="time_complexity.c" /* 指数阶(循环实现) */ - int exponential(int n) - { + int exponential(int n) { int count = 0; int bas = 1; // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) - for (int i = 0; i < n; i++) - { - for (int j = 0; j < bas; j++) - { + for (int i = 0; i < n; i++) { + for (int j = 0; j < bas; j++) { count++; } bas *= 2; @@ -1538,8 +1523,7 @@ $$ ```c title="time_complexity.c" /* 指数阶(递归实现) */ - int expRecur(int n) - { + int expRecur(int n) { if (n == 1) return 1; return expRecur(n - 1) + expRecur(n - 1) + 1; } @@ -1646,11 +1630,9 @@ $$ ```c title="time_complexity.c" /* 对数阶(循环实现) */ - int logarithmic(float n) - { + int logarithmic(float n) { int count = 0; - while (n > 1) - { + while (n > 1) { n = n / 2; count++; } @@ -1752,8 +1734,7 @@ $$ ```c title="time_complexity.c" /* 对数阶(递归实现) */ - int logRecur(float n) - { + int logRecur(float n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } @@ -1864,13 +1845,11 @@ $$ ```c title="time_complexity.c" /* 线性对数阶 */ - int linearLogRecur(float n) - { + int linearLogRecur(float n) { if (n <= 1) return 1; int count = linearLogRecur(n / 2) + linearLogRecur(n / 2); - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { count ++; } return count; @@ -2000,12 +1979,10 @@ $$ ```c title="time_complexity.c" /* 阶乘阶(递归实现) */ - int factorialRecur(int n) - { + int factorialRecur(int n) { if (n == 0) return 1; int count = 0; - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { count += factorialRecur(n - 1); } return count; @@ -2226,18 +2203,15 @@ $$ ```c title="worst_best_time_complexity.c" /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ - int *randomNumbers(int n) - { + int *randomNumbers(int n) { // 分配堆区内存(创建一维可变长数组:数组中元素数量为n,元素类型为int) int *nums = (int *)malloc(n * sizeof(int)); // 生成数组 nums = { 1, 2, 3, ..., n } - for (int i = 0; i < n; i++) - { + for (int i = 0; i < n; i++) { nums[i] = i + 1; } // 随机打乱数组元素 - for (int i = n - 1; i > 0; i--) - { + for (int i = n - 1; i > 0; i--) { int j = rand() % (i + 1); int temp = nums[i]; nums[i] = nums[j]; @@ -2247,33 +2221,26 @@ $$ } /* 查找数组 nums 中数字 1 所在索引 */ - int findOne(int *nums, int n) - { - for (int i = 0; i < n; i++) - { + int findOne(int *nums, int n) { + for (int i = 0; i < n; i++) { if (nums[i] == 1) return i; } return -1; } /* Driver Code */ - int main(int argc, char *argv[]) - { + int main(int argc, char *argv[]) { // 初始化随机数种子 srand((unsigned int)time(NULL)); - for (int i = 0; i < 10; i++) - { + for (int i = 0; i < 10; i++) { int n = 100; int *nums = randomNumbers(n); int index = findOne(nums, n); printf("\n数组 [ 1, 2, ..., n ] 被打乱后 = "); - for (int j = 0; j < n; j++) - { - printf("%d%s", nums[j], (j == n-1)? "" : "," ); - } - printf("\n数字 1 的索引为 %d\n", index); + printArray(nums, n); + printf("数字 1 的索引为 %d\n", index); // 释放堆区内存 - if(nums != NULL){ + if (nums != NULL) { free(nums); nums = NULL; } diff --git a/docs/chapter_preface/about_the_book.md b/docs/chapter_preface/about_the_book.md index db70a8acd..54cd2844c 100644 --- a/docs/chapter_preface/about_the_book.md +++ b/docs/chapter_preface/about_the_book.md @@ -82,10 +82,118 @@ comments: true ## 风格约定 -- 文章中的重要名词会用「」符号标注,例如「数组 Array」。名词混淆会导致不必要的歧义,因此最好可以记住这类名词(包括中文和英文),以便后续阅读文献时使用。 -- 重点内容、总起句、总结句会被 **加粗** ,此类文字值得更多关注。 -- 专有名词和有特指含义的词句会使用 “ ” 标注,以避免歧义。 - 标题后标注 * 符号的是选读章节,如果你的时间有限,可以先跳过这些章节。 +- 文章中的重要名词会用「」符号标注,例如「数组 Array」。名词混淆会导致不必要的歧义,因此最好可以记住这类名词(包括中文和英文),以便后续阅读文献时使用。 +- 重点内容、总起句、总结句会被 **加粗** ,此类文字值得特别关注。 +- 专有名词和有特指含义的词句会使用 “ ” 标注,以避免歧义。 +- 在工程应用中,每种语言都有注释规范;而本书放弃了一部分的注释规范性,以换取更加紧凑的内容排版。注释主要分为三种类型:标题注释、内容注释、多行注释。 + +=== "Java" + + ```java title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + +=== "C++" + + ```cpp title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + +=== "Python" + + ```python title="" + """ 标题注释,用于标注函数、类、测试样例等 """ + + # 内容注释,用于详解代码 + + """ + 多行 + 注释 + """ + ``` + +=== "Go" + + ```go title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + +=== "JavaScript" + + ```js title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + +=== "TypeScript" + + ```typescript title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + +=== "C" + + ```c title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + +=== "C#" + + ```csharp title="" + /* 标题注释,用于标注函数、类、测试样例等 */ + + // 内容注释,用于详解代码 + + /** + * 多行 + * 注释 + */ + ``` + +""" +在 Java, C, C++, C#, Go, JS, TS 的代码注释中,`/* ... */` 用于注释函数、类、测试样例等标题, `// ...` 用于解释代码内容;类似地,在 Python 中,`""" ... """` 用于注释标题, `# ...` 用于解释代码。 ## 本书特点 * diff --git a/docs/chapter_preface/contribution.assets/edit_markdown.png b/docs/chapter_preface/contribution.assets/edit_markdown.png index 02fc6318d..25037fce6 100644 Binary files a/docs/chapter_preface/contribution.assets/edit_markdown.png and b/docs/chapter_preface/contribution.assets/edit_markdown.png differ diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index 35d6a3210..6bc53fe5a 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -466,7 +466,7 @@ $$ **空间复杂度 $O(1)$ :** 指针 `i` , `j` 使用常数大小空间。 -## 优缺点 +## 优点与缺点 二分查找效率很高,体现在: diff --git a/docs/chapter_searching/hashing_search.md b/docs/chapter_searching/hashing_search.md index a52f72e21..a70a55483 100644 --- a/docs/chapter_searching/hashing_search.md +++ b/docs/chapter_searching/hashing_search.md @@ -185,7 +185,7 @@ comments: true **空间复杂度:** $O(n)$ ,其中 $n$ 为数组或链表长度。 -## 优缺点 +## 优点与缺点 在哈希表中,**查找、插入、删除操作的平均时间复杂度都为 $O(1)$** ,这意味着无论是高频增删还是高频查找场景,哈希查找的性能表现都非常好。当然,一切的前提是保证哈希表未退化。 diff --git a/docs/chapter_searching/linear_search.md b/docs/chapter_searching/linear_search.md index 3284db06c..3d592962e 100644 --- a/docs/chapter_searching/linear_search.md +++ b/docs/chapter_searching/linear_search.md @@ -244,7 +244,7 @@ comments: true **空间复杂度 $O(1)$ :** 无需使用额外空间。 -## 优缺点 +## 优点与缺点 **线性查找的通用性极佳。** 由于线性查找是依次访问元素的,即没有跳跃访问元素,因此数组或链表皆适用。 diff --git a/docs/chapter_stack_and_queue/queue.md b/docs/chapter_stack_and_queue/queue.md index cc815b3ab..cbf854f9c 100644 --- a/docs/chapter_stack_and_queue/queue.md +++ b/docs/chapter_stack_and_queue/queue.md @@ -328,7 +328,7 @@ comments: true queSize++; } /* 出队 */ - int poll() { + void poll() { int num = peek(); // 删除头结点 ListNode *tmp = front; @@ -336,7 +336,6 @@ comments: true // 释放内存 delete tmp; queSize--; - return num; } /* 访问队首元素 */ int peek() { @@ -719,11 +718,10 @@ comments: true rear = (rear + 1) % capacity(); } /* 出队 */ - int poll() { + void poll() { int num = peek(); // 队头指针向后移动一位,若越过尾部则返回到数组头部 front = (front + 1) % capacity(); - return num; } /* 访问队首元素 */ int peek() { diff --git a/docs/chapter_stack_and_queue/stack.md b/docs/chapter_stack_and_queue/stack.md index c982f5a75..ac4cf7cca 100644 --- a/docs/chapter_stack_and_queue/stack.md +++ b/docs/chapter_stack_and_queue/stack.md @@ -236,7 +236,9 @@ comments: true ### 基于链表的实现 -使用「链表」实现栈时,将链表的尾结点看作栈顶即可。 +使用「链表」实现栈时,将链表的头结点看作栈顶,尾结点看作栈底。 + +对于入栈操作,将元素插入到链表头部即可,这种结点添加方式被称为“头插法”。而对于出栈操作,则将头结点从链表中删除即可。 受益于链表的离散存储方式,栈的扩容更加灵活,删除元素的内存也会被系统自动回收;缺点是无法像数组一样高效地随机访问,并且由于链表结点需存储指针,导致单个元素占用空间更大。 @@ -311,14 +313,13 @@ comments: true stkSize++; } /* 出栈 */ - int pop() { + void pop() { int num = top(); ListNode *tmp = stackTop; stackTop = stackTop->next; // 释放内存 delete tmp; stkSize--; - return num; } /* 访问栈顶元素 */ int top() { @@ -592,7 +593,7 @@ comments: true ### 基于数组的实现 -使用「数组」实现栈时,将数组的尾部当作栈顶。准确地说,我们需要使用「列表」,因为入栈的元素可能是源源不断的,因此使用动态数组可以方便扩容。 +使用「数组」实现栈时,将数组的尾部当作栈顶,这样可以保证入栈与出栈操作的时间复杂度都为 $O(1)$ 。准确地说,由于入栈的元素可能是源源不断的,我们需要使用可以动态扩容的「列表」。 基于数组实现的栈,优点是支持随机访问,缺点是会造成一定的空间浪费,因为列表的容量始终 $\geq$ 元素数量。 @@ -655,10 +656,9 @@ comments: true stack.push_back(num); } /* 出栈 */ - int pop() { + void pop() { int oldTop = top(); stack.pop_back(); - return oldTop; } /* 访问栈顶元素 */ int top() { diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 0c858fd3a..3e3222222 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -120,7 +120,7 @@ comments: true - 「根结点 Root Node」:二叉树最顶层的结点,其没有父结点; - 「叶结点 Leaf Node」:没有子结点的结点,其两个指针都指向 $\text{null}$ ; -- 结点所处「层 Level」:从顶置底依次增加,根结点所处层为 1 ; +- 结点所处「层 Level」:从顶至底依次增加,根结点所处层为 1 ; - 结点「度 Degree」:结点的子结点数量。二叉树中,度的范围是 0, 1, 2 ; - 「边 Edge」:连接两个结点的边,即结点指针; - 二叉树「高度」:二叉树中根结点到最远叶结点走过边的数量; diff --git a/docs/index.md b/docs/index.md index 71c34360d..3b6405202 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,7 @@ hide: ![conceptual_rendering](index.assets/conceptual_rendering.png){ align=left width=350 }




《 Hello,算法 》

-

动画图解、能运行、可讨论的
数据结构与算法快速入门教程

+

动画图解、能运行、可提问的
数据结构与算法快速入门教程

[![github-stars](https://img.shields.io/github/stars/krahets/hello-algo?style=social)](https://github.com/krahets/hello-algo)

[@Krahets](https://leetcode.cn/u/jyd/)