当前位置: 首页 > news >正文

C++学习:六个月从基础到就业——STL算法(二)排序与变序算法

C++学习:六个月从基础到就业——STL算法(二)排序与变序算法

本文是我C++学习之旅系列的第二十六篇技术文章,也是第二阶段"C++进阶特性"的第四篇,主要介绍C++ STL算法库中的排序和变序算法。查看完整系列目录了解更多内容。

引言

在上一篇文章中,我们介绍了STL算法库的基本概念以及查找类算法。本文作为系列的第二篇,将重点探讨STL算法库中的排序和变序算法,这些算法对于数据处理和分析至关重要。

排序是计算机科学中最基础也是最常用的操作之一,STL提供了多种高效且灵活的排序算法实现。而变序算法则用于改变容器中元素的顺序,如反转、旋转或随机打乱。

本文将详细介绍这些算法的用法、特性和应用场景,同时提供实际的代码示例,帮助你更好地理解和应用这些强大的工具。

排序算法详解

STL提供了多种排序算法,每种算法都有其特定的用途和优势。

std::sort:通用排序

std::sort是STL中最常用的排序算法,它实现了一种高效的快速排序(通常是内省排序,结合了快速排序、堆排序和插入排序的优点)。

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>struct Person {std::string name;int age;Person(std::string n, int a) : name(std::move(n)), age(a) {}
};bool compareAge(const Person& a, const Person& b) {return a.age < b.age;
}int main() {// 基本类型排序std::vector<int> numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};// 升序排序(默认)std::sort(numbers.begin(), numbers.end());std::cout << "升序排序结果: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;// 降序排序std::sort(numbers.begin(), numbers.end(), std::greater<int>());std::cout << "降序排序结果: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;// 自定义对象排序std::vector<Person> people = {{"Alice", 30},{"Bob", 25},{"Charlie", 35},{"David", 28}};// 使用函数指针比较std::sort(people.begin(), people.end(), compareAge);std::cout << "\n按年龄排序的人员列表:" << std::endl;for (const auto& p : people) {std::cout << p.name << ": " << p.age << "岁" << std::endl;}// 使用lambda表达式按名字排序std::sort(people.begin(), people.end(), [](const Person& a, const Person& b) {return a.name < b.name;});std::cout << "\n按名字排序的人员列表:" << std::endl;for (const auto& p : people) {std::cout << p.name << ": " << p.age << "岁" << std::endl;}// 部分排序 - 只排序前半部分std::vector<int> partialNumbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};std::sort(partialNumbers.begin(), partialNumbers.begin() + partialNumbers.size()/2);std::cout << "\n部分排序结果: ";for (int n : partialNumbers) {std::cout << n << " ";}std::cout << std::endl;return 0;
}

输出:

升序排序结果: 1 2 3 4 5 6 7 8 9 
降序排序结果: 9 8 7 6 5 4 3 2 1 按年龄排序的人员列表:
Bob: 25岁
David: 28岁
Alice: 30岁
Charlie: 35岁按名字排序的人员列表:
Alice: 30岁
Bob: 25岁
Charlie: 35岁
David: 28岁部分排序结果: 1 2 5 8 9 3 7 4 6 

std::sort的主要特点:

  1. 要求随机访问迭代器:只能用于支持随机访问迭代器的容器(如vector、array和deque),不能用于list或关联容器
  2. 不稳定排序:不保证相等元素的相对顺序保持不变
  3. 复杂度:平均和最坏情况下都是O(n log n)
  4. 原地排序:不需要额外空间

std::stable_sort:稳定排序

当需要保持相等元素的原始相对顺序时,应该使用std::stable_sort

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>struct Student {std::string name;int score;Student(std::string n, int s) : name(std::move(n)), score(s) {}// 用于输出friend std::ostream& operator<<(std::ostream& os, const Student& s) {return os << s.name << "(" << s.score << ")";}
};int main() {// 创建一些学生成绩std::vector<Student> students = {{"Alice", 85},{"Bob", 92},{"Charlie", 85},  // 注意Charlie和Alice分数相同{"David", 78},{"Eve", 92}  // 注意Eve和Bob分数相同};// 按分数排序(使用不稳定的std::sort)std::vector<Student> studentsUnstable = students;std::sort(studentsUnstable.begin(), studentsUnstable.end(),[](const Student& a, const Student& b) {return a.score > b.score;  // 按分数降序});std::cout << "使用std::sort排序后(不稳定):" << std::endl;for (const auto& s : studentsUnstable) {std::cout << s << " ";}std::cout << std::endl;// 按分数排序(使用稳定的std::stable_sort)std::vector<Student> studentsStable = students;std::stable_sort(studentsStable.begin(), studentsStable.end(),[](const Student& a, const Student& b) {return a.score > b.score;  // 按分数降序});std::cout << "使用std::stable_sort排序后(稳定):" << std::endl;for (const auto& s : studentsStable) {std::cout << s << " ";}std::cout << std::endl;return 0;
}

输出(可能因实现而异,因为sort的不稳定性):

使用std::sort排序后(不稳定):
Bob(92) Eve(92) Alice(85) Charlie(85) David(78) 
使用std::stable_sort排序后(稳定):
Bob(92) Eve(92) Alice(85) Charlie(85) David(78) 

注意:虽然在这个简单的例子中,不稳定排序的输出可能与稳定排序相同,但在实际应用中,当数据集较大且有更多相等元素时,差异会更明显。

std::stable_sort的主要特点:

  1. 稳定性:保证相等元素的相对顺序不变
  2. 复杂度:如果有足够的额外内存,是O(n log n),否则是O(n log^2 n)
  3. 额外空间:通常需要额外的内存空间

std::partial_sort:部分排序

