diff --git a/codes/cpp/chapter_computational_complexity/leetcode_two_sum.cpp b/codes/cpp/chapter_computational_complexity/leetcode_two_sum.cpp index 24bb181..9df3b0a 100644 --- a/codes/cpp/chapter_computational_complexity/leetcode_two_sum.cpp +++ b/codes/cpp/chapter_computational_complexity/leetcode_two_sum.cpp @@ -10,6 +10,7 @@ class SolutionBruteForce { public: vector twoSum(vector& nums, int target) { int size = nums.size(); + // 两层循环,时间复杂度 O(n^2) for (int i = 0; i < size - 1; i++) { for (int j = i + 1; j < size; j++) { if (nums[i] + nums[j] == target) @@ -24,7 +25,9 @@ class SolutionHashMap { public: vector twoSum(vector& nums, int target) { int size = nums.size(); + // 辅助哈希表,空间复杂度 O(n) unordered_map dic; + // 单层循环,时间复杂度 O(n) for (int i = 0; i < size; i++) { if (dic.find(target - nums[i]) != dic.end()) { return { dic[target - nums[i]], i }; @@ -45,9 +48,13 @@ int main() { // 方法一 SolutionBruteForce* slt1 = new SolutionBruteForce(); vector res = slt1->twoSum(nums, target); + cout << "方法一 res = "; PrintUtil::printVector(res); // 方法二 SolutionHashMap* slt2 = new SolutionHashMap(); res = slt2->twoSum(nums, target); + cout << "方法二 res = "; PrintUtil::printVector(res); + + return 0; } diff --git a/codes/cpp/chapter_computational_complexity/space_complexity_types.cpp b/codes/cpp/chapter_computational_complexity/space_complexity_types.cpp index 0a7d765..0eb4d9d 100644 --- a/codes/cpp/chapter_computational_complexity/space_complexity_types.cpp +++ b/codes/cpp/chapter_computational_complexity/space_complexity_types.cpp @@ -6,3 +6,97 @@ #include "../include/include.hpp" +/* 函数 */ +int func() { + // do something + return 0; +} + +/* 常数阶 */ +void constant(int n) { + // 常量、变量、对象占用 O(1) 空间 + const int a = 0; + int b = 0; + vector nums(10000); + ListNode* node = new ListNode(0); + // 循环中的变量占用 O(1) 空间 + for (int i = 0; i < n; i++) { + int c = 0; + } + // 循环中的函数占用 O(1) 空间 + for (int i = 0; i < n; i++) { + func(); + } +} + +/* 线性阶 */ +void linear(int n) { + // 长度为 n 的数组占用 O(n) 空间 + vector nums(n); + // 长度为 n 的列表占用 O(n) 空间 + vector nodes; + for (int i = 0; i < n; i++) { + nodes.push_back(new ListNode(i)); + } + // 长度为 n 的哈希表占用 O(n) 空间 + unordered_map map; + for (int i = 0; i < n; i++) { + map[i] = to_string(i); + } +} + +/* 线性阶(递归实现) */ +void linearRecur(int n) { + cout << "递归 n = " << n << endl; + if (n == 1) return; + linearRecur(n - 1); +} + +/* 平方阶 */ +void quadratic(int n) { + // 二维列表占用 O(n^2) 空间 + vector> numMatrix; + for (int i = 0; i < n; i++) { + vector tmp; + for (int j = 0; j < n; j++) { + tmp.push_back(0); + } + numMatrix.push_back(tmp); + } +} + +/* 平方阶(递归实现) */ +int quadraticRecur(int n) { + if (n <= 0) return 0; + vector nums(n); + cout << "递归 n = " << n << " 中的 nums 长度 = " << nums.size() << endl; + return quadraticRecur(n - 1); +} + +/* 指数阶(建立满二叉树) */ +TreeNode* buildTree(int n) { + if (n == 0) return nullptr; + TreeNode* root = new TreeNode(0); + root->left = buildTree(n - 1); + root->right = buildTree(n - 1); + return root; +} + + +/* Driver Code */ +int main() { + int n = 5; + // 常数阶 + constant(n); + // 线性阶 + linear(n); + linearRecur(n); + // 平方阶 + quadratic(n); + quadraticRecur(n); + // 指数阶 + TreeNode* root = buildTree(n); + PrintUtil::printTree(root); + + return 0; +} diff --git a/codes/cpp/chapter_computational_complexity/time_complexity_types.cpp b/codes/cpp/chapter_computational_complexity/time_complexity_types.cpp index dd4d4fa..5672b04 100644 --- a/codes/cpp/chapter_computational_complexity/time_complexity_types.cpp +++ b/codes/cpp/chapter_computational_complexity/time_complexity_types.cpp @@ -6,3 +6,161 @@ #include "../include/include.hpp" +/* 常数阶 */ +int constant(int n) { + int count = 0; + int size = 100000; + for (int i = 0; i < size; i++) + count++; + return count; +} + +/* 线性阶 */ +int linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) + count++; + return count; +} + +/* 线性阶(遍历数组) */ +int arrayTraversal(vector& nums) { + int count = 0; + // 循环次数与数组长度成正比 + for (int num : nums) { + count++; + } + return count; +} + +/* 平方阶 */ +int quadratic(int n) { + int count = 0; + // 循环次数与数组长度成平方关系 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; +} + +/* 平方阶(冒泡排序) */ +int bubbleSort(vector& nums) { + int count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = nums.size() - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; +} + +/* 指数阶(循环实现) */ +int exponential(int n) { + int count = 0, base = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (int i = 0; i < n; i++) { + for (int j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; +} + +/* 指数阶(递归实现) */ +int expRecur(int n) { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; +} + +/* 对数阶(循环实现) */ +int logarithmic(float n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; +} + +/* 对数阶(递归实现) */ +int logRecur(float n) { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; +} + +/* 线性对数阶 */ +int linearLogRecur(float n) { + if (n <= 1) return 1; + int count = linearLogRecur(n / 2) + + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) { + count++; + } + return count; +} + +/* 阶乘阶(递归实现) */ +int factorialRecur(int n) { + if (n == 0) return 1; + int count = 0; + // 从 1 个分裂出 n 个 + for (int i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; +} + + +/* Driver Code */ +int main() { + // 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 + int n = 8; + cout << "输入数据大小 n = " << n << endl; + + int count = constant(n); + cout << "常数阶的计算操作数量 = " << count << endl; + + count = linear(n); + cout << "线性阶的计算操作数量 = " << count << endl; + vector arr(n); + count = arrayTraversal(arr); + cout << "线性阶(遍历数组)的计算操作数量 = " << count << endl; + + count = quadratic(n); + cout << "平方阶的计算操作数量 = " << count << endl; + vector nums(n); + for (int i = 0; i < n; i++) + nums[i] = n - i; // [n,n-1,...,2,1] + count = bubbleSort(nums); + cout << "平方阶(冒泡排序)的计算操作数量 = " << count << endl; + + count = exponential(n); + cout << "指数阶(循环实现)的计算操作数量 = " << count << endl; + count = expRecur(n); + cout << "指数阶(递归实现)的计算操作数量 = " << count << endl; + + count = logarithmic((float) n); + cout << "对数阶(循环实现)的计算操作数量 = " << count << endl; + count = logRecur((float) n); + cout << "对数阶(递归实现)的计算操作数量 = " << count << endl; + + count = linearLogRecur((float) n); + cout << "线性对数阶(递归实现)的计算操作数量 = " << count << endl; + + count = factorialRecur(n); + cout << "阶乘阶(递归实现)的计算操作数量 = " << count << endl; + + return 0; +} diff --git a/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp b/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp index e6e7d8e..9b215cf 100644 --- a/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp +++ b/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp @@ -6,3 +6,39 @@ #include "../include/include.hpp" +/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ +vector randomNumbers(int n) { + vector nums(n); + // 生成数组 nums = { 1, 2, 3, ..., n } + for (int i = 0; i < n; i++) { + nums[i] = i + 1; + } + // 使用系统时间生成随机种子 + unsigned seed = chrono::system_clock::now().time_since_epoch().count(); + // 随机打乱数组元素 + shuffle(nums.begin(), nums.end(), default_random_engine(seed)); + return nums; +} + +/* 查找数组 nums 中数字 1 所在索引 */ +int findOne(vector& nums) { + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == 1) + return i; + } + return -1; +} + + +/* Driver Code */ +int main() { + for (int i = 0; i < 1000; i++) { + int n = 100; + vector nums = randomNumbers(n); + int index = findOne(nums); + cout << "\n数组 [ 1, 2, ..., n ] 被打乱后 = "; + PrintUtil::printVector(nums); + cout << "数字 1 的索引为 " << index << endl; + } + return 0; +} diff --git a/codes/cpp/chapter_searching/binary_search.cpp b/codes/cpp/chapter_searching/binary_search.cpp index 5bd3a94..9729128 100644 --- a/codes/cpp/chapter_searching/binary_search.cpp +++ b/codes/cpp/chapter_searching/binary_search.cpp @@ -6,3 +6,55 @@ #include "../include/include.hpp" +/* 二分查找(双闭区间) */ +int binarySearch(vector& nums, int target) { + // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + int i = 0, j = nums.size() - 1; + // 循环,当搜索区间为空时跳出(当 i > j 时为空) + while (i <= j) { + int m = (i + j) / 2; // 计算中点索引 m + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 + i = m + 1; + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中 + j = m - 1; + else // 找到目标元素,返回其索引 + return m; + } + // 未找到目标元素,返回 -1 + return -1; +} + +/* 二分查找(左闭右开) */ +int binarySearch1(vector& nums, int target) { + // 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + int i = 0, j = nums.size(); + // 循环,当搜索区间为空时跳出(当 i = j 时为空) + while (i < j) { + int m = (i + j) / 2; // 计算中点索引 m + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中 + i = m + 1; + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中 + j = m; + else // 找到目标元素,返回其索引 + return m; + } + // 未找到目标元素,返回 -1 + return -1; +} + + +/* Driver Code */ +int main() { + int target = 6; + vector nums = { 1, 3, 6, 8, 12, 15, 23, 67, 70, 92 }; + + /* 二分查找(双闭区间) */ + int index = binarySearch(nums, target); + cout << "目标元素 6 的索引 = " << index << endl; + + /* 二分查找(左闭右开) */ + index = binarySearch1(nums, target); + cout << "目标元素 6 的索引 = " << index << endl; + + return 0; +} diff --git a/codes/cpp/chapter_searching/hashing_search.cpp b/codes/cpp/chapter_searching/hashing_search.cpp index df2b136..ee37a18 100644 --- a/codes/cpp/chapter_searching/hashing_search.cpp +++ b/codes/cpp/chapter_searching/hashing_search.cpp @@ -6,3 +6,49 @@ #include "../include/include.hpp" +/* 哈希查找(数组) */ +int hashingSearch(unordered_map map, int target) { + // 哈希表的 key: 目标元素,value: 索引 + // 若哈希表中无此 key ,返回 -1 + if (map.find(target) == map.end()) + return -1; + return map[target]; +} + +/* 哈希查找(链表) */ +ListNode* hashingSearch1(unordered_map map, int target) { + // 哈希表的 key: 目标结点值,value: 结点对象 + // 若哈希表中无此 key ,返回 nullptr + if (map.find(target) == map.end()) + return nullptr; + return map[target]; +} + + +/* Driver Code */ +int main() { + int target = 3; + + /* 哈希查找(数组) */ + vector nums = { 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }; + // 初始化哈希表 + unordered_map map; + for (int i = 0; i < nums.size(); i++) { + map[nums[i]] = i; // key: 元素,value: 索引 + } + int index = hashingSearch(map, target); + cout << "目标元素 3 的索引 = " << index << endl; + + /* 哈希查找(链表) */ + ListNode* head = vectorToLinkedList(nums); + // 初始化哈希表 + unordered_map map1; + while (head != nullptr) { + map1[head->val] = head; // key: 结点值,value: 结点 + head = head->next; + } + ListNode* node = hashingSearch1(map1, target); + cout << "目标结点值 3 的对应结点对象为 " << node << endl; + + return 0; +} diff --git a/codes/cpp/chapter_searching/linear_search.cpp b/codes/cpp/chapter_searching/linear_search.cpp index 1243d90..c2de816 100644 --- a/codes/cpp/chapter_searching/linear_search.cpp +++ b/codes/cpp/chapter_searching/linear_search.cpp @@ -6,3 +6,45 @@ #include "../include/include.hpp" +/* 线性查找(数组) */ +int linearSearch(vector& nums, int target) { + // 遍历数组 + for (int i = 0; i < nums.size(); i++) { + // 找到目标元素,返回其索引 + if (nums[i] == target) + return i; + } + // 未找到目标元素,返回 -1 + return -1; +} + +/* 线性查找(链表) */ +ListNode* linearSearch(ListNode* head, int target) { + // 遍历链表 + while (head != nullptr) { + // 找到目标结点,返回之 + if (head->val == target) + return head; + head = head->next; + } + // 未找到目标结点,返回 nullptr + return nullptr; +} + + +/* Driver Code */ +int main() { + int target = 3; + + /* 在数组中执行线性查找 */ + vector nums = { 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }; + int index = linearSearch(nums, target); + cout << "目标元素 3 的索引 = " << index << endl; + + /* 在链表中执行线性查找 */ + ListNode* head = vectorToLinkedList(nums); + ListNode* node = linearSearch(head, target); + cout << "目标结点值 3 的对应结点对象为 " << node << endl; + + return 0; +} diff --git a/codes/cpp/chapter_sorting/bubble_sort.cpp b/codes/cpp/chapter_sorting/bubble_sort.cpp index 9949dba..0718274 100644 --- a/codes/cpp/chapter_sorting/bubble_sort.cpp +++ b/codes/cpp/chapter_sorting/bubble_sort.cpp @@ -6,3 +6,53 @@ #include "../include/include.hpp" +/* 冒泡排序 */ +void bubbleSort(vector& nums) { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = nums.size() - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + } + } + } +} + +/* 冒泡排序(标志优化)*/ +void bubbleSortWithFlag(vector& nums) { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = nums.size() - 1; i > 0; i--) { + bool flag = false; // 初始化标志位 + // 内循环:冒泡操作 + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + flag = true; // 记录交换元素 + } + } + if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + } +} + + +/* Driver Code */ +int main() { + vector nums = { 4, 1, 3, 1, 5, 2 }; + bubbleSort(nums); + cout << "冒泡排序完成后 nums = "; + PrintUtil::printVector(nums); + + vector nums1 = { 4, 1, 3, 1, 5, 2 }; + bubbleSortWithFlag(nums1); + cout << "冒泡排序完成后 nums1 = "; + PrintUtil::printVector(nums1); + + return 0; +} diff --git a/codes/cpp/chapter_sorting/insertion_sort.cpp b/codes/cpp/chapter_sorting/insertion_sort.cpp index f3287ba..9ae5988 100644 --- a/codes/cpp/chapter_sorting/insertion_sort.cpp +++ b/codes/cpp/chapter_sorting/insertion_sort.cpp @@ -6,3 +6,27 @@ #include "../include/include.hpp" +/* 插入排序 */ +void insertionSort(vector& nums) { + // 外循环:base = nums[1], nums[2], ..., nums[n-1] + for (int i = 1; i < nums.size(); i++) { + int base = nums[i], j = i - 1; + // 内循环:将 base 插入到左边的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // 1. 将 nums[j] 向右移动一位 + j--; + } + nums[j + 1] = base; // 2. 将 base 赋值到正确位置 + } +} + + +/* Driver Code */ +int main() { + vector nums = { 4, 1, 3, 1, 5, 2 }; + insertionSort(nums); + cout << "插入排序完成后 nums = "; + PrintUtil::printVector(nums); + + return 0; +} diff --git a/codes/cpp/chapter_sorting/merge_sort.cpp b/codes/cpp/chapter_sorting/merge_sort.cpp index 0d4285d..465d0de 100644 --- a/codes/cpp/chapter_sorting/merge_sort.cpp +++ b/codes/cpp/chapter_sorting/merge_sort.cpp @@ -6,3 +6,54 @@ #include "../include/include.hpp" +/** + * 合并左子数组和右子数组 + * 左子数组区间 [left, mid] + * 右子数组区间 [mid + 1, right] + */ +void merge(vector& nums, int left, int mid, int right) { + // 初始化辅助数组 + vector tmp(nums.begin() + left, nums.begin() + right + 1); + // 左子数组的起始索引和结束索引 + int leftStart = left - left, leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + int rightStart = mid + 1 - left, rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + int i = leftStart, j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (int k = left; k <= right; k++) { + // 若 “左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) + nums[k] = tmp[j++]; + // 否则,若 “右子数组已全部合并完” 或 “左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + else if (j > rightEnd || tmp[i] <= tmp[j]) + nums[k] = tmp[i++]; + // 否则,若 “左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else + nums[k] = tmp[j++]; + } +} + +/* 归并排序 */ +void mergeSort(vector& nums, int left, int right) { + // 终止条件 + if (left >= right) return; // 当子数组长度为 1 时终止递归 + // 划分阶段 + int mid = (left + right) / 2; // 计算中点 + mergeSort(nums, left, mid); // 递归左子数组 + mergeSort(nums, mid + 1, right); // 递归右子数组 + // 合并阶段 + merge(nums, left, mid, right); +} + + +/* Driver Code */ +int main() { + /* 归并排序 */ + vector nums = { 7, 3, 2, 6, 0, 1, 5, 4 }; + mergeSort(nums, 0, nums.size() - 1); + cout << "归并排序完成后 nums = "; + PrintUtil::printVector(nums); + + return 0; +} diff --git a/codes/cpp/chapter_sorting/quick_sort.cpp b/codes/cpp/chapter_sorting/quick_sort.cpp index 8c40804..4e4430e 100644 --- a/codes/cpp/chapter_sorting/quick_sort.cpp +++ b/codes/cpp/chapter_sorting/quick_sort.cpp @@ -6,3 +6,164 @@ #include "../include/include.hpp" +/* 快速排序类 */ +class QuickSort { +private: + /* 元素交换 */ + static void swap(vector& nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* 哨兵划分 */ + static int partition(vector& nums, int left, int right) { + // 以 nums[left] 作为基准数 + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // 从右向左找首个小于基准数的元素 + while (i < j && nums[i] <= nums[left]) + i++; // 从左向右找首个大于基准数的元素 + swap(nums, i, j); // 交换这两个元素 + } + swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } + +public: + /* 快速排序 */ + static void quickSort(vector& nums, int left, int right) { + // 子数组长度为 1 时终止递归 + if (left >= right) + return; + // 哨兵划分 + int pivot = partition(nums, left, right); + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } +}; + +/* 快速排序类(中位基准数优化) */ +class QuickSortMedian { +private: + /* 元素交换 */ + static void swap(vector& nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* 选取三个元素的中位数 */ + static int medianThree(vector& nums, int left, int mid, int right) { + // 使用了异或操作来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] > nums[mid]) ^ (nums[left] > nums[right])) + return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else + return right; + } + + /* 哨兵划分(三数取中值) */ + static int partition(vector& nums, int left, int right) { + // 选取三个候选元素的中位数 + int med = medianThree(nums, left, (left + right) / 2, right); + // 将中位数交换至数组最左端 + swap(nums, left, med); + // 以 nums[left] 作为基准数 + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // 从右向左找首个小于基准数的元素 + while (i < j && nums[i] <= nums[left]) + i++; // 从左向右找首个大于基准数的元素 + swap(nums, i, j); // 交换这两个元素 + } + swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } + +public: + /* 快速排序 */ + static void quickSort(vector& nums, int left, int right) { + // 子数组长度为 1 时终止递归 + if (left >= right) + return; + // 哨兵划分 + int pivot = partition(nums, left, right); + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } +}; + +/* 快速排序类(尾递归优化) */ +class QuickSortTailCall { +private: + /* 元素交换 */ + static void swap(vector& nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* 哨兵划分 */ + static int partition(vector& nums, int left, int right) { + // 以 nums[left] 作为基准数 + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // 从右向左找首个小于基准数的元素 + while (i < j && nums[i] <= nums[left]) + i++; // 从左向右找首个大于基准数的元素 + swap(nums, i, j); // 交换这两个元素 + } + swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } + +public: + /* 快速排序(尾递归优化) */ + static void quickSort(vector& nums, int left, int right) { + // 子数组长度为 1 时终止 + while (left < right) { + // 哨兵划分操作 + int pivot = partition(nums, left, right); + // 对两个子数组中较短的那个执行快排 + if (pivot - left < right - pivot) { + quickSort(nums, left, pivot - 1); // 递归排序左子数组 + left = pivot + 1; // 剩余待排序区间为 [pivot + 1, right] + } else { + quickSort(nums, pivot + 1, right); // 递归排序右子数组 + right = pivot - 1; // 剩余待排序区间为 [left, pivot - 1] + } + } + } +}; + + +/* Driver Code */ +int main() { + /* 快速排序 */ + vector nums { 2, 4, 1, 0, 3, 5 }; + QuickSort::quickSort(nums, 0, nums.size() - 1); + cout << "快速排序完成后 nums = "; + PrintUtil::printVector(nums); + + /* 快速排序(中位基准数优化) */ + vector nums1 = { 2, 4, 1, 0, 3, 5 }; + QuickSortMedian::quickSort(nums1, 0, nums1.size() - 1); + cout << "快速排序(中位基准数优化)完成后 nums = "; + PrintUtil::printVector(nums); + + /* 快速排序(尾递归优化) */ + vector nums2 = { 2, 4, 1, 0, 3, 5 }; + QuickSortTailCall::quickSort(nums2, 0, nums2.size() - 1); + cout << "快速排序(尾递归优化)完成后 nums = "; + PrintUtil::printVector(nums); + + return 0; +} diff --git a/codes/cpp/include/ListNode.hpp b/codes/cpp/include/ListNode.hpp index 4191649..47beb36 100644 --- a/codes/cpp/include/ListNode.hpp +++ b/codes/cpp/include/ListNode.hpp @@ -25,7 +25,7 @@ struct ListNode { * @param list * @return ListNode* */ -ListNode* vectorToLinkedList(vector list) { +ListNode* vectorToLinkedList(vector& list) { ListNode *dum = new ListNode(0); ListNode *head = dum; for (int val : list) { diff --git a/codes/cpp/include/TreeNode.hpp b/codes/cpp/include/TreeNode.hpp index a8e9d04..7c8f170 100644 --- a/codes/cpp/include/TreeNode.hpp +++ b/codes/cpp/include/TreeNode.hpp @@ -23,7 +23,7 @@ struct TreeNode { * @param list * @return TreeNode* */ -TreeNode* vectorToTree(vector list) { +TreeNode* vectorToTree(vector& list) { TreeNode *root = new TreeNode(list[0]); queue que; que.emplace(root); diff --git a/codes/cpp/include/include.hpp b/codes/cpp/include/include.hpp index 8e84583..66404a1 100644 --- a/codes/cpp/include/include.hpp +++ b/codes/cpp/include/include.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "ListNode.hpp" #include "TreeNode.hpp" diff --git a/codes/java/chapter_computational_complexity/space_complexity_types.java b/codes/java/chapter_computational_complexity/space_complexity_types.java index 0a8e1bd..57c6b84 100644 --- a/codes/java/chapter_computational_complexity/space_complexity_types.java +++ b/codes/java/chapter_computational_complexity/space_complexity_types.java @@ -59,7 +59,7 @@ public class space_complexity_types { /* 平方阶 */ static void quadratic(int n) { // 矩阵占用 O(n^2) 空间 - int numMatrix[][] = new int[n][n]; + int[][] numMatrix = new int[n][n]; // 二维列表占用 O(n^2) 空间 List> numList = new ArrayList<>(); for (int i = 0; i < n; i++) { diff --git a/codes/java/chapter_searching/hashing_search.java b/codes/java/chapter_searching/hashing_search.java index fa58baa..c68025d 100644 --- a/codes/java/chapter_searching/hashing_search.java +++ b/codes/java/chapter_searching/hashing_search.java @@ -20,7 +20,7 @@ public class hashing_search { /* 哈希查找(链表) */ static ListNode hashingSearch1(Map map, int target) { // 哈希表的 key: 目标结点值,value: 结点对象 - // 若哈希表中无此 key ,返回 -1 + // 若哈希表中无此 key ,返回 null return map.getOrDefault(target, null); } diff --git a/codes/java/chapter_searching/linear_search.java b/codes/java/chapter_searching/linear_search.java index 269aa07..2e8e410 100644 --- a/codes/java/chapter_searching/linear_search.java +++ b/codes/java/chapter_searching/linear_search.java @@ -9,7 +9,6 @@ package chapter_searching; import include.*; public class linear_search { - /* 线性查找(数组) */ static int linearSearch(int[] nums, int target) { // 遍历数组 diff --git a/codes/java/chapter_sorting/bubble_sort.java b/codes/java/chapter_sorting/bubble_sort.java index eb30a9a..f006a3a 100644 --- a/codes/java/chapter_sorting/bubble_sort.java +++ b/codes/java/chapter_sorting/bubble_sort.java @@ -47,10 +47,10 @@ public class bubble_sort { public static void main(String[] args) { int[] nums = { 4, 1, 3, 1, 5, 2 }; bubbleSort(nums); - System.out.println("排序后数组 nums = " + Arrays.toString(nums)); + System.out.println("冒泡排序完成后 nums = " + Arrays.toString(nums)); int[] nums1 = { 4, 1, 3, 1, 5, 2 }; bubbleSortWithFlag(nums1); - System.out.println("排序后数组 nums1 = " + Arrays.toString(nums)); + System.out.println("冒泡排序完成后 nums1 = " + Arrays.toString(nums)); } } diff --git a/codes/java/chapter_sorting/insertion_sort.java b/codes/java/chapter_sorting/insertion_sort.java index 511f4bf..9a6a031 100644 --- a/codes/java/chapter_sorting/insertion_sort.java +++ b/codes/java/chapter_sorting/insertion_sort.java @@ -26,6 +26,6 @@ public class insertion_sort { public static void main(String[] args) { int[] nums = { 4, 1, 3, 1, 5, 2 }; insertionSort(nums); - System.out.println("排序后数组 nums = " + Arrays.toString(nums)); + System.out.println("插入排序完成后 nums = " + Arrays.toString(nums)); } } diff --git a/codes/java/chapter_sorting/merge_sort.java b/codes/java/chapter_sorting/merge_sort.java index a948ebd..acaa60b 100644 --- a/codes/java/chapter_sorting/merge_sort.java +++ b/codes/java/chapter_sorting/merge_sort.java @@ -51,7 +51,7 @@ public class merge_sort { public static void main(String[] args) { /* 归并排序 */ - int[] nums = { 2, 4, 1, 0, 3, 5 }; + int[] nums = { 7, 3, 2, 6, 0, 1, 5, 4 }; mergeSort(nums, 0, nums.length - 1); System.out.println("归并排序完成后 nums = " + Arrays.toString(nums)); } diff --git a/codes/python/chapter_computational_complexity/space_complexity_types.py b/codes/python/chapter_computational_complexity/space_complexity_types.py index e8568a2..4cb1f1e 100644 --- a/codes/python/chapter_computational_complexity/space_complexity_types.py +++ b/codes/python/chapter_computational_complexity/space_complexity_types.py @@ -37,7 +37,7 @@ def linear(n): """ 线性阶(递归实现) """ def linearRecur(n): - print("递归 n = ", n) + print("递归 n =", n) if n == 1: return linearRecur(n - 1) @@ -50,7 +50,7 @@ def quadratic(n): def quadratic_recur(n): if n <= 0: return 0 nums = [0] * n - print("递归 n = {} 中的 nums 长度 = {}".format(n, len(nums))) + print("递归 n =", n, "中的 nums 长度 =", len(nums)) return quadratic_recur(n - 1) """ 指数阶(建立满二叉树) """ diff --git a/codes/python/chapter_sorting/merge_sort.py b/codes/python/chapter_sorting/merge_sort.py index e4a1b10..09aa09b 100644 --- a/codes/python/chapter_sorting/merge_sort.py +++ b/codes/python/chapter_sorting/merge_sort.py @@ -52,6 +52,6 @@ def merge_sort(nums, left, right): """ Driver Code """ if __name__ == '__main__': - nums = [4, 1, 3, 1, 5, 2] + nums = [ 7, 3, 2, 6, 0, 1, 5, 4 ] merge_sort(nums, 0, len(nums) - 1) print("归并排序完成后 nums =", nums) diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index c1b3a98..9bd24e1 100644 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -56,7 +56,26 @@ comments: true === "C++" ```cpp title="" + /* 结构体 */ + struct Node { + int val; + Node *next; + Node(int x) : val(x), next(nullptr) {} + }; + /* 函数(或称方法) */ + int func() { + // do something... + return 0; + } + + int algorithm(int n) { // 输入数据 + const int a = 0; // 暂存数据(常量) + int b = 0; // 暂存数据(变量) + Node* node = new Node(0); // 暂存数据(对象) + int c = func(); // 栈帧空间(调用函数) + return a + b + c; // 输出数据 + } ``` === "Python" @@ -74,7 +93,6 @@ comments: true return 0 def algorithm(n): # 输入数据 - a = 0 # 暂存数据(常量) b = 0 # 暂存数据(变量) node = Node(0) # 暂存数据(对象) c = function() # 栈帧空间(调用函数) @@ -104,7 +122,12 @@ comments: true === "C++" ```cpp title="" - + void algorithm(int n) { + int a = 0; // O(1) + vector b(10000); // O(1) + if (n > 10) + vector nums(n); // O(n) + } ``` === "Python" @@ -142,7 +165,21 @@ comments: true === "C++" ```cpp title="" - + int func() { + // do something + return 0; + } + /* 循环 O(1) */ + void loop(int n) { + for (int i = 0; i < n; i++) { + func(); + } + } + /* 递归 O(n) */ + void recur(int n) { + if (n == 1) return; + return recur(n - 1); + } ``` === "Python" @@ -212,7 +249,22 @@ $$ === "C++" ```cpp title="space_complexity_types.cpp" - + /* 常数阶 */ + void constant(int n) { + // 常量、变量、对象占用 O(1) 空间 + const int a = 0; + int b = 0; + vector nums(10000); + ListNode* node = new ListNode(0); + // 循环中的变量占用 O(1) 空间 + for (int i = 0; i < n; i++) { + int c = 0; + } + // 循环中的函数占用 O(1) 空间 + for (int i = 0; i < n; i++) { + func(); + } + } ``` === "Python" @@ -259,7 +311,21 @@ $$ === "C++" ```cpp title="space_complexity_types.cpp" - + /* 线性阶 */ + void linear(int n) { + // 长度为 n 的数组占用 O(n) 空间 + vector nums(n); + // 长度为 n 的列表占用 O(n) 空间 + vector nodes; + for (int i = 0; i < n; i++) { + nodes.push_back(new ListNode(i)); + } + // 长度为 n 的哈希表占用 O(n) 空间 + unordered_map map; + for (int i = 0; i < n; i++) { + map[i] = to_string(i); + } + } ``` === "Python" @@ -291,7 +357,12 @@ $$ === "C++" ```cpp title="space_complexity_types.cpp" - + /* 线性阶(递归实现) */ + void linearRecur(int n) { + cout << "递归 n = " << n << endl; + if (n == 1) return; + linearRecur(n - 1); + } ``` === "Python" @@ -318,7 +389,7 @@ $$ /* 平方阶 */ void quadratic(int n) { // 矩阵占用 O(n^2) 空间 - int numMatrix[][] = new int[n][n]; + int [][]numMatrix = new int[n][n]; // 二维列表占用 O(n^2) 空间 List> numList = new ArrayList<>(); for (int i = 0; i < n; i++) { @@ -334,7 +405,18 @@ $$ === "C++" ```cpp title="space_complexity_types.cpp" - + /* 平方阶 */ + void quadratic(int n) { + // 二维列表占用 O(n^2) 空间 + vector> numMatrix; + for (int i = 0; i < n; i++) { + vector tmp; + for (int j = 0; j < n; j++) { + tmp.push_back(0); + } + numMatrix.push_back(tmp); + } + } ``` === "Python" @@ -363,7 +445,13 @@ $$ === "C++" ```cpp title="space_complexity_types.cpp" - + /* 平方阶(递归实现) */ + int quadraticRecur(int n) { + if (n <= 0) return 0; + vector nums(n); + cout << "递归 n = " << n << " 中的 nums 长度 = " << nums.size() << endl; + return quadraticRecur(n - 1); + } ``` === "Python" @@ -401,7 +489,14 @@ $$ === "C++" ```cpp title="space_complexity_types.cpp" - + /* 指数阶(建立满二叉树) */ + TreeNode* buildTree(int n) { + if (n == 0) return nullptr; + TreeNode* root = new TreeNode(0); + root->left = buildTree(n - 1); + root->right = buildTree(n - 1); + return root; + } ``` === "Python" diff --git a/docs/chapter_computational_complexity/space_time_tradeoff.md b/docs/chapter_computational_complexity/space_time_tradeoff.md index 7bc827c..8e778f8 100644 --- a/docs/chapter_computational_complexity/space_time_tradeoff.md +++ b/docs/chapter_computational_complexity/space_time_tradeoff.md @@ -39,7 +39,20 @@ comments: true === "C++" ```cpp title="leetcode_two_sum.cpp" - + class SolutionBruteForce { + public: + vector twoSum(vector& nums, int target) { + int size = nums.size(); + // 两层循环,时间复杂度 O(n^2) + for (int i = 0; i < size - 1; i++) { + for (int j = i + 1; j < size; j++) { + if (nums[i] + nums[j] == target) + return { i, j }; + } + } + return {}; + } + }; ``` === "Python" @@ -101,7 +114,22 @@ comments: true === "C++" ```cpp title="leetcode_two_sum.cpp" - + class SolutionHashMap { + public: + vector twoSum(vector& nums, int target) { + int size = nums.size(); + // 辅助哈希表,空间复杂度 O(n) + unordered_map dic; + // 单层循环,时间复杂度 O(n) + for (int i = 0; i < size; i++) { + if (dic.find(target - nums[i]) != dic.end()) { + return { dic[target - nums[i]], i }; + } + dic.emplace(nums[i], i); + } + return {}; + } + }; ``` === "Python" diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index a25cabf..ed11975 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -36,7 +36,16 @@ $$ === "C++" ```cpp title="" - + // 在某运行平台下 + void algorithm(int n) { + int a = 2; // 1 ns + a = a + 1; // 1 ns + a = a * 2; // 10 ns + // 循环 n 次 + for (int i = 0; i < n; i++) { // 1 ns ,每轮都要执行 i++ + cout << 0 << endl; // 5 ns + } + } ``` === "Python" @@ -88,7 +97,22 @@ $$ === "C++" ```cpp title="" - + // 算法 A 时间复杂度:常数阶 + void algorithm_A(int n) { + cout << 0 << endl; + } + // 算法 B 时间复杂度:线性阶 + void algorithm_B(int n) { + for (int i = 0; i < n; i++) { + cout << 0 << endl; + } + } + // 算法 C 时间复杂度:常数阶 + void algorithm_C(int n) { + for (int i = 0; i < 1000000; i++) { + cout << 0 << endl; + } + } ``` === "Python" @@ -144,7 +168,15 @@ $$ === "C++" ```cpp title="" - + void algorithm(int n) { + int a = 1; // +1 + a = a + 1; // +1 + a = a * 2; // +1 + // 循环 n 次 + for (int i = 0; i < n; i++) { // +1(每轮都执行 i ++) + cout << 0 << endl; // +1 + } + } ``` === "Python" @@ -233,7 +265,20 @@ $$ === "C++" ```cpp title="" - + void algorithm(int n) { + int a = 1; // +0(技巧 1) + a = a + n; // +0(技巧 1) + // +n(技巧 2) + for (int i = 0; i < 5 * n + 1; i++) { + cout << 0 << endl; + } + // +n*n(技巧 3) + for (int i = 0; i < 2 * n; i++) { + for (int j = 0; j < n + 1; j++) { + cout << 0 << endl; + } + } + } ``` === "Python" @@ -310,7 +355,14 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 常数阶 */ + int constant(int n) { + int count = 0; + int size = 100000; + for (int i = 0; i < size; i++) + count++; + return count; + } ``` === "Python" @@ -344,7 +396,13 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 线性阶 */ + int linear(int n) { + int count = 0; + for (int i = 0; i < n; i++) + count++; + return count; + } ``` === "Python" @@ -381,7 +439,15 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 线性阶(遍历数组) */ + int arrayTraversal(vector& nums) { + int count = 0; + // 循环次数与数组长度成正比 + for (int num : nums) { + count++; + } + return count; + } ``` === "Python" @@ -419,7 +485,17 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 平方阶 */ + int quadratic(int n) { + int count = 0; + // 循环次数与数组长度成平方关系 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + count++; + } + } + return count; + } ``` === "Python" @@ -471,7 +547,24 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 平方阶(冒泡排序) */ + int bubbleSort(vector& nums) { + int count = 0; // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = nums.size() - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + count += 3; // 元素交换包含 3 个单元操作 + } + } + } + return count; + } ``` === "Python" @@ -522,7 +615,19 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 指数阶(循环实现) */ + int exponential(int n) { + int count = 0, base = 1; + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for (int i = 0; i < n; i++) { + for (int j = 0; j < base; j++) { + count++; + } + base *= 2; + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count; + } ``` === "Python" @@ -559,7 +664,11 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 指数阶(递归实现) */ + int expRecur(int n) { + if (n == 1) return 1; + return expRecur(n - 1) + expRecur(n - 1) + 1; + } ``` === "Python" @@ -596,7 +705,15 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 对数阶(循环实现) */ + int logarithmic(float n) { + int count = 0; + while (n > 1) { + n = n / 2; + count++; + } + return count; + } ``` === "Python" @@ -630,7 +747,11 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 对数阶(递归实现) */ + int logRecur(float n) { + if (n <= 1) return 0; + return logRecur(n / 2) + 1; + } ``` === "Python" @@ -666,7 +787,16 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 线性对数阶 */ + int linearLogRecur(float n) { + if (n <= 1) return 1; + int count = linearLogRecur(n / 2) + + linearLogRecur(n / 2); + for (int i = 0; i < n; i++) { + count++; + } + return count; + } ``` === "Python" @@ -714,7 +844,16 @@ $$ === "C++" ```cpp title="time_complexity_types.cpp" - + /* 阶乘阶(递归实现) */ + int factorialRecur(int n) { + if (n == 0) return 1; + int count = 0; + // 从 1 个分裂出 n 个 + for (int i = 0; i < n; i++) { + count += factorialRecur(n - 1); + } + return count; + } ``` === "Python" @@ -789,7 +928,42 @@ $$ === "C++" ```cpp title="worst_best_time_complexity.cpp" - + /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ + vector randomNumbers(int n) { + vector nums(n); + // 生成数组 nums = { 1, 2, 3, ..., n } + for (int i = 0; i < n; i++) { + nums[i] = i + 1; + } + // 使用系统时间生成随机种子 + unsigned seed = chrono::system_clock::now().time_since_epoch().count(); + // 随机打乱数组元素 + shuffle(nums.begin(), nums.end(), default_random_engine(seed)); + return nums; + } + + /* 查找数组 nums 中数字 1 所在索引 */ + int findOne(vector& nums) { + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == 1) + return i; + } + return -1; + } + + + /* Driver Code */ + int main() { + for (int i = 0; i < 1000; i++) { + int n = 100; + vector nums = randomNumbers(n); + int index = findOne(nums); + cout << "\n数组 [ 1, 2, ..., n ] 被打乱后 = "; + PrintUtil::printVector(nums); + cout << "数字 1 的索引为 " << index << endl; + } + return 0; + } ``` === "Python" diff --git a/docs/chapter_searching/binary_search.md b/docs/chapter_searching/binary_search.md index 58b7bcb..80709cd 100644 --- a/docs/chapter_searching/binary_search.md +++ b/docs/chapter_searching/binary_search.md @@ -80,6 +80,28 @@ $$ } ``` +=== "C++" + + ```cpp title="binary_search.cpp" + /* 二分查找(双闭区间) */ + int binarySearch(vector& nums, int target) { + // 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素 + int i = 0, j = nums.size() - 1; + // 循环,当搜索区间为空时跳出(当 i > j 时为空) + while (i <= j) { + int m = (i + j) / 2; // 计算中点索引 m + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中 + i = m + 1; + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中 + j = m - 1; + else // 找到目标元素,返回其索引 + return m; + } + // 未找到目标元素,返回 -1 + return -1; + } + ``` + ### “左闭右开” 实现 当然,我们也可以使用 “左闭右开” 的表示方法,写出相同功能的二分查找代码。 @@ -106,6 +128,28 @@ $$ } ``` +=== "C++" + + ```cpp title="binary_search.cpp" + /* 二分查找(左闭右开) */ + int binarySearch1(vector& nums, int target) { + // 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1 + int i = 0, j = nums.size(); + // 循环,当搜索区间为空时跳出(当 i = j 时为空) + while (i < j) { + int m = (i + j) / 2; // 计算中点索引 m + if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中 + i = m + 1; + else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中 + j = m; + else // 找到目标元素,返回其索引 + return m; + } + // 未找到目标元素,返回 -1 + return -1; + } + ``` + ### 两种表示对比 对比下来,两种表示的代码写法有以下不同点: @@ -148,7 +192,5 @@ int m = i + (j - i) / 2; 但并不意味着所有情况下都应使用二分查找,这是因为: - **二分查找仅适用于有序数据。** 如果输入数据是乱序的,为了使用二分查找而专门执行数据排序,那么是得不偿失的,因为排序算法的时间复杂度一般为 $O(n \log n)$ ,比线性查找和二分查找都更差。再例如,对于频繁插入元素的场景,为了保持数组的有序性,需要将元素插入到特定位置,时间复杂度为 $O(n)$ ,也是非常昂贵的。 - - **二分查找仅适用于数组。** 由于在二分查找中,访问索引是 ”非连续“ 的,因此链表或者基于链表实现的数据结构都无法使用。 - - **在小数据量下,线性查找的性能更好。** 在线性查找中,每轮只需要 1 次判断操作;而在二分查找中,需要 1 次加法、1 次除法、1 ~ 3 次判断操作、1 次加法(减法),共 4 ~ 6 个单元操作;因此,在数据量 $n$ 较小时,线性查找反而比二分查找更快。 diff --git a/docs/chapter_searching/hashing_search.md b/docs/chapter_searching/hashing_search.md index c8371fb..ce5132c 100644 --- a/docs/chapter_searching/hashing_search.md +++ b/docs/chapter_searching/hashing_search.md @@ -4,7 +4,7 @@ comments: true # 哈希查找 -!!! question +!!! question 在数据量很大时,「线性查找」太慢;而「二分查找」要求数据必须是有序的,并且只能在数组中应用。那么是否有方法可以同时避免上述缺点呢?答案是肯定的,此方法被称为「哈希查找」。 @@ -27,6 +27,19 @@ comments: true } ``` +=== "C++" + + ```cpp title="hashing_search.cpp" + /* 哈希查找(数组) */ + int hashingSearch(unordered_map map, int target) { + // 哈希表的 key: 目标元素,value: 索引 + // 若哈希表中无此 key ,返回 -1 + if (map.find(target) == map.end()) + return -1; + return map[target]; + } + ``` + 再比如,如果我们想要给定一个目标结点值 `target` ,获取对应的链表结点对象,那么也可以使用哈希查找实现。 ![hash_search_listnode](hashing_search.assets/hash_search_listnode.png) @@ -37,11 +50,24 @@ comments: true /* 哈希查找(链表) */ ListNode hashingSearch1(Map map, int target) { // 哈希表的 key: 目标结点值,value: 结点对象 - // 若哈希表中无此 key ,返回 -1 + // 若哈希表中无此 key ,返回 null return map.getOrDefault(target, null); } ``` +=== "C++" + + ```cpp title="hashing_search.cpp" + /* 哈希查找(链表) */ + ListNode* hashingSearch1(unordered_map map, int target) { + // 哈希表的 key: 目标结点值,value: 结点对象 + // 若哈希表中无此 key ,返回 nullptr + if (map.find(target) == map.end()) + return nullptr; + return map[target]; + } + ``` + ## 复杂度分析 **时间复杂度:** $O(1)$ ,哈希表的查找操作使用 $O(1)$ 时间。 diff --git a/docs/chapter_searching/linear_search.md b/docs/chapter_searching/linear_search.md index b967de9..5186d19 100644 --- a/docs/chapter_searching/linear_search.md +++ b/docs/chapter_searching/linear_search.md @@ -28,6 +28,22 @@ comments: true } ``` +=== "C++" + + ```cpp title="linear_search.cpp" + /* 线性查找(数组) */ + int linearSearch(vector& nums, int target) { + // 遍历数组 + for (int i = 0; i < nums.size(); i++) { + // 找到目标元素,返回其索引 + if (nums[i] == target) + return i; + } + // 未找到目标元素,返回 -1 + return -1; + } + ``` + 再比如,我们想要在给定一个目标结点值 `target` ,返回此结点对象,也可以在链表中进行线性查找。 === "Java" @@ -47,6 +63,23 @@ comments: true } ``` +=== "C++" + + ```cpp title="linear_search.cpp" + /* 线性查找(链表) */ + ListNode* linearSearch(ListNode* head, int target) { + // 遍历链表 + while (head != nullptr) { + // 找到目标结点,返回之 + if (head->val == target) + return head; + head = head->next; + } + // 未找到目标结点,返回 nullptr + return nullptr; + } + ``` + ## 复杂度分析 **时间复杂度 $O(n)$ :** 其中 $n$ 为数组或链表长度。 diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index c3ac3ba..3f35ea1 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -56,7 +56,7 @@ comments: true === "Java" - ```java + ```java title="bubble_sort.java" /* 冒泡排序 */ void bubbleSort(int[] nums) { // 外循环:待排序元素数量为 n-1, n-2, ..., 1 @@ -73,10 +73,29 @@ comments: true } } ``` +=== "C++" + + ```cpp title="bubble_sort.cpp" + /* 冒泡排序 */ + void bubbleSort(vector& nums) { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = nums.size() - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + } + } + } + } + ``` === "Python" - ```python + ```python title="bubble_sort.py" """ 冒泡排序 """ def bubble_sort(nums): n = len(nums) @@ -109,7 +128,7 @@ comments: true === "Java" - ```java + ```java title="bubble_sort.java" /* 冒泡排序(标志优化)*/ void bubbleSortWithFlag(int[] nums) { // 外循环:待排序元素数量为 n-1, n-2, ..., 1 @@ -130,9 +149,32 @@ comments: true } ``` +=== "C++" + + ```cpp title="bubble_sort.cpp" + /* 冒泡排序(标志优化)*/ + void bubbleSortWithFlag(vector& nums) { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (int i = nums.size() - 1; i > 0; i--) { + bool flag = false; // 初始化标志位 + // 内循环:冒泡操作 + for (int j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + int tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + flag = true; // 记录交换元素 + } + } + if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + } + } + ``` + === "Python" - ```python + ```python title="bubble_sort.py" """ 冒泡排序(标志优化) """ def bubble_sort_with_flag(nums): n = len(nums) diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index 06efb08..c9563fc 100644 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -17,9 +17,7 @@ comments: true ## 算法流程 1. 第 1 轮先选取数组的 **第 2 个元素** 为 `base` ,执行「插入操作」后, **数组前 2 个元素已完成排序**。 - 2. 第 2 轮选取 **第 3 个元素** 为 `base` ,执行「插入操作」后, **数组前 3 个元素已完成排序**。 - 3. 以此类推……最后一轮选取 **数组尾元素** 为 `base` ,执行「插入操作」后 **所有元素已完成排序**。 ![insertion_sort](insertion_sort.assets/insertion_sort.png) @@ -28,7 +26,7 @@ comments: true === "Java" - ```java + ```java title="insertion_sort.java" /* 插入排序 */ void insertionSort(int[] nums) { // 外循环:base = nums[1], nums[2], ..., nums[n-1] @@ -44,9 +42,27 @@ comments: true } ``` +=== "C++" + + ```cpp title="insertion_sort.cpp" + /* 插入排序 */ + void insertionSort(vector& nums) { + // 外循环:base = nums[1], nums[2], ..., nums[n-1] + for (int i = 1; i < nums.size(); i++) { + int base = nums[i], j = i - 1; + // 内循环:将 base 插入到左边的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // 1. 将 nums[j] 向右移动一位 + j--; + } + nums[j + 1] = base; // 2. 将 base 赋值到正确位置 + } + } + ``` + === "Python" - ```python + ```python title="insertion_sort.py" """ 插入排序 """ def insertion_sort(nums): # 外循环:base = nums[1], nums[2], ..., nums[n-1] diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index a8b31c9..00df177 100644 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -103,6 +103,50 @@ comments: true } ``` +=== "C++" + + ```cpp title="merge_sort.cpp" + /** + * 合并左子数组和右子数组 + * 左子数组区间 [left, mid] + * 右子数组区间 [mid + 1, right] + */ + void merge(vector& nums, int left, int mid, int right) { + // 初始化辅助数组 + vector tmp(nums.begin() + left, nums.begin() + right + 1); + // 左子数组的起始索引和结束索引 + int leftStart = left - left, leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + int rightStart = mid + 1 - left, rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + int i = leftStart, j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (int k = left; k <= right; k++) { + // 若 “左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) + nums[k] = tmp[j++]; + // 否则,若 “右子数组已全部合并完” 或 “左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + else if (j > rightEnd || tmp[i] <= tmp[j]) + nums[k] = tmp[i++]; + // 否则,若 “左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + else + nums[k] = tmp[j++]; + } + } + + /* 归并排序 */ + void mergeSort(vector& nums, int left, int right) { + // 终止条件 + if (left >= right) return; // 当子数组长度为 1 时终止递归 + // 划分阶段 + int mid = (left + right) / 2; // 计算中点 + mergeSort(nums, left, mid); // 递归左子数组 + mergeSort(nums, mid + 1, right); // 递归右子数组 + // 合并阶段 + merge(nums, left, mid, right); + } + ``` + === "Python" ```python title="merge_sort.py" diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index d29d80d..c9488f1 100644 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -61,6 +61,32 @@ comments: true } ``` +=== "C++" + + ```cpp title="quick_sort.cpp" + /* 元素交换 */ + void swap(vector& nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* 哨兵划分 */ + int partition(vector& nums, int left, int right) { + // 以 nums[left] 作为基准数 + int i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j--; // 从右向左找首个小于基准数的元素 + while (i < j && nums[i] <= nums[left]) + i++; // 从左向右找首个大于基准数的元素 + swap(nums, i, j); // 交换这两个元素 + } + swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } + ``` + === "Python" ```python title="quick_sort.py" @@ -112,6 +138,22 @@ comments: true } ``` +=== "C++" + + ```cpp title="quick_sort.cpp" + /* 快速排序 */ + static void quickSort(vector& nums, int left, int right) { + // 子数组长度为 1 时终止递归 + if (left >= right) + return; + // 哨兵划分 + int pivot = partition(nums, left, right); + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } + ``` + === "Python" ```python title="quick_sort.py" @@ -183,6 +225,32 @@ comments: true } ``` +=== "C++" + + ```cpp title="quick_sort.cpp" + /* 选取三个元素的中位数 */ + int medianThree(vector& nums, int left, int mid, int right) { + // 使用了异或操作来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] > nums[mid]) ^ (nums[left] > nums[right])) + return left; + else if ((nums[mid] < nums[left]) ^ (nums[mid] < nums[right])) + return mid; + else + return right; + } + + /* 哨兵划分(三数取中值) */ + int partition(vector& nums, int left, int right) { + // 选取三个候选元素的中位数 + int med = medianThree(nums, left, (left + right) / 2, right); + // 将中位数交换至数组最左端 + swap(nums, left, med); + // 以 nums[left] 作为基准数 + // 下同省略... + } + ``` + === "Python" ```python title="quick_sort.py" @@ -233,6 +301,27 @@ comments: true } ``` +=== "C++" + + ```cpp title="quick_sort.cpp" + /* 快速排序(尾递归优化) */ + static void quickSort(vector& nums, int left, int right) { + // 子数组长度为 1 时终止 + while (left < right) { + // 哨兵划分操作 + int pivot = partition(nums, left, right); + // 对两个子数组中较短的那个执行快排 + if (pivot - left < right - pivot) { + quickSort(nums, left, pivot - 1); // 递归排序左子数组 + left = pivot + 1; // 剩余待排序区间为 [pivot + 1, right] + } else { + quickSort(nums, pivot + 1, right); // 递归排序右子数组 + right = pivot - 1; // 剩余待排序区间为 [left, pivot - 1] + } + } + } + ``` + === "Python" ```python title="quick_sort.py"