Add cpp codes for the chapter

computational complexity, sorting, searching.
This commit is contained in:
Yudong Jin 2022-11-27 04:20:30 +08:00
parent 431a0f6caf
commit 19a4ccd86a
32 changed files with 1362 additions and 52 deletions

View File

@ -10,6 +10,7 @@ class SolutionBruteForce {
public: public:
vector<int> twoSum(vector<int>& nums, int target) { vector<int> twoSum(vector<int>& nums, int target) {
int size = nums.size(); int size = nums.size();
// 两层循环,时间复杂度 O(n^2)
for (int i = 0; i < size - 1; i++) { for (int i = 0; i < size - 1; i++) {
for (int j = i + 1; j < size; j++) { for (int j = i + 1; j < size; j++) {
if (nums[i] + nums[j] == target) if (nums[i] + nums[j] == target)
@ -24,7 +25,9 @@ class SolutionHashMap {
public: public:
vector<int> twoSum(vector<int>& nums, int target) { vector<int> twoSum(vector<int>& nums, int target) {
int size = nums.size(); int size = nums.size();
// 辅助哈希表,空间复杂度 O(n)
unordered_map<int, int> dic; unordered_map<int, int> dic;
// 单层循环,时间复杂度 O(n)
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
if (dic.find(target - nums[i]) != dic.end()) { if (dic.find(target - nums[i]) != dic.end()) {
return { dic[target - nums[i]], i }; return { dic[target - nums[i]], i };
@ -45,9 +48,13 @@ int main() {
// 方法一 // 方法一
SolutionBruteForce* slt1 = new SolutionBruteForce(); SolutionBruteForce* slt1 = new SolutionBruteForce();
vector<int> res = slt1->twoSum(nums, target); vector<int> res = slt1->twoSum(nums, target);
cout << "方法一 res = ";
PrintUtil::printVector(res); PrintUtil::printVector(res);
// 方法二 // 方法二
SolutionHashMap* slt2 = new SolutionHashMap(); SolutionHashMap* slt2 = new SolutionHashMap();
res = slt2->twoSum(nums, target); res = slt2->twoSum(nums, target);
cout << "方法二 res = ";
PrintUtil::printVector(res); PrintUtil::printVector(res);
return 0;
} }

View File

@ -6,3 +6,97 @@
#include "../include/include.hpp" #include "../include/include.hpp"
/* 函数 */
int func() {
// do something
return 0;
}
/* 常数阶 */
void constant(int n) {
// 常量、变量、对象占用 O(1) 空间
const int a = 0;
int b = 0;
vector<int> 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<int> nums(n);
// 长度为 n 的列表占用 O(n) 空间
vector<ListNode*> nodes;
for (int i = 0; i < n; i++) {
nodes.push_back(new ListNode(i));
}
// 长度为 n 的哈希表占用 O(n) 空间
unordered_map<int, string> 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<vector<int>> numMatrix;
for (int i = 0; i < n; i++) {
vector<int> 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<int> 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;
}

View File

@ -6,3 +6,161 @@
#include "../include/include.hpp" #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<int>& 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<int>& 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<int> arr(n);
count = arrayTraversal(arr);
cout << "线性阶(遍历数组)的计算操作数量 = " << count << endl;
count = quadratic(n);
cout << "平方阶的计算操作数量 = " << count << endl;
vector<int> 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;
}

View File

@ -6,3 +6,39 @@
#include "../include/include.hpp" #include "../include/include.hpp"
/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */
vector<int> randomNumbers(int n) {
vector<int> 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<int>& 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<int> nums = randomNumbers(n);
int index = findOne(nums);
cout << "\n数组 [ 1, 2, ..., n ] 被打乱后 = ";
PrintUtil::printVector(nums);
cout << "数字 1 的索引为 " << index << endl;
}
return 0;
}

View File

@ -6,3 +6,55 @@
#include "../include/include.hpp" #include "../include/include.hpp"
/* 二分查找(双闭区间) */
int binarySearch(vector<int>& 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<int>& 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<int> 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;
}

View File

@ -6,3 +6,49 @@
#include "../include/include.hpp" #include "../include/include.hpp"
/* 哈希查找(数组) */
int hashingSearch(unordered_map<int, int> map, int target) {
// 哈希表的 key: 目标元素value: 索引
// 若哈希表中无此 key ,返回 -1
if (map.find(target) == map.end())
return -1;
return map[target];
}
/* 哈希查找(链表) */
ListNode* hashingSearch1(unordered_map<int, ListNode*> 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<int> nums = { 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 };
// 初始化哈希表
unordered_map<int, int> 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<int, ListNode*> map1;
while (head != nullptr) {
map1[head->val] = head; // key: 结点值value: 结点
head = head->next;
}
ListNode* node = hashingSearch1(map1, target);
cout << "目标结点值 3 的对应结点对象为 " << node << endl;
return 0;
}

View File

@ -6,3 +6,45 @@
#include "../include/include.hpp" #include "../include/include.hpp"
/* 线性查找(数组) */
int linearSearch(vector<int>& 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<int> 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;
}

View File

@ -6,3 +6,53 @@
#include "../include/include.hpp" #include "../include/include.hpp"
/* 冒泡排序 */
void bubbleSort(vector<int>& 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<int>& 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<int> nums = { 4, 1, 3, 1, 5, 2 };
bubbleSort(nums);
cout << "冒泡排序完成后 nums = ";
PrintUtil::printVector(nums);
vector<int> nums1 = { 4, 1, 3, 1, 5, 2 };
bubbleSortWithFlag(nums1);
cout << "冒泡排序完成后 nums1 = ";
PrintUtil::printVector(nums1);
return 0;
}

View File

@ -6,3 +6,27 @@
#include "../include/include.hpp" #include "../include/include.hpp"
/* 插入排序 */
void insertionSort(vector<int>& 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<int> nums = { 4, 1, 3, 1, 5, 2 };
insertionSort(nums);
cout << "插入排序完成后 nums = ";
PrintUtil::printVector(nums);
return 0;
}

View File

@ -6,3 +6,54 @@
#include "../include/include.hpp" #include "../include/include.hpp"
/**
*
* [left, mid]
* [mid + 1, right]
*/
void merge(vector<int>& nums, int left, int mid, int right) {
// 初始化辅助数组
vector<int> 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<int>& 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<int> nums = { 7, 3, 2, 6, 0, 1, 5, 4 };
mergeSort(nums, 0, nums.size() - 1);
cout << "归并排序完成后 nums = ";
PrintUtil::printVector(nums);
return 0;
}

View File

@ -6,3 +6,164 @@
#include "../include/include.hpp" #include "../include/include.hpp"
/* 快速排序类 */
class QuickSort {
private:
/* 元素交换 */
static void swap(vector<int>& nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
/* 哨兵划分 */
static int partition(vector<int>& 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<int>& 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<int>& nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
/* 选取三个元素的中位数 */
static int medianThree(vector<int>& 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<int>& 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<int>& 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<int>& nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
/* 哨兵划分 */
static int partition(vector<int>& 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<int>& 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<int> nums { 2, 4, 1, 0, 3, 5 };
QuickSort::quickSort(nums, 0, nums.size() - 1);
cout << "快速排序完成后 nums = ";
PrintUtil::printVector(nums);
/* 快速排序(中位基准数优化) */
vector<int> nums1 = { 2, 4, 1, 0, 3, 5 };
QuickSortMedian::quickSort(nums1, 0, nums1.size() - 1);
cout << "快速排序(中位基准数优化)完成后 nums = ";
PrintUtil::printVector(nums);
/* 快速排序(尾递归优化) */
vector<int> nums2 = { 2, 4, 1, 0, 3, 5 };
QuickSortTailCall::quickSort(nums2, 0, nums2.size() - 1);
cout << "快速排序(尾递归优化)完成后 nums = ";
PrintUtil::printVector(nums);
return 0;
}

View File

@ -25,7 +25,7 @@ struct ListNode {
* @param list * @param list
* @return ListNode* * @return ListNode*
*/ */
ListNode* vectorToLinkedList(vector<int> list) { ListNode* vectorToLinkedList(vector<int>& list) {
ListNode *dum = new ListNode(0); ListNode *dum = new ListNode(0);
ListNode *head = dum; ListNode *head = dum;
for (int val : list) { for (int val : list) {

View File

@ -23,7 +23,7 @@ struct TreeNode {
* @param list * @param list
* @return TreeNode* * @return TreeNode*
*/ */
TreeNode* vectorToTree(vector<int> list) { TreeNode* vectorToTree(vector<int>& list) {
TreeNode *root = new TreeNode(list[0]); TreeNode *root = new TreeNode(list[0]);
queue<TreeNode*> que; queue<TreeNode*> que;
que.emplace(root); que.emplace(root);

View File

@ -15,6 +15,7 @@
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <set> #include <set>
#include <random>
#include "ListNode.hpp" #include "ListNode.hpp"
#include "TreeNode.hpp" #include "TreeNode.hpp"

View File

@ -59,7 +59,7 @@ public class space_complexity_types {
/* 平方阶 */ /* 平方阶 */
static void quadratic(int n) { static void quadratic(int n) {
// 矩阵占用 O(n^2) 空间 // 矩阵占用 O(n^2) 空间
int numMatrix[][] = new int[n][n]; int[][] numMatrix = new int[n][n];
// 二维列表占用 O(n^2) 空间 // 二维列表占用 O(n^2) 空间
List<List<Integer>> numList = new ArrayList<>(); List<List<Integer>> numList = new ArrayList<>();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {

View File

@ -20,7 +20,7 @@ public class hashing_search {
/* 哈希查找(链表) */ /* 哈希查找(链表) */
static ListNode hashingSearch1(Map<Integer, ListNode> map, int target) { static ListNode hashingSearch1(Map<Integer, ListNode> map, int target) {
// 哈希表的 key: 目标结点值value: 结点对象 // 哈希表的 key: 目标结点值value: 结点对象
// 若哈希表中无此 key 返回 -1 // 若哈希表中无此 key 返回 null
return map.getOrDefault(target, null); return map.getOrDefault(target, null);
} }

View File

@ -9,7 +9,6 @@ package chapter_searching;
import include.*; import include.*;
public class linear_search { public class linear_search {
/* 线性查找(数组) */ /* 线性查找(数组) */
static int linearSearch(int[] nums, int target) { static int linearSearch(int[] nums, int target) {
// 遍历数组 // 遍历数组

View File

@ -47,10 +47,10 @@ public class bubble_sort {
public static void main(String[] args) { public static void main(String[] args) {
int[] nums = { 4, 1, 3, 1, 5, 2 }; int[] nums = { 4, 1, 3, 1, 5, 2 };
bubbleSort(nums); bubbleSort(nums);
System.out.println("排序后数组 nums = " + Arrays.toString(nums)); System.out.println("冒泡排序完成后 nums = " + Arrays.toString(nums));
int[] nums1 = { 4, 1, 3, 1, 5, 2 }; int[] nums1 = { 4, 1, 3, 1, 5, 2 };
bubbleSortWithFlag(nums1); bubbleSortWithFlag(nums1);
System.out.println("排序后数组 nums1 = " + Arrays.toString(nums)); System.out.println("冒泡排序完成后 nums1 = " + Arrays.toString(nums));
} }
} }

View File

@ -26,6 +26,6 @@ public class insertion_sort {
public static void main(String[] args) { public static void main(String[] args) {
int[] nums = { 4, 1, 3, 1, 5, 2 }; int[] nums = { 4, 1, 3, 1, 5, 2 };
insertionSort(nums); insertionSort(nums);
System.out.println("排序后数组 nums = " + Arrays.toString(nums)); System.out.println("插入排序完成后 nums = " + Arrays.toString(nums));
} }
} }

View File

@ -51,7 +51,7 @@ public class merge_sort {
public static void main(String[] args) { 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); mergeSort(nums, 0, nums.length - 1);
System.out.println("归并排序完成后 nums = " + Arrays.toString(nums)); System.out.println("归并排序完成后 nums = " + Arrays.toString(nums));
} }

View File

@ -37,7 +37,7 @@ def linear(n):
""" 线性阶(递归实现) """ """ 线性阶(递归实现) """
def linearRecur(n): def linearRecur(n):
print("递归 n = ", n) print("递归 n =", n)
if n == 1: return if n == 1: return
linearRecur(n - 1) linearRecur(n - 1)
@ -50,7 +50,7 @@ def quadratic(n):
def quadratic_recur(n): def quadratic_recur(n):
if n <= 0: return 0 if n <= 0: return 0
nums = [0] * n nums = [0] * n
print("递归 n = {} 中的 nums 长度 = {}".format(n, len(nums))) print("递归 n =", n, "中的 nums 长度 =", len(nums))
return quadratic_recur(n - 1) return quadratic_recur(n - 1)
""" 指数阶(建立满二叉树) """ """ 指数阶(建立满二叉树) """

View File

@ -52,6 +52,6 @@ def merge_sort(nums, left, right):
""" Driver Code """ """ Driver Code """
if __name__ == '__main__': 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) merge_sort(nums, 0, len(nums) - 1)
print("归并排序完成后 nums =", nums) print("归并排序完成后 nums =", nums)

View File

@ -56,7 +56,26 @@ comments: true
=== "C++" === "C++"
```cpp title="" ```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" === "Python"
@ -74,7 +93,6 @@ comments: true
return 0 return 0
def algorithm(n): # 输入数据 def algorithm(n): # 输入数据
a = 0 # 暂存数据(常量)
b = 0 # 暂存数据(变量) b = 0 # 暂存数据(变量)
node = Node(0) # 暂存数据(对象) node = Node(0) # 暂存数据(对象)
c = function() # 栈帧空间(调用函数) c = function() # 栈帧空间(调用函数)
@ -104,7 +122,12 @@ comments: true
=== "C++" === "C++"
```cpp title="" ```cpp title=""
void algorithm(int n) {
int a = 0; // O(1)
vector<int> b(10000); // O(1)
if (n > 10)
vector<int> nums(n); // O(n)
}
``` ```
=== "Python" === "Python"
@ -142,7 +165,21 @@ comments: true
=== "C++" === "C++"
```cpp title="" ```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" === "Python"
@ -212,7 +249,22 @@ $$
=== "C++" === "C++"
```cpp title="space_complexity_types.cpp" ```cpp title="space_complexity_types.cpp"
/* 常数阶 */
void constant(int n) {
// 常量、变量、对象占用 O(1) 空间
const int a = 0;
int b = 0;
vector<int> 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" === "Python"
@ -259,7 +311,21 @@ $$
=== "C++" === "C++"
```cpp title="space_complexity_types.cpp" ```cpp title="space_complexity_types.cpp"
/* 线性阶 */
void linear(int n) {
// 长度为 n 的数组占用 O(n) 空间
vector<int> nums(n);
// 长度为 n 的列表占用 O(n) 空间
vector<ListNode*> nodes;
for (int i = 0; i < n; i++) {
nodes.push_back(new ListNode(i));
}
// 长度为 n 的哈希表占用 O(n) 空间
unordered_map<int, string> map;
for (int i = 0; i < n; i++) {
map[i] = to_string(i);
}
}
``` ```
=== "Python" === "Python"
@ -291,7 +357,12 @@ $$
=== "C++" === "C++"
```cpp title="space_complexity_types.cpp" ```cpp title="space_complexity_types.cpp"
/* 线性阶(递归实现) */
void linearRecur(int n) {
cout << "递归 n = " << n << endl;
if (n == 1) return;
linearRecur(n - 1);
}
``` ```
=== "Python" === "Python"
@ -318,7 +389,7 @@ $$
/* 平方阶 */ /* 平方阶 */
void quadratic(int n) { void quadratic(int n) {
// 矩阵占用 O(n^2) 空间 // 矩阵占用 O(n^2) 空间
int numMatrix[][] = new int[n][n]; int [][]numMatrix = new int[n][n];
// 二维列表占用 O(n^2) 空间 // 二维列表占用 O(n^2) 空间
List<List<Integer>> numList = new ArrayList<>(); List<List<Integer>> numList = new ArrayList<>();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
@ -334,7 +405,18 @@ $$
=== "C++" === "C++"
```cpp title="space_complexity_types.cpp" ```cpp title="space_complexity_types.cpp"
/* 平方阶 */
void quadratic(int n) {
// 二维列表占用 O(n^2) 空间
vector<vector<int>> numMatrix;
for (int i = 0; i < n; i++) {
vector<int> tmp;
for (int j = 0; j < n; j++) {
tmp.push_back(0);
}
numMatrix.push_back(tmp);
}
}
``` ```
=== "Python" === "Python"
@ -363,7 +445,13 @@ $$
=== "C++" === "C++"
```cpp title="space_complexity_types.cpp" ```cpp title="space_complexity_types.cpp"
/* 平方阶(递归实现) */
int quadraticRecur(int n) {
if (n <= 0) return 0;
vector<int> nums(n);
cout << "递归 n = " << n << " 中的 nums 长度 = " << nums.size() << endl;
return quadraticRecur(n - 1);
}
``` ```
=== "Python" === "Python"
@ -401,7 +489,14 @@ $$
=== "C++" === "C++"
```cpp title="space_complexity_types.cpp" ```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" === "Python"

View File

@ -39,7 +39,20 @@ comments: true
=== "C++" === "C++"
```cpp title="leetcode_two_sum.cpp" ```cpp title="leetcode_two_sum.cpp"
class SolutionBruteForce {
public:
vector<int> twoSum(vector<int>& 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" === "Python"
@ -101,7 +114,22 @@ comments: true
=== "C++" === "C++"
```cpp title="leetcode_two_sum.cpp" ```cpp title="leetcode_two_sum.cpp"
class SolutionHashMap {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int size = nums.size();
// 辅助哈希表,空间复杂度 O(n)
unordered_map<int, int> 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" === "Python"

View File

@ -36,7 +36,16 @@ $$
=== "C++" === "C++"
```cpp title="" ```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" === "Python"
@ -88,7 +97,22 @@ $$
=== "C++" === "C++"
```cpp title="" ```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" === "Python"
@ -144,7 +168,15 @@ $$
=== "C++" === "C++"
```cpp title="" ```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" === "Python"
@ -233,7 +265,20 @@ $$
=== "C++" === "C++"
```cpp title="" ```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" === "Python"
@ -310,7 +355,14 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```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" === "Python"
@ -344,7 +396,13 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```cpp title="time_complexity_types.cpp"
/* 线性阶 */
int linear(int n) {
int count = 0;
for (int i = 0; i < n; i++)
count++;
return count;
}
``` ```
=== "Python" === "Python"
@ -381,7 +439,15 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```cpp title="time_complexity_types.cpp"
/* 线性阶(遍历数组) */
int arrayTraversal(vector<int>& nums) {
int count = 0;
// 循环次数与数组长度成正比
for (int num : nums) {
count++;
}
return count;
}
``` ```
=== "Python" === "Python"
@ -419,7 +485,17 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```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" === "Python"
@ -471,7 +547,24 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```cpp title="time_complexity_types.cpp"
/* 平方阶(冒泡排序) */
int bubbleSort(vector<int>& 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" === "Python"
@ -522,7 +615,19 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```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" === "Python"
@ -559,7 +664,11 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```cpp title="time_complexity_types.cpp"
/* 指数阶(递归实现) */
int expRecur(int n) {
if (n == 1) return 1;
return expRecur(n - 1) + expRecur(n - 1) + 1;
}
``` ```
=== "Python" === "Python"
@ -596,7 +705,15 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```cpp title="time_complexity_types.cpp"
/* 对数阶(循环实现) */
int logarithmic(float n) {
int count = 0;
while (n > 1) {
n = n / 2;
count++;
}
return count;
}
``` ```
=== "Python" === "Python"
@ -630,7 +747,11 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```cpp title="time_complexity_types.cpp"
/* 对数阶(递归实现) */
int logRecur(float n) {
if (n <= 1) return 0;
return logRecur(n / 2) + 1;
}
``` ```
=== "Python" === "Python"
@ -666,7 +787,16 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```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" === "Python"
@ -714,7 +844,16 @@ $$
=== "C++" === "C++"
```cpp title="time_complexity_types.cpp" ```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" === "Python"
@ -789,7 +928,42 @@ $$
=== "C++" === "C++"
```cpp title="worst_best_time_complexity.cpp" ```cpp title="worst_best_time_complexity.cpp"
/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */
vector<int> randomNumbers(int n) {
vector<int> 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<int>& 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<int> nums = randomNumbers(n);
int index = findOne(nums);
cout << "\n数组 [ 1, 2, ..., n ] 被打乱后 = ";
PrintUtil::printVector(nums);
cout << "数字 1 的索引为 " << index << endl;
}
return 0;
}
``` ```
=== "Python" === "Python"

View File

@ -80,6 +80,28 @@ $$
} }
``` ```
=== "C++"
```cpp title="binary_search.cpp"
/* 二分查找(双闭区间) */
int binarySearch(vector<int>& 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<int>& 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)$ ,也是非常昂贵的。 - **二分查找仅适用于有序数据。** 如果输入数据是乱序的,为了使用二分查找而专门执行数据排序,那么是得不偿失的,因为排序算法的时间复杂度一般为 $O(n \log n)$ ,比线性查找和二分查找都更差。再例如,对于频繁插入元素的场景,为了保持数组的有序性,需要将元素插入到特定位置,时间复杂度为 $O(n)$ ,也是非常昂贵的。
- **二分查找仅适用于数组。** 由于在二分查找中,访问索引是 ”非连续“ 的,因此链表或者基于链表实现的数据结构都无法使用。 - **二分查找仅适用于数组。** 由于在二分查找中,访问索引是 ”非连续“ 的,因此链表或者基于链表实现的数据结构都无法使用。
- **在小数据量下,线性查找的性能更好。** 在线性查找中,每轮只需要 1 次判断操作;而在二分查找中,需要 1 次加法、1 次除法、1 ~ 3 次判断操作、1 次加法(减法),共 4 ~ 6 个单元操作;因此,在数据量 $n$ 较小时,线性查找反而比二分查找更快。 - **在小数据量下,线性查找的性能更好。** 在线性查找中,每轮只需要 1 次判断操作;而在二分查找中,需要 1 次加法、1 次除法、1 ~ 3 次判断操作、1 次加法(减法),共 4 ~ 6 个单元操作;因此,在数据量 $n$ 较小时,线性查找反而比二分查找更快。

View File

@ -27,6 +27,19 @@ comments: true
} }
``` ```
=== "C++"
```cpp title="hashing_search.cpp"
/* 哈希查找(数组) */
int hashingSearch(unordered_map<int, int> map, int target) {
// 哈希表的 key: 目标元素value: 索引
// 若哈希表中无此 key ,返回 -1
if (map.find(target) == map.end())
return -1;
return map[target];
}
```
再比如,如果我们想要给定一个目标结点值 `target` ,获取对应的链表结点对象,那么也可以使用哈希查找实现。 再比如,如果我们想要给定一个目标结点值 `target` ,获取对应的链表结点对象,那么也可以使用哈希查找实现。
![hash_search_listnode](hashing_search.assets/hash_search_listnode.png) ![hash_search_listnode](hashing_search.assets/hash_search_listnode.png)
@ -37,11 +50,24 @@ comments: true
/* 哈希查找(链表) */ /* 哈希查找(链表) */
ListNode hashingSearch1(Map<Integer, ListNode> map, int target) { ListNode hashingSearch1(Map<Integer, ListNode> map, int target) {
// 哈希表的 key: 目标结点值value: 结点对象 // 哈希表的 key: 目标结点值value: 结点对象
// 若哈希表中无此 key ,返回 -1 // 若哈希表中无此 key ,返回 null
return map.getOrDefault(target, null); return map.getOrDefault(target, null);
} }
``` ```
=== "C++"
```cpp title="hashing_search.cpp"
/* 哈希查找(链表) */
ListNode* hashingSearch1(unordered_map<int, ListNode*> map, int target) {
// 哈希表的 key: 目标结点值value: 结点对象
// 若哈希表中无此 key ,返回 nullptr
if (map.find(target) == map.end())
return nullptr;
return map[target];
}
```
## 复杂度分析 ## 复杂度分析
**时间复杂度:** $O(1)$ ,哈希表的查找操作使用 $O(1)$ 时间。 **时间复杂度:** $O(1)$ ,哈希表的查找操作使用 $O(1)$ 时间。

View File

@ -28,6 +28,22 @@ comments: true
} }
``` ```
=== "C++"
```cpp title="linear_search.cpp"
/* 线性查找(数组) */
int linearSearch(vector<int>& nums, int target) {
// 遍历数组
for (int i = 0; i < nums.size(); i++) {
// 找到目标元素,返回其索引
if (nums[i] == target)
return i;
}
// 未找到目标元素,返回 -1
return -1;
}
```
再比如,我们想要在给定一个目标结点值 `target` ,返回此结点对象,也可以在链表中进行线性查找。 再比如,我们想要在给定一个目标结点值 `target` ,返回此结点对象,也可以在链表中进行线性查找。
=== "Java" === "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$ 为数组或链表长度。 **时间复杂度 $O(n)$ ** 其中 $n$ 为数组或链表长度。

View File

@ -56,7 +56,7 @@ comments: true
=== "Java" === "Java"
```java ```java title="bubble_sort.java"
/* 冒泡排序 */ /* 冒泡排序 */
void bubbleSort(int[] nums) { void bubbleSort(int[] nums) {
// 外循环:待排序元素数量为 n-1, n-2, ..., 1 // 外循环:待排序元素数量为 n-1, n-2, ..., 1
@ -73,10 +73,29 @@ comments: true
} }
} }
``` ```
=== "C++"
```cpp title="bubble_sort.cpp"
/* 冒泡排序 */
void bubbleSort(vector<int>& 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 ```python title="bubble_sort.py"
""" 冒泡排序 """ """ 冒泡排序 """
def bubble_sort(nums): def bubble_sort(nums):
n = len(nums) n = len(nums)
@ -109,7 +128,7 @@ comments: true
=== "Java" === "Java"
```java ```java title="bubble_sort.java"
/* 冒泡排序(标志优化)*/ /* 冒泡排序(标志优化)*/
void bubbleSortWithFlag(int[] nums) { void bubbleSortWithFlag(int[] nums) {
// 外循环:待排序元素数量为 n-1, n-2, ..., 1 // 外循环:待排序元素数量为 n-1, n-2, ..., 1
@ -130,9 +149,32 @@ comments: true
} }
``` ```
=== "C++"
```cpp title="bubble_sort.cpp"
/* 冒泡排序(标志优化)*/
void bubbleSortWithFlag(vector<int>& 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 ```python title="bubble_sort.py"
""" 冒泡排序(标志优化) """ """ 冒泡排序(标志优化) """
def bubble_sort_with_flag(nums): def bubble_sort_with_flag(nums):
n = len(nums) n = len(nums)

View File

@ -17,9 +17,7 @@ comments: true
## 算法流程 ## 算法流程
1. 第 1 轮先选取数组的 **第 2 个元素**`base` ,执行「插入操作」后, **数组前 2 个元素已完成排序** 1. 第 1 轮先选取数组的 **第 2 个元素**`base` ,执行「插入操作」后, **数组前 2 个元素已完成排序**
2. 第 2 轮选取 **第 3 个元素**`base` ,执行「插入操作」后, **数组前 3 个元素已完成排序** 2. 第 2 轮选取 **第 3 个元素**`base` ,执行「插入操作」后, **数组前 3 个元素已完成排序**
3. 以此类推……最后一轮选取 **数组尾元素**`base` ,执行「插入操作」后 **所有元素已完成排序** 3. 以此类推……最后一轮选取 **数组尾元素**`base` ,执行「插入操作」后 **所有元素已完成排序**
![insertion_sort](insertion_sort.assets/insertion_sort.png) ![insertion_sort](insertion_sort.assets/insertion_sort.png)
@ -28,7 +26,7 @@ comments: true
=== "Java" === "Java"
```java ```java title="insertion_sort.java"
/* 插入排序 */ /* 插入排序 */
void insertionSort(int[] nums) { void insertionSort(int[] nums) {
// 外循环base = nums[1], nums[2], ..., nums[n-1] // 外循环base = nums[1], nums[2], ..., nums[n-1]
@ -44,9 +42,27 @@ comments: true
} }
``` ```
=== "C++"
```cpp title="insertion_sort.cpp"
/* 插入排序 */
void insertionSort(vector<int>& 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 ```python title="insertion_sort.py"
""" 插入排序 """ """ 插入排序 """
def insertion_sort(nums): def insertion_sort(nums):
# 外循环base = nums[1], nums[2], ..., nums[n-1] # 外循环base = nums[1], nums[2], ..., nums[n-1]

View File

@ -103,6 +103,50 @@ comments: true
} }
``` ```
=== "C++"
```cpp title="merge_sort.cpp"
/**
* 合并左子数组和右子数组
* 左子数组区间 [left, mid]
* 右子数组区间 [mid + 1, right]
*/
void merge(vector<int>& nums, int left, int mid, int right) {
// 初始化辅助数组
vector<int> 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<int>& 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"
```python title="merge_sort.py" ```python title="merge_sort.py"

View File

@ -61,6 +61,32 @@ comments: true
} }
``` ```
=== "C++"
```cpp title="quick_sort.cpp"
/* 元素交换 */
void swap(vector<int>& nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
/* 哨兵划分 */
int partition(vector<int>& 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"
```python title="quick_sort.py" ```python title="quick_sort.py"
@ -112,6 +138,22 @@ comments: true
} }
``` ```
=== "C++"
```cpp title="quick_sort.cpp"
/* 快速排序 */
static void quickSort(vector<int>& 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"
```python title="quick_sort.py" ```python title="quick_sort.py"
@ -183,6 +225,32 @@ comments: true
} }
``` ```
=== "C++"
```cpp title="quick_sort.cpp"
/* 选取三个元素的中位数 */
int medianThree(vector<int>& 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<int>& nums, int left, int right) {
// 选取三个候选元素的中位数
int med = medianThree(nums, left, (left + right) / 2, right);
// 将中位数交换至数组最左端
swap(nums, left, med);
// 以 nums[left] 作为基准数
// 下同省略...
}
```
=== "Python" === "Python"
```python title="quick_sort.py" ```python title="quick_sort.py"
@ -233,6 +301,27 @@ comments: true
} }
``` ```
=== "C++"
```cpp title="quick_sort.cpp"
/* 快速排序(尾递归优化) */
static void quickSort(vector<int>& 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"
```python title="quick_sort.py" ```python title="quick_sort.py"