当只需要排序前N个元素时,std::partial_sort更加高效:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>int main() {std::vector<int> numbers = {9, 8, 7, 6, 5, 4, 3, 2, 1};// 只排序前5个元素std::partial_sort(numbers.begin(), numbers.begin() + 5, numbers.end());std::cout << "部分排序后: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;// 复制版本 - 不修改原始数据std::vector<int> original = {9, 8, 7, 6, 5, 4, 3, 2, 1};std::vector<int> partial(5);  // 注意:需要预先分配空间std::partial_sort_copy(original.begin(), original.end(),partial.begin(), partial.end());std::cout << "原始数据: ";for (int n : original) {std::cout << n << " ";}std::cout << std::endl;std::cout << "复制的部分排序: ";for (int n : partial) {std::cout << n << " ";}std::cout << std::endl;// 实际应用:获取前N名std::vector<std::pair<std::string, int>> scores = {{"Alice", 85},{"Bob", 92},{"Charlie", 78},{"David", 95},{"Eve", 88},{"Frank", 70},{"Grace", 82},{"Hannah", 90}};// 获取前3名std::partial_sort(scores.begin(), scores.begin() + 3, scores.end(),[](const auto& a, const auto& b) {return a.second > b.second;  // 按分数降序});std::cout << "\n前三名:" << std::endl;for (int i = 0; i < 3; ++i) {std::cout << i+1 << ". " << scores[i].first << " - " << scores[i].second << "分" << std::endl;}return 0;
}

输出:

部分排序后: 1 2 3 4 5 9 8 7 6 
原始数据: 9 8 7 6 5 4 3 2 1 
复制的部分排序: 1 2 3 4 5 前三名:
1. David - 95分
2. Bob - 92分
3. Hannah - 90分

std::partial_sort的主要特点:

  1. 效率:当n << N(排序的元素数远小于总元素数)时,比完全排序更高效
  2. 复杂度:O(N log n),其中N是总元素数,n是要排序的元素数
  3. 应用场景:常用于获取"前N名"或"最大/小的N个元素"

std::nth_element:定位第N个元素

std::nth_element算法重新排列元素,使得指定位置的元素位于排序后它应该在的位置,且它之前的所有元素都不大于它,之后的所有元素都不小于它。

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>  // for iotaint main() {std::vector<int> numbers(10);std::iota(numbers.begin(), numbers.end(), 1);  // 填充1到10// 随机打乱std::random_shuffle(numbers.begin(), numbers.end());std::cout << "打乱后: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;// 找到中位数位置size_t middle = numbers.size() / 2;std::nth_element(numbers.begin(), numbers.begin() + middle, numbers.end());std::cout << "第" << middle+1 << "个元素(中位数): " << numbers[middle] << std::endl;std::cout << "重排后: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;// 查找第3小的元素std::vector<int> data = {9, 7, 3, 5, 1, 8, 2, 6, 4};std::nth_element(data.begin(), data.begin() + 2, data.end());std::cout << "\n第3小的元素: " << data[2] << std::endl;std::cout << "前3小的元素: ";for (int i = 0; i < 3; ++i) {std::cout << data[i] << " ";}std::cout << std::endl;// 查找第3大的元素std::vector<int> data2 = {9, 7, 3, 5, 1, 8, 2, 6, 4};std::nth_element(data2.begin(), data2.begin() + 2, data2.end(), std::greater<int>());std::cout << "第3大的元素: " << data2[2] << std::endl;std::cout << "前3大的元素: ";for (int i = 0; i < 3; ++i) {std::cout << data2[i] << " ";}std::cout << std::endl;return 0;
}

输出:

打乱后: 8 5 10 1 6 4 2 9 7 3 
第5个元素(中位数): 5
重排后: 1 2 4 3 5 6 7 9 10 8 第3小的元素: 3
前3小的元素: 1 2 3 
第3大的元素: 7
前3大的元素: 9 8 7 

std::nth_element的主要特点:

  1. 快速定位:可以快速找到序列中第n小(或大)的元素
  2. 复杂度:平均情况下是O(N),其中N是元素总数
  3. 应用场景:计算中位数、分位数或选择前N大/小的元素

std::is_sortedstd::is_sorted_until:检查排序状态

这些算法用于检查容器是否已排序:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>int main() {std::vector<int> sorted = {1, 2, 3, 4, 5};std::vector<int> partially = {1, 2, 5, 3, 4};std::vector<int> descending = {5, 4, 3, 2, 1};// 检查是否已排序bool is_sorted1 = std::is_sorted(sorted.begin(), sorted.end());bool is_sorted2 = std::is_sorted(partially.begin(), partially.end());std::cout << "sorted是否已排序: " << (is_sorted1 ? "是" : "否") << std::endl;std::cout << "partially是否已排序: " << (is_sorted2 ? "是" : "否") << std::endl;// 带谓词的排序检查bool is_desc_sorted = std::is_sorted(descending.begin(), descending.end(), std::greater<int>());std::cout << "descending是否已降序排序: " << (is_desc_sorted ? "是" : "否") << std::endl;// 查找第一个不排序的位置auto it = std::is_sorted_until(partially.begin(), partially.end());if (it != partially.end()) {std::cout << "partially中第一个不排序元素的位置: " << (it - partially.begin()) << ",值为: " << *it << std::endl;}// 实际应用 - 仅在未排序时排序std::vector<int> data = {1, 3, 5, 7, 9, 2, 4, 6, 8};std::cout << "\n排序前: ";for (int n : data) {std::cout << n << " ";}std::cout << std::endl;// 查找排序终止位置auto sortedUntil = std::is_sorted_until(data.begin(), data.end());// 仅在未排序时排序if (sortedUntil != data.end()) {std::cout << "数据已排序直到位置: " << (sortedUntil - data.begin()) << std::endl;// 只对后半部分排序,然后合并std::sort(sortedUntil, data.end());std::inplace_merge(data.begin(), sortedUntil, data.end());}std::cout << "排序后: ";for (int n : data) {std::cout << n << " ";}std::cout << std::endl;return 0;
}

输出:

sorted是否已排序: 是
partially是否已排序: 否
descending是否已降序排序: 是
partially中第一个不排序元素的位置: 2,值为: 5排序前: 1 3 5 7 9 2 4 6 8 
数据已排序直到位置: 5
排序后: 1 2 3 4 5 6 7 8 9 

std::is_sortedstd::is_sorted_until对于优化排序操作和诊断排序问题非常有用。

二分查找算法

二分查找算法用于在已排序的范围内快速查找元素。

