c/c++开发,无可避免的模板编程实践(篇六)
一、泛型算法
1.1 泛型算法概述
c++标准库不仅包含数据结构(容器、容器适配器等),还有很多算法。数据结构可以帮助存放特定情况下需要保存的数据,而算法则会将数据结构中存储的数据进行变换。标准库没有给容器添加大量的功能函数,而是选择提供一组算法,这些算法大都不依赖特定的容器类型,是通过泛型编程(模板)实现的,可作用在不同类型的容器和不同类型的元素上。
为了快速了解泛型算法,来看下面一个简要案例,第三参数0是累加初始化值:
vector<int> v{100, 400, 200, 300, 200, 500, 100, 400, 600, 300 };int sum = 0;for (auto &&i : v){sum += i;}cout << "sum = " << sum << endl; //sum = 3100//调用泛型算法计算 cout << "sum = " << accumulate(begin(v), end(v), 0) << endl;//sum = 3100,第三参数0是累加初始化值
两者都能得到同样的结果,例子中循环变量也不是很长,不过其可读性比 accumulate 差很多。标准库泛型算法的思想就是提供丰富的算法(通用为主)供开发者使用,避免耗费大量的时间在重复制造轮子上面。
一方面就是,即便开发者会自己去实现相应标准库中的算法,也要进行大量的测试来确保自己实现的算法是否正确,标准库提供的算法都是经过了严格的测试。所以没有必要做重复的工作,这样也能节省代码审阅者的时间,否则他们还要确定算法实现中是否有Bug。
另一方面是STL算法非常的高效。很多标准库算法提供了多种特化实现,这样足以应对依赖迭代器类型的使用方式。例如,将 vector 中的所有元素都填充0时,就可以使用 std::fill 。因为 vector 使用的是一段连续的内存,对于这种使用连续内存存放数据的结构都可以使用 std::fill 进行填充,这个函数类似于C中的 memset 函数。
标准容器定义了很少的操作。大部分容器主要是支持到元素增删、访问、遍历,获取容器大小等功能。用户若希望对容器元素进行更多其他有用的操作:例如给顺序容器排序,或者查找某个特定的元素,或者查找最大或最小的元素,等等。标准库并没有为每种容器类型都定义实现这些操作的成员函数,而是定义了一组泛型算法:因为它们实现共同的操作,所以称之为“算法”;而“泛型”指的是通过模板支持,它们可以操作在多种容器类型上,即不但可作用于 vector 或 list 这些标准库类型,还可用在内置数组类型、甚至其他类型的序列上。
1.2 泛型算法应用示例
例如,针对排序,标准库提供了 sort算法,为需要排序的各个容器提供服务,对于list 这种不支持到sort泛型算法的,还提供了功能函数sort来实现。而对于关联容器,本身是已排序的。下面针对sort算法在相关容器的一些应用,看看都是如何达成排序目的的。
//创建一个大数组vectorvector<int> v_sort;const int v_size = 10000; //数组大小const int read_size = 20; //输出数量for(int i=0; i<v_size;i++){v_sort.push_back(rand()%v_size); //随机赋值}//构造一个新的vertor,为了公平测试后面容器,调用sort算法排序vectorvector<int> v_sort_new(v_sort.begin(),v_sort.end());cout << "clock()1 = " << clock() <<endl;sort(v_sort_new.begin(),v_sort_new.end());cout << "clock()2 = " << clock() <<endl;//输出已排序的vector前一段数据for(int i=0; i<read_size;i++){cout << v_sort_new[i] << " ";}cout << "\n";//将vector拷贝构造list,调用list的sort函数排序list<int> l_sort(v_sort.begin(),v_sort.end());cout << "clock()3 = " << clock() <<endl;l_sort.sort();cout << "clock()4 = " << clock() <<endl;//输出已排序的list前一段数据list<int>::iterator it_list=l_sort.begin();for (int i=0; i<read_size;i++){/* code */cout << *(it_list++) << " ";}cout << "\n";//将vector拷贝构造multiset,multiset在构造时就自动排序cout << "clock()5 = " << clock() <<endl;multiset<int> set_sort(v_sort.begin(),v_sort.end());cout << "clock()6 = " << clock() <<endl;//输出已排序的multiset前一段数据multiset<int>::iterator it_mset = set_sort.begin();for (int i=0; i<read_size;i++){/* code */cout << *(it_mset++) << " ";}cout << "\n";//将multiset拷贝回vector,multiset在构造时就自动排序,vector同样得到已排序结构cout << "clock()7 = " << clock() <<endl;v_sort.clear();v_sort.assign(set_sort.begin(),set_sort.end());cout << "clock()8 = " << clock() <<endl;//重新输出已排序的vector前一段数据for(int i=0; i<read_size;i++){cout << v_sort[i] << " ";}cout << "\n";
//out log
clock()1 = 3
clock()2 = 10
0 0 2 3 3 3 4 4 6 8 8 9 12 12 12 12 14 16 16 17
clock()3 = 19
clock()4 = 34
0 0 2 3 3 3 4 4 6 8 8 9 12 12 12 12 14 16 16 17
clock()5 = 38
clock()6 = 53
0 0 2 3 3 3 4 4 6 8 8 9 12 12 12 12 14 16 16 17
clock()7 = 62
clock()8 = 65
0 0 2 3 3 3 4 4 6 8 8 9 12 12 12 12 14 16 16 17
上述这段代码,构建了一个大随机数组,然后将数据复制构造到vector、list、mulset看看其排序功能及排序耗时,由于mulset插入数据就已排序,把数据重新拷贝会vector,也能同样获得已排序结构。
而is_sorted 函数会告诉我们,容器内部的值是否已经经过排序:
//创建一个大数组vectorvector<int> v_sort;const int v_size = 10000;const int read_size = 20;for(int i=0; i<v_size;i++){v_sort.push_back(rand()%v_size);}cout << "v_sort is_sorted = "<< is_sorted(v_sort.begin(), v_sort.end()) << endl;...见前代码...cout << "clock()7 = " << clock() <<endl;v_sort.clear();v_sort.assign(set_sort.begin(),set_sort.end());cout << "clock()8 = " << clock() <<endl;...见前代码...cout << "v_sort is_sorted = "<< is_sorted(v_sort.begin(), v_sort.end()) << endl;
//out log
v_sort is_sorted = 0
...省略...
clock()7 = 49
clock()8 = 51
0 0 2 3 3 3 4 4 6 8 8 9 12 12 12 12 14 16 16 17
v_sort is_sorted = 1
shuffle 将打乱 vector 中的内容,前两个参数是容器的首尾迭代器,第三个参数是一个随机数生成器:
random_device rd;
mt19937 g {rd()};
//shuffle(v_sort.begin(),v_sort.end(),g); //g是随机数生成器cout << "v_sort is_sorted = "<< is_sorted(v_sort.begin(), v_sort.end()) << endl; //=0
有时候,并不需要对列表完全进行排序,只需要比它前面的某些值小就可以。所以,让使用 partition 将数值小于20的元素放到前面:
//partition(v_sort.begin(),v_sort.end(), [] (int i) { return i < 20; });//重新输出已排序的vector前一段数据for(int i=0; i<read_size;i++){cout << v_sort[i] << " ";}cout << "\n";
//out log
0 17 16 16 4 12 0 2 9 14 6 12 3 3 3 4 8 8 12 12
或者只想对部分元素排序,可以调用partial_sort 。这个函数对容器的内容在某种程度上的排序。其会将 vector 中最小的N个数,放在容器的前半部分。其余的留在 vector 的后半部分,不进行排序:
//shuffle(v_sort.begin(),v_sort.end(),g);auto middle (next(begin(v_sort), 100));//[0,100)partial_sort(begin(v_sort), middle, end(v_sort));//将小的数放置前面,并排序//重新输出已排序的vector前一段数据for(int i=0; i<read_size;i++){cout << v_sort[i] << " ";}cout << "\n";
//out log
0 0 2 3 3 3 4 4 6 8 8 9 12 12 12 12 14 16 16 17
sort 函数还支持使用者将自行定义的比较函数作为第三个参数进行传入,按实际场景意愿排序,例如上述vector降序排序。
bool compare(int a,int b){ return a>b;}; //降序排序
//shuffle(v_sort.begin(),v_sort.end(),g);sort(v_sort.begin(),v_sort.end(),compare);//重新输出已排序的vector前一段数据for(int i=0; i<read_size;i++){cout << v_sort[i] << " ";}cout << "\n";
//out log
9997 9996 9995 9994 9993 9991 9990 9989 9988 9986 9984 9984 9984 9984 9983 9982 9982 9981 9981 9981
1.3 泛型算法与容器
可以看出,标准库提供的泛型算法一般与容器的类型无关,不依赖于容器类型。但会隐式地依赖元素类型(算法函数的模板参数类型):必须能够对元素做比较等运算。然后,泛型算法用迭代器来实现算法与容器的绑定,例如sort(begin(v),end(v)),通过遍历容器及排序。所有迭代器都支持自增操作符,从一个元素定位下一个元素,并提供解引用操作符访问元素的值,迭代器还支持相等和不等操作符,用于判断两个迭代是否相等。
大多数情况下,每个算法都需要使用(至少)两个迭代器指出该算法操纵的元素范围。第一个迭代器指向第一个元素,而第二个迭代器则指向最后一个元素的下一位置。第二个迭代器所指向的元素[有时被称为超出末端迭代器]本身不是要操作的元素,而被用作终止遍历的哨兵(sentinel)。
使用超出末端迭代器还可以很方便地处理第四个要求,只要以此迭代器为返回值,即可表示没有找到要查找的元素。如果要查找的值未找到,则返回超出末端迭代器;否则,返回的迭代器指向匹配的元素。
第三个要求——元素值的比较,有两种解决方法。默认情况下,sort操作要元素类型定义了比较<、>等操作符,算法使用这个操作符比较元素。如果元素类型不支持这些操作符,或者打算用不同的测试方法来比较元素,则可使用一个额外的参数:实现元素比较的函数名字。
使用泛型算法必须包含 algorithm 头文件:
#include <algorithm>,
标准库还定义了一组泛化的算术算法(generalized numeric algorithm),其命名习惯与泛型算法相同。使用这些算法则必须包含 numeric 头文件:
#include <numeric>,
除了少数例外情况,所有算法都在一段范围内的元素上操作,我们将这段范围称为“输出范围(input range)”。带有输入范围参数的算法总是使用头两个形参标记该范围。
算法对算法对输入范围的操作是类似的,但在该范围内如何操纵元素却有所不同:有些操作只读取数据(accumulat、find...),有些操作会修改数据(replace、fill...),有些操作会移动数据(sort、stable_sort 、move...)。
标准库算法与容器关系要点:
1)泛型算法本身从不执行容器操作,只是单独依赖迭代器和迭代器操作实现,因而其实现与容器类型无关。实际的容器类型未知(甚至所处理的元素是否存储在容器中也是未知的)。
//算法与容器类型无关
vector<int>::const_iterator result_vec = find(v_sort.begin(),v_sort.end(),99);//vector查找
list<int>::const_iterator result_list = find(l_sort.begin(),l_sort.end(),99); //list查找
multiset<int>::iterator result_mset = find(set_sort.begin(),set_sort.end(),99);//multiset 查找
int vg[12] = {71,73,29,34,40,23,84,99,21,78,18,58};
int *result_vg = find(vg,vg+12,99); //数组查找
这个事实也许比较意外,但本质上暗示了:使用“普通”的迭代器时,算法从不修改基础容器的大小。正如我们所看到的,算法也许会改变存储在容器中的元素的值,也许会在容器内移动元素,但是,算法从不直接添加或删除元素。
//算法修改容器值
fill(v_sort.begin(),v_sort.begin()+2, 0); //前两个值设为0
fill(vg,vg+2, 0); //前两个值设为0
//算法会移动容器内的数据位置
sort(vg,vg+12,compare);
2)泛型算法都是在标记容器(或其他序列)内的元素范围的迭代器上操作的。标记范围的两个实参类型必须精确匹配,而迭代器本身必须标记一个范围:它们必须指向同一个容器中的元素(或者超出容器末端的下一位置),并且如果两者不相等,则第一个迭代器通过不断地自增,必须可以到达第二个迭代器。
fill(vg,vg+2, 0); //前两个值设为0
fill(vg,vg+10, 10); //前10个值设为10
fill(vg,vg+sizeof(vg), 10); //前每个值设为10
3)标准库为所有泛型和算术算法的每一个迭代器形参指定了范围最小的迭代器种类(迭代器分类见下一小节)。
例如,find(以只读方式单步遍历容器)至少需要一个输入迭代器。replace 函数至少需要一对前向迭代器。replace_copy 函数的头两个迭代器必须至少是前向迭代器,第三个参数代表输出目标,必须至少是输出迭代器。对于每一个形参,迭代器必须保证最低功能。将支持更少功能的迭代器传递给函数是错误的;而传递更强功能的迭代器则没问题。向算法传递无效的迭代器类别所引起的错误,无法保证会在编译时被捕获到。
1.4 泛型算法与迭代器
可以看出,标准库的算法是通过迭代器间接来操作容器的,迭代器作为算法与容器之间的桥梁。标准库除了提供平时使用最多的迭代器-iterator迭代器,标准库还提供了其他特殊用途的迭代器:
- 插入迭代器(inserter、back_inserter 、front_inserter):这类迭代器与容器绑定在一起,实现在容器中插入元素的功能。
• back_inserter,创建使用 push_back 实现插入的迭代器。
• front_inserter,使用 push_front 实现插入。
• inserter,使用 insert 实现插入操作。除了所关联的容器外,inserter还带有第二实参:指向插入起始位置的迭代器。
//
list<int> ilst, ilst2, ilst3, ilst4; // empty lists
for (list<int>::size_type i = 0; i != 4; ++i){ ilst.push_front(i); }
copy (ilst.begin(), ilst.end(), front_inserter(ilst2)); // after copy ilst2 contains: 0 1 2 3
copy (ilst.begin(), ilst.end(), back_inserter(ilst3)); // after copy, ilst3 contains: 3 2 1 0
copy (ilst.begin(), ilst.end(), inserter (ilst4, ilst4.begin()));// after copy, ilst4 contains: 3 2 1 0
- 流迭代器(istream_iterator、 ostream_iterator):这类迭代器可与输入或输出流绑定在一起,用于迭代遍历所关联的 IO 流。
•istream_iterator 用于读取输入流
•ostream_iterator 则用于写输出流
istream_iterator<T> in(strm);//创建从输入流 strm 中读取 T 类型对象的istream_iterator 对象istream_iterator<T> in; //istream_iterator 对象的超出末端迭代器ostream_iterator<T> in(strm);//创建将 T 类型的对象写到输出流 strm 的ostream_iterator 对象ostream_iterator<T> in(strm, delim);//创建将 T 类型的对象写到输出流 strm 的ostream_iterator 对象,在写入过程中使用 delim作为元素的分隔符。delim 是以空字符结束的字符数组
流迭代器除了最基本的迭代器操作:自增、解引用和赋值外,还提供了可比较两个 istream 迭代器是否相等(或不等)的操作,而 ostream 迭代器则不提供比较运算 。
流迭代器都是类模板:任何已定义输入操作符(>> 操作符)的类型都可以定义 istream_iterator。类似地,任何已定义输出操作符(<< 操作符)的类型也可定义 ostream_iterator。
#include <iterator>
#include <fstream>
//从输入流cin读取数据,并将读取内容刷新到cout屏幕输出和文件中,输入quit标识退出命名行输入读取
//
istream_iterator<string> cin_it(cin),eof; // reads int from cin,
ofstream outfile("test.txt");
ostream_iterator<string> outputf(outfile, " ");// writes string from the ofstream named outfile
ostream_iterator<string> output(cout, "\n");
string end_str = "quit";
while (cin_it != eof)
{if(0==strcmp((*cin_it).c_str(),end_str.c_str())){break;}*outputf++ = *cin_it;*output++=*cin_it++;flush(cout);
}
flush(outfile);
- reverse_iterator (反向迭代器):这类迭代器实现向后遍历,而不是向前遍历。所有容器类型都定义了自己的 reverse_iterator 类型,由 rbegin 和 rend 成员函数返回。
反向迭代器是一种反向遍历容器的迭代器。也就是,从最后一个元素到第一个元素遍历容器。反向迭代器将自增(和自减)的含义反过来了:对于反向迭代器,++ 运算将访问前一个元素,而 -- 运算则访问下一个元素。
//cout << "ilst.size() = " << ilst.size() << endl;list<int>::reverse_iterator it_rvec = ilst.rbegin();for (size_t i = 0; i < ilst.size(); i++){cout << *it_rvec << " ";/* code */it_rvec++;}cout << "\n";it_rvec = ilst.rend();it_rvec--;for (size_t i = 0; i < ilst.size(); i++){cout << *it_rvec << " ";/* code */it_rvec--;}cout << "\n";
- const_iterator(常量迭代器):相当于常量指针,迭代器指向内容无法修改。
//list<int>::const_iterator it_const = ilst.begin();it_const++; //OK//*it_const = 10; //errorlist<int>::iterator it_val= ilst.begin();*it_val = 10; //OK
对于迭代器,容器类在其定义时,会依据自身特点来明确其支持的迭代器类型;而对于泛型算法,根据算法要求它的迭代器提供什么类型的操作,对算法分类。因此迭代器是算法与容器沟通的桥梁。
算法要求的迭代器操作分为五个类别:
Input iterator//(输入迭代器) 读,不能写;只支持自增运算
Output iterator//(输出迭代器) 写,不能读;只支持自增运算
Forward iterator//(前向迭代器) 读和写;只支持自增运算
Bidirectional iterator//(双向迭代器) 读和写;支持自增和自减运算
Random access iterator//(随机访问迭代器) 读和写;支持完整的迭代器算术运算
例如,map、set 和 list 类型提供双向迭代器,而 string、vector 和 deque 容器上定义的迭代器都是随机访问迭代器,用作访问内置数组元素的指针也是随机访问迭代器。istream_iterator 是输入迭代器,而ostream_iterator 则是输出迭代器。
二 、标准库算法阐述
1.1 算法形参结构
大多数算法采用下面四种形式形参结构:
alg (beg, end, other parms);
alg (beg, end, dest, other parms);
alg (beg, end, beg2, other parms);
alg (beg, end, beg2, end2, other parms);
其中,alg 是算法的名字,beg 和 end 指定算法操作的元素范围。通常将该范围称为算法的“输入范围”。尽管几乎所有算法都有输入范围,但算法是否使用其他形参取决于它所执行的操作。这里列出了比较常用的其他形参:dest、beg2 和 end2,它们都是迭代器。这些迭代器在使用时,充当类似的角色。除了这些迭代器形参之外,有些算法还带有其他的菲迭代器形参,它们是这些算法特有的。
- dest 形参是一个迭代器,用于指定存储输出数据的目标对象。算法假定无论需要写入多少个元素都是安全的。
- 类似beg 和 end,beg2 和 end2 则标记第二个输入范围,是有些算法所需要的。
- 很多算法通过检查其输入范围内的元素实现其功能。这些算法通常要用到标准关系操作符:== 或 <。其中的大部分算法会提供第二个版本的函数parms,允许程序员提供比较或测试函数parms取代操作符的使用。
sort (beg, end); //采用默认比较
sort (beg, end, comp); //采用自定义的parms-comp函数来比较
1.2 算法命名规范
标准库使用一组相同的命名和重载规范来定义算法名称。
1)很多算法通过检查其输入范围内的元素实现其功能。这些算法通常要用到标准关系操作符:== 或 <。其中的大部分算法会提供第二个版本的函数,允许程序员提供比较或测试函数取代操作符的使用。
sort(beg, end);
stable_sort(beg, end); //保证排序后元素的原始顺序
sort(beg, end, comp);
//
find(beg, end, val);
find_if(beg, end, pred);
2)实现复制的算法版本,标准库会在这些算法在名字中添加了_copy 后缀区分。
reverse(beg, end);
reverse_copy(beg, end, dest
replace_copy_if(beg, end, dest, pred, new_val);
1.3 容器特有算法
泛型算法具有通用性,但算法是否其作用归根还是和数据结构关联的,有些容器其特有结构,对一些算法是无法支持到的。但标准库也相应地为这些容器提供了功能函数支持,使得获得容器一致性。
在我们前面的sort应用例子就看到,list容器是采用了list内置的sort函数,而非公用的sort算法。list 容器上的迭代器是双向的,而不是随机访问类型。由于 list 容器不支持随机访问,因此,在此容器上不能使用需要随机访问迭代器的算法, 例如sort 算法及其相关的算法。还有一些其他的泛型算法,如 merge、remove、reverse 和 unique,虽然可以用在 list 上,但却付出了性能上的代价。如果这些算法利用 list 容器实现的特点,则可以更高效地执行。
如果可以结合利用 list 容器的内部结构,则可能编写出更快的算法。与其他顺序容器所支持的操作相比,标准库为 list 容器定义了更精细的操作集合,使它不必只依赖于泛型操作。表 11.4 列出了 list 容器特有的操作,其中不包括要求支持双向或更弱的迭代器类型的泛型算法,这类泛型算法无论是用在list 容器上,还是用在其他容器上,都具有相同的效果。
/*
*将 lst2 的元素合并到 lst 中。这两个 list 容器对象都必须排序。
*lst2 中的元素将被删除。合并后,lst2 为空。返回 void 类型。
*/
lst.merge(lst2) //第一个版本使用 < 操作符
lst.merge(lst2, comp)//第二个版本则使用 comp 指定的比较运算/*调用 lst.erase 删除所有等于指定值或使指定的谓词函数返回非零值的元素。返回 void 类型*/
lst.remove(val)
lst.remove_if(unaryPred)/*反向排列 lst 中的元素*/
lst.reverse() /*对 lst 中的元素排序*/
lst.sort /*将 lst2 的元素移到 lst 中迭代器 iter 指向的元素前面。在 lst2 中删除移出的元素。*/
lst.splice(iter, lst2) //第一个版本将 lst2 的所有元素移到 lst 中;合并后,lst2 为空。lst 和 lst2 不能是同一个 list 对象
lst.splice(iter, lst2, iter2)//
第二个版本只移动 iter2 所指向的元素,这个元素必须是 lst2 中的元素。在这种情况中,lst 和lst2 可以是同一个 list 对象。也就是说,可在一个 list对象中使用 splice 运算移动一个元素
lst.splice(iter, beg, end) //第三个版本移动迭代器 beg 和 end 标记的范围内的元素。beg 和 end 照例必须指定一个有效的范围。这两个迭代器可标记任意 list 对象内的范围,包括 lst。当它们指定 lst 的一段范围时,如果 iter 也指向这个范围的一个元素,则该运算未定义。/*调用 erase 删除同一个值的集合副本。*/
lst.unique() //第一个版本使用 ==操作符判断元素是否相等
lst.unique(binaryPred)//第二个版本则使用指定的谓词函数实现判断
1.4 标准算法库简述
标准库提供了超过 100 种算法。与容器一样,算法有着一致的结构。下面就将这些算法分类归纳一下,来看看算法对容器提供了那些常用操作。
1)容器排序
std::sort(beg, end)//接受一定范围的元素,并对元素进行排序
std::stable_sort(beg, end)//接受一定范围的元素,并对元素进行排序,其能保证排序后元素的原始顺序
std::sort(beg, end, comp)
std::stable_sort(beg, end, comp)//std::is_sorted(beg, end) //接受一定范围的元素,并判断该范围的元素是否经过排序。
std::shuffle(beg, end,gen) //类似于反排序函数;其接受一定范围的元素,并打乱这些元素。
std::random_shuffle(beg, end)//按随机次序重新安排元素,要求随机访问迭代器支持
std::random_shuffle(beg, end, rand)std::partial_sort(beg, mid, end)//接受一定范围的元素和另一个迭代器,前两个参数决定排序的范围,后两个参数决定不排序的范围。
std::partial_sort(beg, mid, end, comp)std::partial_sort_copy(beg, end, destBeg, destEnd)
std::partial_sort_copy(beg, end, destBeg, destEnd, comp)std::reverse(beg, end) //向后处理输入序列排序,要求双向迭代器支持
std::reverse_copy(beg, end, dest)std::partition(beg, end, unaryPred)//能够接受谓词函数。所有元素都会在谓词函数返回true时,被移动到范围的前端。剩下的将放在范围的后方。
std::stable_partition(beg, end, unaryPred)//类似std::partition,不同的是能保证排序后元素的原始顺序std::nth_element(beg, nth, end)//实参 nth 是一个迭代器,定位输入序列中的一个元素。
std::nth_element(beg, nth, end, comp)std::next_permutation(beg, end)//对序列进行前向和后向处理,要求双向迭代器
std::next_permutation(beg, end, comp)std::prev_permutation(beg, end)//与 next_permutation 很像,但变换序列以形成前一个排列
std::prev_permutation(beg, end, comp)
...
2)在容器中查找元素
std::find(beg, end, val) //可将一个搜索范围和一个值作为参数。函数将返回找到的第一个值的迭代器。线性查找。
std::find_if(beg, end, unaryPred) //在 unaryPred 为真的输入范围中查找。
std::count(beg, end, val) //count 算法返回元素在输入序列中出现次数的计数
std::count_if(beg, end, unaryPred) //在 unaryPred 为真的输入范围中查找。
std::find_first_of(beg1, end1, beg2, end2)//返回第二个范围的任意元素在第一个范围的首次出现的迭代器,如果找不到匹配就返回 end1。
std::find_first_of(beg1, end1, beg2, end2, binaryPred)//使用 binaryPred 比较来自两个序列的元素,返回第一个范围中第一个这种元素的迭代器
std::find_end(beg1, end1, beg2, end2) //返回第二个范围的任意元素在第一个范围的最后出现的迭代器
std::find_end(beg1, end1, beg2, end2, binaryPred)//
std::adjacent_find(beg, end)//返回重复元素的第一个相邻对。如果没有相邻的重复元素。就返回 end。
std::adjacent_find(beg, end, binaryPred)std::search(beg1, end1, beg2, end2)//返回输入范围中第二个范围作为子序列出现的第一个位置。
std::search(beg1, end1, beg2, end2, binaryPred)
std::search_n(beg, end, count, val)
std::search_n(beg, end, count, val, binaryPred)
std::binary_search(beg, end, val)//可将一个搜索范围和一个值作为参数。执行二分查找,当找到对应元素时,返回true;否则,返回false。
std::binary_search(beg, end, val, comp)std::lower_bound(beg, end, val)//可将一个查找返回和一个值作为参数,并且执行二分查找,返回第一个不小于给定值元素的迭代器。
std::lower_bound(beg, end, val, comp)
std::upper_bound(beg, end, val)//与 std::lower_bound 类似,不过会返回第一个大于给定值元素的迭代器。
std::upper_bound(beg, end, val, comp)std::equal_range(beg, end, val)//可将一个搜索范围和一个值作为参数,并且返回一对迭代器。其第一个迭代器和返回结果一样,第二个迭代器和 std::upper_bound 返回结果一样。
std::equal_range(beg, end, val, comp)
std::equal(beg1, end1, beg2)//确定两个序列是否相等。
std::equal(beg1, end1, beg2, binaryPred) //std::mismatch(beg1, end1, beg2)//比较两个序列中的元素,返回一对表示第一个不匹配元素的迭代器。
std::mismatch(beg1, end1, beg2, binaryPred) //for_each(beg, end, f)//对其输入范围中的每个元素应用函数f。
...
3)从容器中删除指定元素
std::remove(beg, end, val)//接受一个容器范围和一个具体的值作为参数,并且移除对应的值。返回一个新的end迭代器,用于修改容器的范围。
std::remove_if(beg, end, unaryPred)
std::remove_copy(beg, end, dest, val)//接受一个容器范围,一个输出迭代器和一个值作为参数。并且将所有不满足条件的元素拷贝到输出迭代器的容器中。
std::remove_copy_if(beg, end, dest, unaryPred)//用 new_val 代替每个匹配元素,对每个元素执行unaryPred,代替使 unaryPred 为真的那些元素std::replace(beg, end, old_val, new_val)//接受一个容器范围和两个值作为参数,将使用第二个数值替换所有与第一个数值相同的值。
std::replace_if(beg, end, unaryPred, new_val)
std::replace_copy(beg, end, dest, old_val, new_val)//与 std::replace 功能类似,将每个元素复制到 dest,用 new_val 代替指定元素。
std::replace_copy_if(beg, end, dest, unaryPred, new_val)//
std::unique(beg, end)//清楚非唯一性的元素,只保留一个
std::unique(beg, end, binaryPred)
std::unique_copy(beg, end, dest)//将唯一元素复制到 dest
std::unique_copy(beg, end, dest, binaryPred)std::rotate(beg, mid, end)//围绕由 mid 表示的元素旋转元素。mid 处的元素成为第一个元素,从 mid +1 到 end 的元素其次,后面是从 beg 到 mid 的范围。返回 void
std::rotate_copy(beg, mid, end, dest)//除了保持输入序列不变并将旋转后的序列写至 dest 之外,与 rotated 很像。返回 voidstd::copy_if //与 std::copy 功能相同,可以多接受一个谓词函数作为是否进行拷贝的依据。
...
4)修改内容
std::copy(beg, end, dest) //将一个容器的数据拷贝到另一个容器
std::copy_n //
std::copy_backward(beg, end, dest)//按逆序将元素复制到输出迭代器 dest。返回 deststd::move //
std::fill(beg, end, val)//范围内写入val
std::fill_n(dest, cnt, val)//将 cnt 个值写到 dest。fill_n 函数写 val 值的 cnt 个副本std::generate(beg, end, Gen)//将新值赋给输入序列中的每个元素,执行 Gen()来创建新值
std::generate_n(dest, cnt, Gen)//对发生器 Gen() 进行 cnt 次计算std::transform(beg, end, dest, unaryOp)//将容器某一范围的元素放置到其他容器中,在拷贝的同时,修改容器中的元素
std::transform(beg, end, beg2, dest, binaryOp)std::merge(beg1, end1, beg2, end2, dest)//合并序列,两个输入序列都必须是已排序的。将合并后的序列写至 dest
std::merge(beg1, end1, beg2, end2, dest, comp)
std::inplace_merge(beg, mid, end)//将同一序列中的两个相邻子序列合并为一个有序序列:将从 beg 到 mid 和从 mid 到 end 的子序列合并为从 beg 到 end 的序列
std::inplace_merge(beg, mid, end, comp)std::swap(elem1, elem2)//算法要求前向迭代器,因为修改输入序列中的元素,交换指定元素或由给定迭代器表示的元素
std::swap_ranges(beg1, end1, beg2)//用开始于 beg2 的第二个序列中的元素交换输入范围中的元素。
std::iter_swap(iter1, iter2)...
5)输入迭代器特有算法
includes(beg, end, beg2, end2)//如果输入序列包含第二个序列中的每个元素,就返回 true;否则,返回 false
includes(beg, end, beg2, end2, comp)set_union(beg, end, beg2, end2, dest)//创建在任一序列中存在的元素的有序序列。两个序列中都存在的元素在输出序列中只出现一次。将序列存储在 dest 中
set_union(beg, end, beg2, end2, dest, comp)set_intersection(beg, end, beg2, end2, dest)//创建在两个序列中都存在的元素的有序序列。将序列存储在 dest 中。
set_intersection(beg, end, beg2, end2, dest, comp)set_difference(beg, end, beg2, end2, dest)//创建在第一个容器中但不在第二个容器中的元素的有序序列。
set_difference(beg, end, beg2, end2, dest, comp)set_symmetric_difference(beg, end, beg2, end2, dest)//创建在任一容器中存在但不在两个容器中同时存在的元素的有序序列。
set_symmetric_difference(beg, end, beg2, end2, dest, comp)
6)最大最小值
min(val1, val2)
min(val1, val2, comp)
max(val1, val2)
max(val1, val2, comp)min_element(beg, end)
min_element(beg, end, comp)
max_element(beg, end)
max_element(beg, end, comp)
7)简单数学计算
accumulate(beg, end, init)//返回输入范围中所有值的总和
accumulate(beg, end, init, BinaryOp)inner_product(beg1, end1, beg2, init)//返回作为两个序列乘积而生成的元素的总和。
inner_product(beg1, end1, beg2, init, BinOp1, BinOp2)partial_sum(beg, end, dest)//将新序列写至 dest,其中每个新元素的值表示输入范围中在它的位置之前(不包括它的位置)的所有元素的总和。
partial_sum(beg, end, dest, BinaryOp)adjacent_difference(beg, end, dest)//将新序列写至 dest,其中除了第一个元素之外每个新元素表示当前元素和前一元素的差。
adjacent_difference(beg, end, dest, BinaryOp)
8)更多算法相关操作如下
头文件 名字
algorithm lower_bound
algorithm max
algorithm min
algorithm nth_element
algorithm partial_sort
algorithm stable_sort
algorithm unique
algorithm unique_copy
algorithm upper_bound
algorithm copy
algorithm count
algorithm count_if
algorithm equal_range
algorithm fill
algorithm fill_n
algorithm find
algorithm find_end
algorithm find_first_of
algorithm for_each
algorithm replace
algorithm replace_copy
algorithm set_difference
algorithm set_intersection
algorithm set_union
algorithm sort
cstdlib abort
functional bind2nd
bitset bitset
deque deque
exception exception
fstream fstream
fstream ifstream
cstddef ptrdiff_t
iomanip setfill
iomanip setprecision
iomanip setw
cstddef size_t
ios_base ios_base
cctype isalpha
cctype islower
cctype ispunct
cctype isspace
cctype isupper
functional less_equal
functional negate
functional not1
fstream of stream
functional plus
cmath sqrt
cstring strcmp
cstring strcpy
cstring strlen
cstring strncpy
exception terminate
cctype tolower
cctype toupper
exception unexpected
头文件 名字
iostream boolalpha
iostream cerr
iostream cin
iostream cout
iostream dec
iostream endl
iostream ends
iostream fixed
iostream flush
iostream hex
iostream internal
iostream right
iostream scientific
iostream showbase
iostream showpoint
iostream skipws
iostream istream
iostream left
iostream noboolalpha
iostream noshowbase
iostream noshowpoint
iostream noskipws
iostream nounitbuf
iostream nouppercase
iostream oct
iostream ostream
iostream unitbuf
iostream uppercase
头文件 名字
numeric accumulate
memory allocator
memory auto_ptr
iterator back_inserter
new bad_alloc
typeinfo bad_cast
iterator front_inserter
string getline
numeric inner_product
iterator inserter
queue priority_queue
queue queue
stdexcept range_error
iterator reverse_iterator
stdexcept runtime_error
set set
iterator istream_iterator
sstream istringstream
list list
stdexcept logic_error
utility make_pair
map map
map multimap
set multiset
iterator ostream_iterator
sstream ostringstream
stdexcept out_of_range
utility pair
stack stack
string string
sstream stringstream
typeinfo type_info
memory uninitialized_copy
vector vector
三、源码案例及测试
3.1 编译测试
创建test.h/cpp源文件,通过g++ test.cpp -o test.exe编译指令生成程序,运行测试:
3.2 源代码
test.h
#ifndef _TEST_H_
#define _TEST_H_
#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <numeric>
#include <algorithm>
#include <time.h>
#include <random>
#include <iterator>
#include <fstream>
#include <cstring>
#endif //_TEST_H_
test.cpp
#include "test.h"using namespace std;bool compare(int a,int b)
{return a>b;
};
//
random_device rd;
mt19937 g {rd()};int main(int argc, char* argv[])
{vector<int> v{100, 400, 200, 300, 200, 500, 100, 400, 600, 300 };int sum = 0;for (auto &&i : v){sum += i;}cout << "sum = " << sum << endl;//cout << "sum = " << accumulate(begin(v), end(v), 0) << endl;//创建一个大数组vectorvector<int> v_sort;const int v_size = 10000;const int read_size = 20;for(int i=0; i<v_size;i++){v_sort.push_back(rand()%v_size);}cout << "v_sort is_sorted = "<< is_sorted(v_sort.begin(), v_sort.end()) << endl;//构造一个新的vertor,为了公平测试后面容器,调用sort算法排序vectorvector<int> v_sort_new(v_sort.begin(),v_sort.end());cout << "clock()1 = " << clock() <<endl;sort(v_sort_new.begin(),v_sort_new.end());cout << "clock()2 = " << clock() <<endl;//输出已排序的vector前一段数据for(int i=0; i<read_size;i++){cout << v_sort_new[i] << " ";}cout << "\n";//将vector拷贝构造list,调用list的sort函数排序list<int> l_sort(v_sort.begin(),v_sort.end());cout << "clock()3 = " << clock() <<endl;l_sort.sort();cout << "clock()4 = " << clock() <<endl;//输出已排序的list前一段数据list<int>::iterator it_list=l_sort.begin();for (int i=0; i<read_size;i++){/* code */cout << *(it_list++) << " ";}cout << "\n";//将vector拷贝构造multiset,multiset在构造时就自动排序cout << "clock()5 = " << clock() <<endl;multiset<int> set_sort(v_sort.begin(),v_sort.end());cout << "clock()6 = " << clock() <<endl;//输出已排序的multiset前一段数据multiset<int>::iterator it_mset = set_sort.begin();for (int i=0; i<read_size;i++){/* code */cout << *(it_mset++) << " ";}cout << "\n";//将multiset拷贝回vector,multiset在构造时就自动排序,vector同样得到已排序结构cout << "clock()7 = " << clock() <<endl;v_sort.clear();v_sort.assign(set_sort.begin(),set_sort.end());cout << "clock()8 = " << clock() <<endl;//重新输出已排序的vector前一段数据for(int i=0; i<read_size;i++){cout << v_sort[i] << " ";}cout << "\n";cout << "v_sort is_sorted = "<< is_sorted(v_sort.begin(), v_sort.end()) << endl;//shuffle(v_sort.begin(),v_sort.end(),g);cout << "v_sort is_sorted = "<< is_sorted(v_sort.begin(), v_sort.end()) << endl; //=0//partition(v_sort.begin(),v_sort.end(), [] (int i) { return i < 20; });//重新输出已排序的vector前一段数据for(int i=0; i<read_size;i++){cout << v_sort[i] << " ";}cout << "\n";//shuffle(v_sort.begin(),v_sort.end(),g);auto middle (next(begin(v_sort), 100));partial_sort(begin(v_sort), middle, end(v_sort));//重新输出已排序的vector前一段数据for(int i=0; i<read_size;i++){cout << v_sort[i] << " ";}cout << "\n";//shuffle(v_sort.begin(),v_sort.end(),g);sort(v_sort.begin(),v_sort.end(),compare);//重新输出已排序的vector前一段数据for(int i=0; i<read_size;i++){cout << v_sort[i] << " ";}cout << "\n";//算法与容器类型无关vector<int>::const_iterator result_vec = find(v_sort.begin(),v_sort.end(),99);//vector查找list<int>::const_iterator result_list = find(l_sort.begin(),l_sort.end(),99); //list查找multiset<int>::iterator result_mset = find(set_sort.begin(),set_sort.end(),99);//multiset 查找int vg[12] = {71,73,29,34,40,23,84,99,21,78,18,58};int *result_vg = find(vg,vg+12,99); //数组查找//算法修改容器值fill(v_sort.begin(),v_sort.begin()+2, 0); //前两个值设为0fill(vg,vg+2, 0); //前两个值设为0//算法会移动容器内的数据位置sort(vg,vg+12,compare);fill(vg,vg+sizeof(vg), 10); //前每个值设为10//list<int> ilst, ilst2, ilst3, ilst4; // empty listsfor (list<int>::size_type i = 0; i != 4; ++i)ilst.push_front(i); copy (ilst.begin(), ilst.end(), front_inserter(ilst2)); // after copy ilst2 contains: 0 1 2 3copy (ilst.begin(), ilst.end(), back_inserter(ilst3)); // after copy, ilst3 contains: 3 2 1 0copy (ilst.begin(), ilst.end(), inserter (ilst4, ilst4.begin()));// after copy, ilst4 contains: 3 2 1 0///*//注释,编译后面代码测试istream_iterator<string> cin_it(cin),eof; // reads int from cin,ofstream outfile("test.txt");ostream_iterator<string> outputf(outfile, " ");// writes string from the ofstream named outfileostream_iterator<string> output(cout, "\n");string end_str = "quit";while (cin_it != eof){if(0==strcmp((*cin_it).c_str(),end_str.c_str())){break;}*outputf++ = *cin_it;*output++ = *cin_it++;flush(cout);}flush(outfile);*///cout << "ilst.size() = " << ilst.size() << endl;list<int>::reverse_iterator it_rvec = ilst.rbegin();for (size_t i = 0; i < ilst.size(); i++){cout << *it_rvec << " ";/* code */it_rvec++;}cout << "\n";it_rvec = ilst.rend();it_rvec--;for (size_t i = 0; i < ilst.size(); i++){cout << *it_rvec << " ";/* code */it_rvec--;}cout << "\n";//list<int>::const_iterator it_const = ilst.begin();it_const++;//*it_const = 10; //errorlist<int>::iterator it_val= ilst.begin();*it_val = 10;return 0;
};
相关文章:
![](https://img-blog.csdnimg.cn/1b4563ea0ed242348b47cc93a22265e4.png)
c/c++开发,无可避免的模板编程实践(篇六)
一、泛型算法 1.1 泛型算法概述 c标准库不仅包含数据结构(容器、容器适配器等),还有很多算法。数据结构可以帮助存放特定情况下需要保存的数据,而算法则会将数据结构中存储的数据进行变换。标准库没有给容器添加大量的功能函数&am…...
![](https://img-blog.csdnimg.cn/d33b4be47df346f5bd3b85cc4722060e.png)
【Java】Spring核心与设计思想
文章目录Spring核心与设计思想1. Spring是什么1.1 什么是容器1.2 什么是IOC1.2.1 传统程序开发1.2.2 控制反转式程序开发1.2.3 对比总结规律1.3 理解Spring IOC1.4 DI概念说明Spring核心与设计思想 1. Spring是什么 我们通常所说的Spring指的是Spring Framework(S…...
![](https://img-blog.csdnimg.cn/img_convert/a3af6e5536de9febdc64654bc5157782.png)
组合实现多类别分割(含实战代码)
来源:投稿 作者:AI浩 编辑:学姐 摘要 segmentation_models_pytorch是一款非常优秀的图像分割库,albumentations是一款非常优秀的图像增强库,这篇文章将这两款优秀结合起来实现多类别的图像分割算法。数据集选用CamVid…...
![](https://hnxx.oss-cn-shanghai.aliyuncs.com/official/1673487401625.png?t=0.37365235739630287)
从红队视角看AWD攻击
AWD的权限维持 攻防兼备AWD模式是一种综合考核参赛团队攻击、防御技术能力、即时策略的比赛模式。在攻防模式中,参赛队伍分别防守同样配置的虚拟靶机,并在有限的博弈时间内,找到其他战队的薄弱环节进行攻击,同时要对自己的靶机环…...
![](https://img-blog.csdnimg.cn/img_convert/60594079dd78e9b94bc8c233f2973d9a.png)
龙腾万里,福至万家——“北京龙文化促进协会第九届龙抬头传承会”在京举办
2023年2月21日(农历2月初二)上午9:00点至下午13:00,由北京龙文化促进协会主办、传世经典(北京)文化发展有限公司承办、北京华夏龙文旅联盟协办的“北京龙文化促进协会第九届二月二龙抬头传承会”在北京市丰台区顺和国际大厦A口6层会议厅隆重召开。 传承会活动内容主…...
![](https://www.ngui.cc/images/no-images.jpg)
《软件方法》强化自测题-业务建模(4)
按照业务建模、需求、分析、设计工作流考察,答案不直接给出,可访问自测链接或扫二维码自测,做到全对才能知道答案。 知识点见《软件方法》(http://www.umlchina.com/book/softmeth.html)、 “软件需求设计方法学全程…...
![](https://img-blog.csdnimg.cn/ede475fb2b3e4636b7177fede0c71bca.png)
Prometheus之pushgateway
Pushgateway简介 Pushgateway是Prometheus监控系统中的一个重要组件,它采用被动push的方式获取数据,由应用主动将数据推送到pushgateway,然后Prometheus再从Pushgateway抓取数据。使用Pushgateway的主要原因是: Prometheus和targ…...
![](https://img-blog.csdnimg.cn/img_convert/9a36fd8409df4120bbda13672e7b8c17.png)
3分钟带您快速了解HIL测试及其架构
什么是HIL测试硬件在环(HIL)仿真是一种用于测试导航系统的技术,其中测试前并不知道车辆轨迹。在这种情况下,车辆轨迹被实时馈送到GNSS模拟器。HIL可用于复杂实时系统的开发和测试,如卫星控制系统、军事战术导弹、飞机飞…...
![](https://img-blog.csdnimg.cn/img_convert/a5e6ce6eacd4c6f0ec6043db1ff906af.jpeg)
华为认证含金量如何?
一本证书是否有用,还要看它是否被市场所认可。 我们说华为认证HCIP有用,很大一部分还取决于它极高的适用性和权威性。华为是国内最大的生产销售通信设备的民营通信科技公司。 自2013年起,国家对网络安全极度重视,相继把国外的网…...
![](https://www.ngui.cc/images/no-images.jpg)
刷题记录:牛客NC54586小翔和泰拉瑞亚
传送门:牛客 题目描述: 小翔爱玩泰拉瑞亚 。 一天,他碰到了一幅地图。这幅地图可以分为n列,第i列的高度为Hi,他认为这个地图不好看,决定对它进行改造。 小翔又学会了m个魔法,实施第i个魔法可以使地图的第Li列到第Ri列…...
![](https://img-blog.csdnimg.cn/fc1a15988cea4ebf864c88b8aa7387cf.png)
面试个3年自动化测试,测试水平一言难尽。。。。
公司前段缺人,也面了不少测试,结果竟然没有一个合适的。 一开始瞄准的就是中级的水准,也没指望来大牛,提供的薪资在10-20k,面试的人很多,但平均水平很让人失望。 看简历很多都是3年工作经验,但…...
![](https://img-blog.csdnimg.cn/16c060d9a42c4913b3f059443b245439.png)
C++面向对象(下)
文章目录前言1.再谈构造函数1.初始化列表2.explicit关键字2. static成员1.概念3.友元1.概念2.友元函数3.友元类4. 内部类5.匿名对象6.编译器优化7.总结前言 本文是主要是将之前关于C面向对象中的一些没有归纳到的零星知识点进行补充,同时对C中的面向对象简单收个尾…...
![](https://img-blog.csdnimg.cn/9a633f6b132c48fc90398f64255e5c2a.png)
面试一位软件测试6年工作者:一年经验掰成六年来用....
在众多面试中,对于那个工作了6年的面试者,我印象很深刻,因为最开始拿到简历的时候,我一摸:"这简历,好厚啊!"再一看,工作6年。 于是我去找了我的领导,我说:“这人我应该没…...
![](https://www.ngui.cc/images/no-images.jpg)
Java8 新特性--Optional
Optional是什么 java.util.Optional Jdk8提供Optional,一个可以包含null值的容器对象,可以用来代替xx ! null的判断。 Optional常用方法 of public static <T> Optional<T> of(T value) {return new Optional<>(value); }为value…...
![](https://img-blog.csdnimg.cn/196303784b2c49f5be19e159fe6bb3a9.png)
Pytorch GPU版本简明下载安装教程
1.根据自己的显卡型号下载显卡驱动并安装。这一步会更新你的显卡驱动,也可忽略第1步,如果第2步出现问题,返回执行第1步。 点击这里下载英伟达显卡驱动 2.安装完成后,wincmd打开命令行,输入nvidia-smi,查看…...
![](https://img-blog.csdnimg.cn/img_convert/6f8087eca74d7ae3e93cbbef05c024d3.png)
【C++】map和set的封装
文章目录一、前情回顾二、简化源码三、仿函数四、迭代器五、set的实现六、map的实现七、红黑树代码一、前情回顾 set 参数只有 key,但是map除了key还有value。我们还是需要KV模型的红黑树的: #pragma once #include <iostream> #include <ass…...
![](https://www.ngui.cc/images/no-images.jpg)
互融云金融控股集团管理平台系统搭建
金融控股公司是指对两个或两个以上不同类型金融机构拥有实质控制权,自身仅开展股权投资管理、不直接从事商业性经营活动的有限责任公司或者股份有限公司。 金融控股公司是金融业实现综合经营的一种组织形式,也是一种追求资本投资最优化、资本利润最大化…...
![](https://img-blog.csdnimg.cn/dccedd9b8ee3400cb1e0d50fb0b58fe6.png)
Git复习
1. 引言 现在要用到Git,复习一下关于Git的指令,知识摘自《Pro Git》 2. 起步 git和其他版本控制软件最大的差别在于git是直接记录某个版本的快照,而不是逐渐地比较差异。 安装: sudo apt install git-all设置用户信息: git c…...
![](https://www.ngui.cc/images/no-images.jpg)
WebGPU学习(2)---使用VertexBuffer(顶点缓冲区)
在本文中,我们使用 VertexBuffer 绘制一个矩形。示例地址 1.准备顶点数据 首先,我们准备好顶点数据。定义顶点数据有多种方法,这次我们将在 TypeScript 代码中将其定义为 Float32Array 类型的数据。 const quadVertexSize 4 * 8; // 一个顶…...
![](https://www.ngui.cc/images/no-images.jpg)
【C++之容器篇】AVL树的底层原理和使用
目录前言一、AVL树二、AVL树的底层实现1. 结点类型的定义2. AVL树的定义3. 查找函数4. 插入函数(重难点)三、判断平衡树的方法前言 AVL树其实是在搜索树的基础上加上一些限制因素,从而使搜索树的结构保持相对平衡,通过前面我们对二叉搜索树的学习&#x…...
![](https://hnxx.oss-cn-shanghai.aliyuncs.com/official/1673487401625.png?t=0.37365235739630287)
从交换机安全配置看常见局域网攻击
前言 构建零信任网络,自然离不开网络准入(NAC),这就涉及到交换机的一些安全测试,于是有了此文《从交换机安全配置看常见局域网攻击》。 交换机安全配置 如本文标题所说从交换机安全配置看常见的局域网攻击,那么下面提到的各种攻…...
![](https://img-blog.csdnimg.cn/e28cedd476b545b2b1245a7d50e255cb.png)
工具篇3.5世界热力图
一、定义 世界热力图是一种地图形式,它使用颜色的变化来显示世界各个地区的某种指标(如 GDP、人口、气候等)的分布和密度。通常,世界热力图会使用不同的颜色来表示数据的变化,例如使用蓝色表示低值,红色表…...
![](https://www.ngui.cc/images/no-images.jpg)
2023-02-20 leetcode-insertionSortList
摘要: 记录leetcode-insertionSortList的反思 要求: https://leetcode.cn/problems/insertion-sort-list/ Given the head of a singly linked list, sort the list using insertion sort, and return the sorted lists head. The steps of the insertion sort algorithm: In…...
![](https://www.ngui.cc/images/no-images.jpg)
LeetCode初级算法题:环形链表+排列硬币+合并两个有序数组java解法
环形链表排列硬币合并两个有序数组(没错,出现过一次的LeetCode合并数组又双叒叕出现了!)经典算法题java解法 目录1 环形链表题目描述解题思路与代码解法一:哈希表解法二:双指针2 排列硬币题目描述解题思路与…...
![](https://img-blog.csdnimg.cn/img_convert/d8f156ff73d90e895721ce3f8b5f0db8.png)
网络编程学习一
1、初识网络编程2、网络编程三要素3、三要素(IP)4、IPV4的一些小细节5、Inetaddress类的使用package com.leitao.demo.network;import java.net.InetAddress; import java.net.UnknownHostException;/*** Description: TODO* Author LeiTao* Date 2023/2…...
![](https://img-blog.csdnimg.cn/img_convert/f4c2b5099fb61d5e95d7a4afeb207ee5.png)
Javascript 立即执行函数
IIFE,一般称为立即执行函数。你可能会问我,*“嘿!我知道正常的函数表达式是什么样子的,但是 IIFE 到底是什么?”。*好吧,这正是我今天要在本文中回答的问题。 函数表达式 在了解立即调用函数表达式之前,让…...
![](https://img-blog.csdnimg.cn/img_convert/21181ec7bff94d2b8dd3a512e91adaf5.png)
基于Django和vue的微博用户情感分析系统
完整代码:https://download.csdn.net/download/weixin_55771290/87471350概述这里简单说明一下项目下下来直接跑起的方法。前提先搞好python环境和vue环境,保证你有一个账户密码连上数据库mysql。1、pip install requirements.txt 安装python包2、修改mysql数据库的…...
![](https://img-blog.csdnimg.cn/74b26d4ff2734ada97cf3ed04dfb911a.png)
【C++】IO流
🌈欢迎来到C专栏~~IO流 (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort目前状态:大三非科班啃C中🌍博客主页:张小姐的猫~江湖背景快上车🚘,握好方向盘跟我有一起打天下嘞!送给自己的一句鸡汤…...
![](https://img-community.csdnimg.cn/avatar/fcc8fa9f87404652beb9e08a0ac9652d.png?x-oss-process=image/resize,m_fixed,h_88,w_88)
【论文速递】ACL 2021-CLEVE: 事件抽取的对比预训练
【论文速递】ACL 2021-CLEVE: 事件抽取的对比预训练 【论文原文】:CLEVE: Contrastive Pre-training for Event Extraction 【作者信息】:Wang, Ziqi and Wang, Xiaozhi and Han, Xu and Lin, Yankai and Hou, Lei and Liu, Zhiyuan and Li, Peng and …...
![](https://img-blog.csdnimg.cn/aac15518b0de432fa31f67eb00e926aa.jpeg#pic_center)
《自动驾驶规划入门》专栏结语
一、 源起 2021年10月12日,化学工业出版社的金编辑根据博客中留下的微信号联系上我,问我有没有出书的想法。从小到大,书与文字在我心里是有着神圣地位的。我在“想试试”与“害怕做不好”这两种矛盾的心情中,还是先应了下来。签了…...
![](https://img-blog.csdnimg.cn/20201007205012479.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FhMjUyODg3Nzk4Nw==,size_16,color_FFFFFF,t_70#pic_center)
太平洋手机报价大全/家庭优化大师
什么是架构分隔 单体 单体:是把系统部署到一台服务器上,所有的请求业务都由这台服务器处理 优点:适合小型系统,节省资源 缺点:安全性低,一旦有突发压力, 整个系统都会面临崩溃 分层—隔离效果…...
![](https://img-blog.csdnimg.cn/img_convert/637c06e21c5ed56faaa121ade8e5fa13.png)
东莞微联建站/商业计划书
媒体报道的消息显示,金立前总裁卢伟冰加入小米,同时小米宣布旗下品牌红米改名为Redmi并独立运营,有分析认为卢伟冰可能将负责红米品牌,并巩固红米品牌在国际市场的竞争优势,而小米品牌则专注于高端手机市场。小米这一做…...
![](https://img-blog.csdnimg.cn/img_convert/c197e4fe17a9e818e11d43148bb51bd4.png)
wordpress临时维护/seo推广论坛
在Vue中,其中最最最核心的一个知识点就是数据响应式原理,数据响应式原理归结起来就包含两大部分:侦测数据变化、依赖收集,了解这两个知识点就了解到了数据响应式原理的精华。一、侦测数据变化能够帧听到数据变化是数据响应式原理的…...
![](http://upload-images.jianshu.io/upload_images/15567473-140e7ecc0e745237.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
网站建设商城/独立站建站需要多少钱
做了这么多年的数据分析和挖掘工作,一直都在思考一个问题,“互联网和金融,在数据挖掘上到底存在什么样的区别”。在对这个问题的摸索和理解过程中,发现数据挖掘本身包含很多层次。模型本身也是存在传统和时髦之分的。本文就想聊聊…...
![](/images/no-images.jpg)
网站营销方式/今日十大热点新闻头条
写在前面 本文首发于公众号:符合预期的CoyPan 最近做了一个移动端活动页的需求,大概就是diy一个页面。用户可以对物料进行拖动、缩放、旋转,来达到diy的目的。用DOM来实现是不现实的,我采用了canvas来实现和用户的交互。开发过程中…...
省政府网站集约化建设/站长工具查询域名
先这里把unassigned_shards的节点手动删除掉,然后重新配置你的集群 这是应急之策,暂时留用,之后理解后重新解决...