std::binary_searchstd::lower_boundstd::upper_bound

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>int main() {std::vector<int> sorted = {1, 3, 3, 5, 7, 9, 9, 11, 13};// 二分查找 - 检查元素是否存在bool has3 = std::binary_search(sorted.begin(), sorted.end(), 3);bool has4 = std::binary_search(sorted.begin(), sorted.end(), 4);std::cout << "数组中包含3? " << (has3 ? "是" : "否") << std::endl;std::cout << "数组中包含4? " << (has4 ? "是" : "否") << std::endl;// lower_bound - 找出第一个不小于值的位置auto lower3 = std::lower_bound(sorted.begin(), sorted.end(), 3);auto lower4 = std::lower_bound(sorted.begin(), sorted.end(), 4);std::cout << "第一个不小于3的位置: " << (lower3 - sorted.begin()) << ",值为: " << *lower3 << std::endl;std::cout << "第一个不小于4的位置: " << (lower4 - sorted.begin()) << ",值为: " << *lower4 << std::endl;// upper_bound - 找出第一个大于值的位置auto upper3 = std::upper_bound(sorted.begin(), sorted.end(), 3);auto upper9 = std::upper_bound(sorted.begin(), sorted.end(), 9);std::cout << "第一个大于3的位置: " << (upper3 - sorted.begin()) << ",值为: " << *upper3 << std::endl;std::cout << "第一个大于9的位置: " << (upper9 - sorted.begin()) << ",值为: " << *upper9 << std::endl;// equal_range - 同时获得lower_bound和upper_boundauto range3 = std::equal_range(sorted.begin(), sorted.end(), 3);auto range9 = std::equal_range(sorted.begin(), sorted.end(), 9);std::cout << "\n值为3的范围: [" << (range3.first - sorted.begin()) << ", " << (range3.second - sorted.begin()) << ")" << ",包含 " << (range3.second - range3.first) << " 个元素" << std::endl;std::cout << "值为9的范围: [" << (range9.first - sorted.begin()) << ", " << (range9.second - sorted.begin()) << ")" << ",包含 " << (range9.second - range9.first) << " 个元素" << std::endl;// 实际应用 - 计算特定值的个数int count9 = std::count(sorted.begin(), sorted.end(), 9);int count9_v2 = range9.second - range9.first;  // 使用二分搜索,更高效std::cout << "\n使用count计算9的个数: " << count9 << std::endl;std::cout << "使用equal_range计算9的个数: " << count9_v2 << std::endl;// 在自定义对象中使用二分搜索struct Person {std::string name;int age;bool operator<(const Person& other) const {return age < other.age; // 按年龄排序}};std::vector<Person> people = {{"Alice", 25},{"Bob", 30},{"Charlie", 30},{"David", 35},{"Eve", 40},{"Frank", 40},{"Grace", 45}};// 查找30岁的人Person searchKey{"", 30};auto personRange = std::equal_range(people.begin(), people.end(), searchKey);std::cout << "\n30岁的人:" << std::endl;for (auto it = personRange.first; it != personRange.second; ++it) {std::cout << it->name << " (" << it->age << "岁)" << std::endl;}return 0;
}

输出:

数组中包含3? 是
数组中包含4? 否
第一个不小于3的位置: 1,值为: 3
第一个不小于4的位置: 3,值为: 5
第一个大于3的位置: 3,值为: 5
第一个大于9的位置: 7,值为: 11值为3的范围: [1, 3),包含 2 个元素
值为9的范围: [5, 7),包含 2 个元素使用count计算9的个数: 2
使用equal_range计算9的个数: 230岁的人:
Bob (30岁)
Charlie (30岁)

二分查找算法的主要特点:

  1. 前提条件:只能用于已排序的范围
  2. 复杂度:O(log n),比线性搜索更高效
  3. 应用场景
    • binary_search:检查元素是否存在
    • lower_bound:查找不小于目标值的第一个位置
    • upper_bound:查找大于目标值的第一个位置
    • equal_range:查找等于目标值的范围

变序算法详解

变序算法改变元素的顺序而不改变元素本身的值。

std::reverse:反转元素顺序

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 反转整个序列std::reverse(numbers.begin(), numbers.end());std::cout << "完全反转后: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;// 反转部分序列std::reverse(numbers.begin() + 2, numbers.begin() + 8);std::cout << "部分反转后: ";for (int n : numbers) {std::cout << n << " ";}std::cout << std::endl;// 反转并复制到新容器std::vector<int> original = {1, 2, 3, 4, 5};std::vector<int> reversed(original.size());std::reverse_copy(original.begin(), original.end(), reversed.begin());std::cout << "\n原始容器: ";for (int n : original) {std::cout << n << " ";}std::cout << std::endl;std::cout << "反转复制后的容器: ";for (int n : reversed) {std::cout << n << " ";}std::cout << std::endl;// 反转字符串std::string text = "Hello, World!";std::reverse(text.begin(), text.end());std::cout << "\n反转后的字符串: " << text << std::endl;// 实际应用:单词反转但保持单词顺序std::string sentence = "The quick brown fox jumps over the lazy dog";std::string result;result.reserve(sentence.size());std::istringstream iss(sentence);std::string word;bool first = true;while (iss >> word) {if (!first) result += " ";std::reverse(word.begin(), word.end());result += word;first = false;}std::cout << "原句: " << sentence << std::endl;std::cout << "单词反转后: " << result << std::endl;return 0;
}

输出:

完全反转后: 10 9 8 7 6 5 4 3 2 1 
部分反转后: 10 9 3 4 5 6 7 8 2 1 原始容器: 1 2 3 4 5 
反转复制后的容器: 5 4 3 2 1 反转后的字符串: !dlroW ,olleH原句: The quick brown fox jumps over the lazy dog
单词反转后: ehT kciuq nworb xof spmuj revo eht yzal god

std::reverse的主要特点:

  1. 就地操作:直接在原始范围内改变元素顺序
  2. 复杂度:线性时间O(n)
  3. 常见应用:字符串反转、回文检查、数组反转等

std::rotate:旋转元素

std::rotate将范围内的元素循环移动,使得指定的中间元素成为新的开始。

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>template<typename T>
void printVector(const std::vector<T>& vec, const std::string& name) {std::cout << name << ": ";for (const auto& item : vec) {std::cout << item << " ";}std::cout << std::endl;
}int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 将第4个元素(下标3)旋转到开头auto middle = numbers.begin() + 3;  // 指向元素4std::rotate(numbers.begin(), middle, numbers.end());printVector(numbers, "左旋转后(4到开头)");// 将倒数第4个元素(下标6)旋转到开头std::vector<int> numbers2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};middle = numbers2.end() - 4;  // 指向元素7std::rotate(numbers2.begin(), middle, numbers2.end());printVector(numbers2, "左旋转后(7到开头)");// 将第1个元素(下标0)旋转到结尾(右旋转)std::vector<int> numbers3 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};middle = numbers3.begin() + 1;  // 右旋转一次std::rotate(numbers3.begin(), middle, numbers3.end());printVector(numbers3, "右旋转后(1到结尾)");// 旋转并复制到新容器std::vector<int> original = {1, 2, 3, 4, 5};std::vector<int> rotated(original.size());std::rotate_copy(original.begin(), original.begin() + 2, original.end(), rotated.begin());printVector(original, "原始容器");printVector(rotated, "旋转复制后的容器");// 实际应用:循环移动字符串std::string str = "abcdefg";std::rotate(str.begin(), str.begin() + 2, str.end());  // 左移2个位置std::cout << "\n左移2位后的字符串: " << str << std::endl;// 循环左移k位实现std::string original_str = "abcdefg";int k = 3;  // 移动k位// 确保k在合理范围内k %= original_str.size();std::rotate(original_str.begin(), original_str.begin() + k, original_str.end());std::cout << "左移" << k << "位后的字符串: " << original_str << std::endl;return 0;
}

输出:

左旋转后(4到开头): 4 5 6 7 8 9 10 1 2 3 
左旋转后(7到开头): 7 8 9 10 1 2 3 4 5 6 
右旋转后(1到结尾): 2 3 4 5 6 7 8 9 10 1 原始容器: 1 2 3 4 5 
旋转复制后的容器: 3 4 5 1 2 左移2位后的字符串: cdefgab
左移3位后的字符串: defgabc

std::rotate的主要特点:

  1. 效率:原地旋转,不需要额外空间
  2. 复杂度:对于双向迭代器是线性时间O(n)
  3. 常见应用:字符串/数组旋转、循环移位、排列生成等

std::shufflestd::random_shuffle:随机打乱元素

这些算法用于将元素随机排列。注意:std::random_shuffle在C++17中已弃用,应使用std::shuffle

#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
#include <ctime>
#include <string>int main() {std::vector<int> deck;// 创建一副"扑克牌"for (int i = 1; i <= 13; ++i) {  // 1=A, 11=J, 12=Q, 13=Kfor (int j = 0; j < 4; ++j) {  // 四种花色deck.push_back(i);}}// 使用C++98/03风格的随机洗牌(弃用的方法,仅作为参考)std::vector<int> deck1 = deck;std::srand(std::time(nullptr));  // 设置随机种子std::random_shuffle(deck1.begin(), deck1.end());std::cout << "random_shuffle洗牌后(前10张): ";for (int i = 0; i < 10; ++i) {std::cout << deck1[i] << " ";}std::cout << std::endl;// 使用现代C++风格的随机洗牌std::vector<int> deck2 = deck;// 创建随机数生成器std::random_device rd;std::mt19937 g(rd());  // 梅森旋转算法std::shuffle(deck2.begin(), deck2.end(), g);std::cout << "shuffle洗牌后(前10张): ";for (int i = 0; i < 10; ++i) {std::cout << deck2[i] << " ";}std::cout << std::endl;// 实际应用:生成随机排列std::vector<int> perm(10);std::iota(perm.begin(), perm.end(), 0);  // 填充0到9std::shuffle(perm.begin(), perm.end(), g);std::cout << "\n随机排列: ";for (int n : perm) {std::cout << n << " ";}std::cout << std::endl;// 实际应用:随机选择样本std::vector<std::string> population = {"Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Hannah", "Ian", "Julia"};// 随机打乱并选择前3个std::shuffle(population.begin(), population.end(), g);std::cout << "\n随机选择的3个样本: ";for (int i = 0; i < 3; ++i) {std::cout << population[i] << " ";}std::cout << std::endl;return 0;
}

输出(因为是随机的,所以每次运行结果都会不同):

random_shuffle洗牌后(前10张): 8 7 1 9 6 4 3 5 3 12 
shuffle洗牌后(前10张): 8 13 13 7 5 8 12 10 2 10 随机排列: 6 2 3 9 4 5 0 8 7 1 随机选择的3个样本: Julia Hannah David 

std::shuffle的主要特点:

  1. 均匀分布:使用现代随机数生成器提供更好的随机性
  2. 复杂度:线性时间O(n)
  3. 常见应用:洗牌、随机采样、蒙特卡洛模拟等

std::next_permutationstd::prev_permutation:排列生成

这些算法用于生成元素的所有可能排列:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>void printPermutation(const std::string& perm, int count) {std::cout << count << ": " << perm << std::endl;
}int main() {// 生成字符串的所有排列std::string str = "ABC";int count = 1;std::cout << "所有 " << str << " 的排列:" << std::endl;printPermutation(str, count++);while (std::next_permutation(str.begin(), str.end())) {printPermutation(str, count++);}// 生成数字的排列std::vector<int> nums = {1, 2, 3};std::cout << "\n所有 [1,2,3] 的排列:" << std::endl;// 确保从第一个排列开始std::sort(nums.begin(), nums.end());count = 1;do {std::cout << count++ << ": ";for (int n : nums) {std::cout << n << " ";}std::cout << std::endl;} while (std::next_permutation(nums.begin(), nums.end()));// 生成前一个排列std::string str2 = "CBA";  // 注意:从最大排列开始std::cout << "\n所有 " << str2 << " 的前序排列:" << std::endl;count = 1;printPermutation(str2, count++);while (std::prev_permutation(str2.begin(), str2.end())) {printPermutation(str2, count++);}// 实际应用:解决排列问题std::vector<std::string> people = {"Alice", "Bob", "Charlie"};std::cout << "\n三人座位的所有可能安排:" << std::endl;count = 1;do {std::cout << count++ << ": ";for (const auto& person : people) {std::cout << person << " ";}std::cout << std::endl;} while (std::next_permutation(people.begin(), people.end()));return 0;
}

输出:

所有 ABC 的排列:
1: ABC
2: ACB
3: BAC
4: BCA
5: CAB
6: CBA所有 [1,2,3] 的排列:
1: 1 2 3 
2: 1 3 2 
3: 2 1 3 
4: 2 3 1 
5: 3 1 2 
6: 3 2 1 所有 CBA 的前序排列:
1: CBA
2: CAB
3: BCA
4: BAC
5: ACB
6: ABC三人座位的所有可能安排:
1: Alice Bob Charlie 
2: Alice Charlie Bob 
3: Bob Alice Charlie 
4: Bob Charlie Alice 
5: Charlie Alice Bob 
6: Charlie Bob Alice 

std::next_permutationstd::prev_permutation的主要特点:

  1. 完整性:可以生成所有可能的排列
  2. 字典序:生成的排列是字典序的
  3. 复杂度:单次操作是线性时间O(n),生成所有排列是O(n!)
  4. 常见应用:排列问题、组合优化、穷举搜索等

实际应用案例

让我们通过一个更复杂的例子来说明如何结合使用多种排序和变序算法:

学生成绩管理系统

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <iomanip>
#include <random>
#include <numeric>// 学生信息结构
struct Student {std::string id;std::string name;std::vector<int> scores;  // 多门课程的分数double average;  // 平均分// 计算平均分void calculateAverage() {if (scores.empty()) {average = 0.0;} else {average = std::accumulate(scores.begin(), scores.end(), 0.0) / scores.size();}}
};// 比较函数
bool compareByAverage(const Student& a, const Student& b) {return a.average > b.average;  // 降序排列
}bool compareByName(const Student& a, const Student& b) {return a.name < b.name;  // 按姓名字典序
}bool compareById(const Student& a, const Student& b) {return a.id < b.id;  // 按ID字典序
}// 随机生成学生数据
std::vector<Student> generateRandomStudents(int n) {std::vector<Student> students(n);std::vector<std::string> firstNames = {"John", "Emma", "Michael", "Sophia", "William", "Olivia", "James", "Ava", "Logan", "Isabella"};std::vector<std::string> lastNames = {"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"};std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<> firstNameDist(0, firstNames.size() - 1);std::uniform_int_distribution<> lastNameDist(0, lastNames.size() - 1);std::uniform_int_distribution<> scoreDist(50, 100);for (int i = 0; i < n; ++i) {students[i].id = "S" + std::to_string(10000 + i);students[i].name = firstNames[firstNameDist(gen)] + " " + lastNames[lastNameDist(gen)];// 生成3门课程的分数students[i].scores.resize(3);std::generate(students[i].scores.begin(), students[i].scores.end(), [&]() { return scoreDist(gen); });students[i].calculateAverage();}return students;
}// 打印学生信息
void printStudents(const std::vector<Student>& students, int limit = -1) {std::cout << std::left << std::setw(8) << "ID" << std::setw(20) << "Name" << std::setw(10) << "Average" << "Scores" << std::endl;std::cout << std::string(50, '-') << std::endl;int count = 0;for (const auto& student : students) {if (limit != -1 && count >= limit) break;std::cout << std::left << std::setw(8) << student.id << std::setw(20) << student.name << std::fixed << std::setprecision(2) << std::setw(10) << student.average;for (int score : student.scores) {std::cout << score << " ";}std::cout << std::endl;++count;}
}int main() {// 生成20个随机学生auto students = generateRandomStudents(20);std::cout << "原始学生数据 (前10名):" << std::endl;printStudents(students, 10);// 按平均分排序(降序)std::sort(students.begin(), students.end(), compareByAverage);std::cout << "\n按平均分排序后 (前10名):" << std::endl;printStudents(students, 10);// 查找平均分超过85的学生数量auto it = std::find_if(students.begin(), students.end(), [](const Student& s) { return s.average < 85.0; });int highScoreCount = std::distance(students.begin(), it);std::cout << "\n平均分85分以上的学生数量: " << highScoreCount << std::endl;// 按姓名排序std::sort(students.begin(), students.end(), compareByName);std::cout << "\n按姓名排序后 (前10名):" << std::endl;printStudents(students, 10);// 随机选择5名学生std::random_device rd;std::mt19937 g(rd());std::shuffle(students.begin(), students.end(), g);std::cout << "\n随机选择的5名学生:" << std::endl;printStudents(students, 5);// 二分查找示例// 首先按ID排序std::sort(students.begin(), students.end(), compareById);// 创建一个学生对象用于查找Student searchStudent;searchStudent.id = "S10010";  // 假设我们要找ID为S10010的学生// 使用二分查找auto foundIt = std::lower_bound(students.begin(), students.end(), searchStudent, compareById);if (foundIt != students.end() && foundIt->id == searchStudent.id) {std::cout << "\n找到ID为" << searchStudent.id << "的学生:" << std::endl;std::cout << "姓名: " << foundIt->name << ", 平均分: " << foundIt->average << std::endl;} else {std::cout << "\n未找到ID为" << searchStudent.id << "的学生" << std::endl;}return 0;
}

这个例子展示了如何在一个学生成绩管理系统中结合使用多种排序和变序算法:

  • 使用std::sort按不同条件排序学生信息
  • 使用std::find_if查找满足特定条件的学生
  • 使用std::shuffle随机选择学生
  • 使用std::lower_bound进行二分查找特定ID的学生

总结

本文详细介绍了STL算法库中的排序和变序算法,这些算法为C++程序员提供了强大的数据处理工具。我们探讨了:

  1. 排序算法

    • std::sort:通用快速排序
    • std::stable_sort:稳定排序
    • std::partial_sort:部分排序
    • std::nth_element:定位第N个元素
    • std::is_sortedstd::is_sorted_until:排序状态检查
  2. 二分查找算法

    • std::binary_search:检查元素是否存在
    • std::lower_boundstd::upper_bound:查找边界
    • std::equal_range:查找等值范围
  3. 变序算法

    • std::reverse:反转元素顺序
    • std::rotate:旋转元素
    • std::shuffle:随机打乱元素
    • std::next_permutationstd::prev_permutation:生成排列

通过实例,我们展示了这些算法在实际应用中的用法,如数据排序、查找、随机采样和排列生成等。

掌握这些算法可以帮助你编写更简洁、高效的代码,并且避免重复发明轮子。在实际开发中,合理选择和组合这些算法可以大大提高程序的性能和可维护性。

在下一篇文章中,我们将继续探索STL算法库,重点介绍数值算法和集合算法,如std::accumulatestd::inner_productstd::set_union等。

参考资源

  • C++ Reference - 详细的STL算法文档
  • 《C++标准库》by Nicolai M. Josuttis
  • 《Effective STL》by Scott Meyers
  • 《C++17 STL Cookbook》by Jacek Galowicz

这是我C++学习之旅系列的第二十六篇技术文章。查看完整系列目录了解更多内容。

如有任何问题或建议,欢迎在评论区留言交流!

相关文章:

C++学习:六个月从基础到就业——STL算法(二)排序与变序算法

C学习&#xff1a;六个月从基础到就业——STL算法&#xff08;二&#xff09;排序与变序算法 本文是我C学习之旅系列的第二十六篇技术文章&#xff0c;也是第二阶段"C进阶特性"的第四篇&#xff0c;主要介绍C STL算法库中的排序和变序算法。查看完整系列目录了解更多…...

JVM性能优化之年轻代参数设置

一、引言 在Java应用开发中&#xff0c;性能问题往往是最难预测却又最影响用户体验的关键因素。即便代码逻辑完美&#xff0c;若JVM&#xff08;Java虚拟机&#xff09;配置不当&#xff0c;也可能导致频繁GC停顿、内存泄漏&#xff0c;甚至引发系统崩溃。JVM性能优化并非简单…...

A*迷宫寻路

二、实验内容 以寻路问题为例实现A*算法的求解程序&#xff0c;设计两种不同的估价函数&#xff1a; 1.设置两种地图&#xff1a; 根据题意&#xff0c;用矩阵设置两个地图。 地图1&#xff1a;设置5行5列的迷宫&#xff0c;代码如下&#xff1a; 地图2&#xff1a;设置20行…...

秒出PPT推出更强版本,AI PPT工具进入新纪元!

在现代职场中&#xff0c;PPT是我们沟通和展示信息的重要工具。无论是做产品演示&#xff0c;还是准备工作汇报&#xff0c;一份精美的PPT能大大提升演示效果。然而&#xff0c;传统的PPT制作往往需要消耗大量时间&#xff0c;尤其是在排版、设计和内容调整上。如今&#xff0c…...

electron-updater实现自动更新

electron-updater 是一个专为 Electron 应用设计的自动更新工具&#xff0c;能够帮助开发者轻松实现跨平台的自动更新功能。它支持 Windows、macOS 和 Linux 系统&#xff0c;通过简单的配置即可集成到 Electron 应用中&#xff0c;自动检查应用的最新版本并在后台完成更新。el…...

Ubuntu22学习记录

Ubuntu22学习记录 虚拟机挂载共享文件夹离线安装.net core3.1离线安装mysql离线安装supervisor离线安装nginx开机自启 虚拟机挂载共享文件夹 sudo vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other 挂载路径&#xff1a;/mnt/hgfs/离线安装.net core3.1 离线安装mysql 离线安装…...

【MinerU】:一款将PDF转化为机器可读格式的工具——RAG加强(Docker版本)

目录 创建容器 安装miniconda 安装mineru CPU运行 GPU加速 多卡问题 创建容器 构建Dockerfile文件 开启ssh服务&#xff0c;设置密码为1234等操作 # 使用官方 Ubuntu 24.04 镜像 FROM ubuntu:24.04# 安装基础工具和SSH服务 RUN apt-get update && \apt-get ins…...

leetcode 69和367

69. Sqrt(x) 代码&#xff1a; class Solution { public:int mySqrt(int x) {int left 0;int right x;long long mid 0;int res 0;long long temp 0;while(left < right){mid left ((right - left)>>1);temp mid*mid;if(temp x){res mid;break;}else if(te…...

# 代码随想录算法训练营Day37 | Leetcode300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组

代码随想录算法训练营Day37 | Leetcode300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组 一、最长递增子序列 相关题目&#xff1a;Leetcode300 文档讲解&#xff1a;Leetcode300 视频讲解&#xff1a;Leetcode300 1. Leetcode300.最长递增子序列 给你一个整数数…...

中小企业技术跃迁:云原生后端如何实现高效低成本系统建设

📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:技术变革的“门槛”能否被跨越? 过去十年,云计算与容器化技术飞速发展,互联网巨头纷纷构建自己的云原生基础设施,实现系统模块化、弹性伸缩、自动化运维。然而,中小企业在这股浪潮中…...

系统架构师2025年论文《系统架构风格2》

论软件系统架构风格 摘要: 某市医院预约挂号系统建设推广应用项目是我市卫生健康委员会 2019 年发起的一项医疗卫生行业信息化项目,目的是实现辖区内患者在辖区各公立医疗机构就诊时,可以通过多种线上渠道进行预约挂号。我作为系统架构师参与此项目。本文围绕软件系统架构…...

Java面试实战:电商场景下的Spring Cloud微服务架构与缓存技术剖析

第一轮提问 面试官: 谢飞机&#xff0c;我们先从基础问题开始。请问你知道Spring Boot和Spring Cloud的区别吗&#xff1f; 谢飞机: 当然知道&#xff01;Spring Boot主要用于快速构建独立运行的Spring应用&#xff0c;而Spring Cloud则是在Spring Boot的基础上实现分布式系统…...

快速配置linux远程开发-go语言

1.go安装包安装 2.go env 配置 go env -w GO111MODULEon go env -w GOPROXYxx go env -w GOSUMDBoff go env -w GOPRIVATExx 3.复制linux公钥到gitlab中&#xff0c;用于通过ssh免密拉取gitlab项目 ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 4.设置…...

C++23文本编码革新:迈向更现代的字符处理

文章目录 一、字符集与编码&#xff08;P2314R4&#xff09;二、统一的字符字面量编码&#xff08;P2316R2&#xff09;三、具名通用字符转义&#xff08;P2071R2&#xff09;四、带分隔的转义序列&#xff08;P2290R3&#xff09;五、支持UTF-8作为可移植源文件编码&#xff0…...

CentOS系统中MySQL安装步骤分享

在 CentOS 系统上安装 MySQL&#xff0c;需要依次进行环境检查、软件源配置、安装 MySQL、启动服务等操作。我将按照规范流程&#xff0c;为你详细分享完整且具体的安装步骤。 在 CentOS 系统中安装 MySQL 数据库&#xff0c;能够为各类应用提供高效稳定的数据存储和管理服务。…...

【产品经理从0到1】Axure介绍

01. 上期内容回顾 创建元件库的时候&#xff0c;在添加原件时不知道怎么操作。讲解很耐心&#xff0c;希望课上分解步骤多带着练习下&#xff1b;PC 端的原型&#xff0c;相对于移动端&#xff0c;非常自由&#xff0c;没有任何的设计规范&#xff1b;但是&#xff0c;要求 PC…...

30天通过软考高项-第二天

30天通过软考高项-第二天 任务&#xff1a;项目立项管理、项目整合管理 思维导图阅读 知识点记忆 章节习题练习 知识点练习 手写回忆ITTO 立项管理-背 1. 项目可研的5个方面 基金社运法 技术可行性、经济可行性、社会效益可行性、运行环境可行性、其他&#xff08;法律、政…...

yt-dlp 下载时需要 cookie

下载 b 站 歌曲 yt-dlp -x --proxy http://127.0.0.1:1080 --audio-format mp3 https://www.bilibili.com/video/BV1Zn4y1X75b解决方案&#xff0c;使用 firefox 登录相关网站 yt-dlp -o "downloads/%(title)s.%(ext)s" -f "bestvideo[height<1080]bestaud…...

快速上手GO的net/http包,个人学习笔记

更多个人笔记&#xff1a;&#xff08;仅供参考&#xff0c;非盈利&#xff09; gitee&#xff1a; https://gitee.com/harryhack/it_note github&#xff1a; https://github.com/ZHLOVEYY/IT_note 针对GO中net/http包的学习笔记 基础快速了解 创建简单的GOHTTP服务 func …...

Flask + ajax上传文件(二)--多文件上传

Flask多文件上传完整教程 本教程将详细介绍如何使用Flask实现多文件上传功能,并使用时间戳为上传文件自动命名,避免文件名冲突。 一、环境准备 确保已安装Python和Flask pip install flask项目结构 flask_upload/ ├── app.py ├── upload/ # 上传文…...

sysstat介绍以及交叉编译

文章目录 1. 工具集介绍2. 指令使用参考3. 交叉编译3.1 源码下载3.2 编译步骤 4. 工具验证4.1 将相关工具导入到设备4.2 功能验证 1. 工具集介绍 Sysstat 是一个功能强大的 Linux 系统性能监控工具包&#xff0c;提供实时监控和历史数据分析功能&#xff0c;帮助管理员优化系统…...

常见正则表达式整理与Java使用正则表达式的例子

一、常见正则表达式整理 1. 基础验证类 邮箱地址 ^[a-zA-Z0-9._%-][a-zA-Z0-9.-]\\.[a-zA-Z]{2,}$ &#xff08;匹配如 userexample.com&#xff09;手机号 ^1[3-9]\\\\d{9}$ &#xff08;匹配国内11位手机号&#xff0c;如 13812345678&#xff09;中文字符 ^[\u4e00-\u9fa5…...

UE5 Assimp 自用

记录一下配assimp库到ue中的过程。因为想在ue里面实现一些几何处理(虽然ue好像有相关的geo的代码&#xff09;&#xff0c;遂配置了一下assimp。 1. 编译整理生成自己所需要的文件。cmake编译&#xff0c;下载github 的官方的assimp-master&#xff0c;然后cmake都是默认的就行…...

java—12 kafka

目录 一、消息队列的优缺点 二、常用MQ 1. Kafka 2. RocketMQ 3. RabbitMQ 4. ActiveMQ 5. ZeroMQ 6. MQ选型对比 适用场景——从公司基础建设力量角度出发 适用场景——从业务场景角度出发 四、基本概念和操作 1. kafka常用术语 2. kafka常用指令 3. 单播消息&a…...

VS Code 智能代理模式:重塑开发体验

在编程领域&#xff0c;效率与精准度无疑是开发者们永恒的追求。而如今&#xff0c;VS Code 推出的智能代理模式&#xff08;Agent Mode&#xff09;&#xff0c;正以前所未有的方式&#xff0c;彻底颠覆了传统开发流程&#xff0c;为程序员们带来了一场前所未有的效率革命。本…...

基于深度学习和单目测距的前车防撞及车道偏离预警系统

随着人工智能与计算机视觉技术的飞速发展,高级驾驶辅助系统(ADAS)已成为现代汽车智能化的关键标志。它不仅能有效提升行车安全,还能为自动驾驶时代的全面到来奠定坚实基础。本文深入剖析一套功能完备、基于深度学习模型的 ADAS 系统的架构与核心实现,带您领略智能驾驶背后…...

第二篇:Django配置及ORM操作

第二篇&#xff1a;Django配置及ORM操作 文章目录 第二篇&#xff1a;Django配置及ORM操作一、静态文件配置1、为什么要配置静态文件&#xff1f;2、如何配置静态文件&#xff1f;3、静态文件动态解析4、form表单默认是get请求数据 二、request对象方法初识三、pycharm链接数据…...

亚马逊英国站FBA费用重构:轻小商品迎红利期,跨境卖家如何抢占先机?

一、政策背景&#xff1a;成本优化成平台与卖家共同诉求 2024年4月&#xff0c;亚马逊英国站&#xff08;Amazon.co.uk&#xff09;发布近三年来力度最大的FBA费用调整方案&#xff0c;标志着英国电商市场正式进入精细化成本管理时代。这一决策背后&#xff0c;是多重因素的叠…...

算法时代的“摩西十诫”:AI治理平台重构数字戒律

一、引言 数字时代的狂飙突进中&#xff0c;人工智能&#xff08;AI&#xff09;正以颠覆性的力量重塑人类社会。从医疗诊断到金融决策&#xff0c;从智能制造到舆论传播&#xff0c;AI的触角已延伸至每个角落。 然而&#xff0c;斯坦福大学《2024年人工智能指数报告》揭示的…...

Kafka的ISR机制是什么?如何保证数据一致性?

一、Kafka ISR机制深度解析 1. ISR机制定义 ISR&#xff08;In-Sync Replicas&#xff09;是Kafka保证数据一致性的核心机制&#xff0c;由Leader副本&#xff08;复杂读写&#xff09;和Follower副本(负责备份)组成。当Follower副本的延迟超过replica.lag.time.max.ms&#…...

Flink 消费 Kafka 数据流的最佳实践

一、前言&#xff1a;Kafka 只是开始&#xff0c;消费才是关键 Kafka 提供了优雅的 Topic 管理与消息缓冲机制&#xff0c;但只有当 Flink 能稳定、有序、无数据丢失地消费并处理这些数据流&#xff0c;实时数仓系统才真正发挥作用。 本篇将围绕 Flink 如何“吃好” Kafka 数据…...

UEC++第10天|UEC++获取对象、RTTI是C++

最近在写UEC项目&#xff0c;这里写几个案例里的问题&#xff0c;还在学习阶段 1. 如何获取小鸟对象&#xff1f; void AFlappyBirdGameModeBase::BeginGame() { // 让管道动起来PipeActor->SetMoveSpeed();// 让小鸟开始飞行// 如何获取到小鸟对象APawn* Pawn UGameplayS…...

原生微信小程序,canvas生成凭证,保存到手机

原生微信小程序通过canvas可以将表单( 文本、图片 )转化成图片&#xff0c;同时添加水印&#xff0c;生成凭证&#xff0c;这里只是基本功能实现。可以继续完善和扩展。 <view class"container"><!-- Canvas 组件 --><canvas type"2d" id&…...

如何轻松将 Python 英文版切换至中文界面

Python 是一种广泛使用的编程语言&#xff0c;尤其在数据科学、人工智能和网络开发等领域。在最近的 Python 版本中&#xff0c;用户可以方便地使用多种语言&#xff0c;这也包括将 Python 界面语言从英文切换至中文。这不仅是出于用户体验的考虑&#xff0c;也能帮助初学者更快…...

2025.04.24【3D】3D绘图入门指南

Nifty graph A contribution by Matt Asher. 3D animation A 3D animated scatterplot made with R and rgl. 文章目录 Nifty graph3D animation 2025.04.24【3D】| 3D绘图入门指南什么是3D绘图&#xff1f;为什么使用3D绘图&#xff1f;如何在R中进行3D绘图&#xff1f;安装…...

R-CNN,Fast-R-CNN-Faster-R-CNN个人笔记

注&#xff1a;此博客主要为了方便笔者快速复习&#xff0c;只讲大致框架&#xff0c;只讲推理&#xff0c;不讲训练&#xff0c;因此内容不会很详实。 1.R-CNN R-CNN系列的开山之作。 本文将该框架划分为3个模块&#xff1a; 1.region proposal generator 2.CNN&#xff08…...

【深度学习核心技术解析】从理论到实践的全链路指南

目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比 二、实战演示环境配置要求核心代码实现&#xff08;MNIST分类&#xff09;运行结果验证 三、性能对比测试方法论量化数据对比结果分析 四…...

【QT网络】构建简单Udp回显服务器

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…...

浅谈国产数据库多租户方案:提升云计算与SaaS的资源管理效率

近年来&#xff0c;“数据库多租户”这一概念在技术圈内频频出现&#xff0c;成为云计算和SaaS&#xff08;软件即服务&#xff09;架构中的重要组成部分。多租户架构不仅为企业提供了高效的资源隔离与共享解决方案&#xff0c;还能大幅降低成本&#xff0c;提高系统的可扩展性…...

【霍夫变换】图像处理(OpenCV)-part11

20 霍夫变换 20.1 理解霍夫变换 霍夫变换的基本思想是将图像空间中的几何元素&#xff08;如直线、圆等&#xff09;通过参数方程转换到参数空间中&#xff0c;形成一个参数空间的累加器数组。图像空间中的每个点在参数空间中对应一个曲线或曲面&#xff0c;而几何形状在图像…...

Flink 源码编译

打包命令 打包整个项目 mvn clean package -DskipTests -Drat.skiptrue打包单个模块 mvn clean package -DskipTests -Drat.skiptrue -pl flink-dist如果该模块依赖其他模块&#xff0c;可能需要先将其他模块 install 到本地&#xff0c;如果依赖的模块的源代码有修改&#…...

React19源码阅读之commitRoot

commitRoot入口 在finishConcurrentRender函数&#xff0c;commitRootWhenReady函数&#xff0c;commitRoot函数。 commitRoot流程图 commitRoot函数 commitRoot 函数是 React 渲染流程中用于提交根节点的关键函数。它的主要作用是设置相关的优先级和状态&#xff0c;然后调…...

单 例 模 式

设计模式&#xff08;Design Pattern&#xff09;说白了就是一套方法论&#xff0c;是我们的前辈们不断试错总结出来的。一般意义上的设计模式有23种&#xff0c;分为创建型、结构型、行为型三大类。今天先拿最简单的单例模式开刀吧。 六大原则 在正式进入设计模式的学习之前&…...

如何在 Postman 中,自动获取 Token 并将其赋值到环境变量

在 Postman 中&#xff0c;你可以通过 预请求脚本&#xff08;Pre-request Script&#xff09; 和 测试脚本&#xff08;Tests&#xff09; 实现自动获取 Token 并将其赋值到环境变量&#xff0c;下面是完整的操作步骤&#xff1a; ✅ 一、创建获取 Token 的请求 通常这个请求…...

CentOS 7 基于 Nginx 的 HTML 部署全流程指南

一、Nginx 安装&#xff08;两种主流方式&#xff09; 1. YUM 安装&#xff08;推荐新手&#xff09; # 安装 EPEL 扩展源&#xff08;部分系统需要&#xff09; yum install epel-release -y# 安装 Nginx yum install nginx -y# 启动并设置开机自启 systemctl start nginx s…...

spring-ai使用Document存储至milvus的数据结构

1、 spring:application:name: spring-ai-alibaba-rag-milvus-exampleai:dashscope:api-key: sk-xxxxxxoooooovectorstore:milvus:client:host: xxx.ooo.mmm.nnnport: 19530username: rootpassword: MilvusdatabaseName: defaultcollectionName: vector_store#初始化 collecti…...

Milvus(6):Collection 管理分区、管理别名

1 管理分区 分区是一个 Collection 的子集。每个分区与其父集合共享相同的数据结构&#xff0c;但只包含集合中的一个数据子集。本页将帮助你了解如何管理分区。 1.1 分区概述 创建一个 Collection 时&#xff0c;Milvus 也会在该 Collection 中创建一个名为_default 的分区。…...

关于Safari浏览器在ios<16.3版本不支持正则表达式零宽断言的解决办法

异常原因 今天在升级Dify版本的时候发现低版本的ios手机出现了以下报错&#xff1a; SyntaxError: Invalid regular expression: invalid group specifier nameError: Invalid regular expression: invalid group specifier name Call Stack 46 eval [native code] (0:0) ./n…...

设计模式--建造者模式详解

建造者模式 建造者模式也属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式 定义&#xff1a;将一个复杂对象的构建和它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示&#xff08;假设有不同的建造者实现类&#xff0c;可以产生不同的产品&#xff09…...

玩转Docker | Docker部署LMS轻量级音乐工具

玩转Docker | Docker部署LMS轻量级音乐工具 前言一、LMS介绍LMS简介主要特点二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署LMS服务下载镜像创建容器创建容器检查容器状态检查服务端口安全设置四、访问LMS服务访问LMS首页注册账号五、基本使用上传音乐文…...