网站开发手机端响应式网站建设代理商

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

网站开发手机端,响应式网站建设代理商,淘宝运营学习,怎样查看wordpress用的什么主题在本篇当中我们将学习STL中的list#xff0c;在此list就是我们之前在数据结构学习过的链表#xff0c;在本篇中我们要来了解list当中的成员函数该如何使用#xff0c;由于list各个函数的接口和之前学习过的vector类型#xff0c;因此在学习list的使用就较为轻松。在lis篇章…在本篇当中我们将学习STL中的list在此list就是我们之前在数据结构学习过的链表在本篇中我们要来了解list当中的成员函数该如何使用由于list各个函数的接口和之前学习过的vector类型因此在学习list的使用就较为轻松。在lis篇章中我们要重点了解的是在下一个篇章当中的list模拟实现中的迭代器实现由于list底层的物理空间不一定是连续的因此list迭代器的实现相比之前学习过的容器就复杂多了在下一篇中将带来细致的讲解。在此之前我们先来了解list该如何使用吧 1.构造函数 queue - C Reference 通过以上的文档所示就可以看出list类提供的构造函数接口和之前我们学习过的vector非常的相识都实现了迭代器区间构造n个指定元素的构造使用initializer_list实现构造;拷贝构造 例如以下实现 #includeiostream #includelist #includestring using namespace std;int main() {string a(abcdef);listint lt1;listint lt2({ 1,2,3,4,5 });listint lt3(a.begin(), a.end());listint lt4(10, 9);listint lt5(lt2);return 0; } 2.析构函数 queue - C Reference 在list类当中也实现了析构函数在此该函数不需要我们在使用list时显示调用这是由于析构函数编译器会在list对象销毁时自动调用 3.赋值运算符重载 list::operator - C Reference 在list实现了赋值运算符重载的函数这就使得将一个已经初始化的list对象赋值给另一个已经初始化的list对象 例如以下示例 #includeiostream #includelist using namespace std;int main() {listint lt1;listint lt2({ 1,2,3,4,5 });lt1 lt2;return 0; } 4. 容量操作 在list当中由于和之前学习的string、vector不同在存储数据时每存储一个指定的元素就申请相应的对应合适的内存空间因此在list就不会提前将空间开好这就使得list当中容量操作相关的函数只有以下所示 4.1 size list::size - C Reference 在list提供了用于得到list对象内有效元素个数的成员函数size到了这里你就可以了解到为什么之前在之前string当中得到有效元素建议使用size而不是length这就使因为size在其他STL当中也是有提供的而length只是string特有的都使用size就利于记忆 4.2 empty list::empty - C Reference 在list也提供用于判断list对象内有效元素是否为空的函数empty当为空时就返回true否则就为flase 5. 迭代器 在list类当中由于底层存储数据的内存空间不像string和vector一样物理结构是连续的因此在list就没有提供下标[ ]的方式实现对list对象内元素的访问因此在list就只能使用迭代器来实现 在此list内的迭代器和之前学习过的容器一样也提供了普通迭代器和反向迭代器并且在这些当中都实现了const和非const版本  5.1 begin与end list::begin - C Reference 在此list内提供的begin函数还是指向对象的第一个元素返回值为迭代器 list::end - C Reference 在此list内提供的end函数还是指向对象的最后一个元素后一个位置返回值为迭代器 有了begin()和end()就可以实现对list对象遍历例如以下示例 #includeiostream #includelist using namespace std;int main() {listint lt2({ 1,2,3,4,5 });lt1 lt2;listint::iterator t1 lt1.begin();while (t1 ! lt1.end()){(*t1);t1;}t1 lt1.begin();while (t1 ! lt1.end()){cout *t1 ;t1;}cout endl;return 0; } 6.范围for 在此在list由于实现了迭代器那么就可以使用范围for来对list对象遍历其的元素 例如以下示例 #includeiostream #includelist using namespace std;int main() {listint lt1;lt1 lt2;for (auto x : lt1){cout x ;}cout endl;return 0; } 7.取头尾元素 list::front - C Reference list::back - C Reference 在list提供了front函数和back函数来分别实现得到list对象内的首元素与尾元素  例如以下示例 #includeiostream #includelist using namespace std;int main() {listint lt2({ 1,2,3,4,5 });cout lt2front: lt2.front()endl;cout lt2back: lt2.back() endl;return 0; } 8.元素修改操作 在list当中提供了以上所示的各种用于修改list对象内元素的函数 8.1 push_front与pop_front list::push_front - C Reference list::pop_front - C Reference 在list内实现了push_front和pop_front来放分别实现在对象内头插和头删元素在此要了解到为什么在list会提供该函数而之前学习的string与vector都未实现该函数这是由于在在链表当中头插和头删元素不需要像顺序表一样在头插和头删时将大量的元素移动这样就不会出现效率低下的问题 8.2 push_back与pop_back list::push_back - C Reference list::pop_back - C Reference 在list内实现了push_back和pop_back来放分别实现在对象内尾插和尾删元素 8.3 insert和erase list::insert - C Reference list::erase - C Reference 在list也提供了inert和erase来实现任意位置的插入和删除在此实现的接口和vector相识参数都是迭代器或者迭代器区间  8.4 emplace_front与emplace_back list::emplace_front - C Reference list::emplace_back - C Reference 在C11之后实现了以上的两个函数emplace_front和emplace_back,那么这两个函数有什么作用呢 在此你可以认为emplace_front和emplace_back与push_front和push_back的功能是类似的功能分别是在list对象头插和尾插指定的数据但其实这两个函数与push_front和push_back还是有差异的接下来就来大体的讲解 注在此emplace_front和emplace_back函数的参数涉及到可变模板参数、右值引用等概念在此不进行讲解这些要到之后C11篇章才进行 通过之前的学习我们知道push_front和push_back是可以实现在相应的类对象内插入内置类型的数据也可以插入自定义类型的数据并且在此支持隐式类型转换 例如以下示例 #includeiostream #includelist using namespace std;class Postion { public:Postion(int row,int col):_row(row),_col(col){}private:int _row;int _col; };int main() {listPostion lt1;Postion p1(1,2);lt1.push_back(p1);//使用匿名对象lt1.push_back(Postion(1, 2));//使用多参数隐式类型转换lt1.push_back({ 1, 2 });return 0; } 那么以上使用emplace_back不同的参数也可以使用吗 #includeiostream #includelist using namespace std;class Postion { public:Postion(int row,int col):_row(row),_col(col){}private:int _row;int _col; };int main() {listPostion lt1;Postion p1(1, 2);lt1.emplace_back(p1);lt1.emplace_back(Postion(1, 2));lt1emplace_back({ 1, 2 });return 0; } 以上代码在VS下就会出现以下的编译报错这是为什么呢 要解决以上的问题就先了解到emplace_back的形参的类型是根据实参的类型来推导的那么在以上使用隐式类型转换形参就无法根据实参{ 1, 2 }来推导具体的类型这里具体的原因就是形参是模板 在此emplace_back和emplace_front最大的特点是支持直接将构造对象的参数直接传emplace_back和emplace_front函数的参数 因此以上代码正确的使用方法是如下所示 #includeiostream #includelist using namespace std;class Postion { public:Postion(int row,int col):_row(row),_col(col){} private:int _row;int _col; };int main() {listPostion lt1;Postion p1(1,2);lt1.push_back(p1);lt1.push_back(Postion(1, 2));lt1.push_back({ 1, 2 });listPostion lt1;Postion p1(1, 2);lt1.emplace_back(p1);lt1.emplace_back(Postion(1, 2));//lt.1emplace_back({ 1, 2 });lt1.emplace_back( 1, 2 );return 0; } 注在此由于emplace_back是直接构造去初始化节点而push_back需要通过构造外加拷贝实现的因此emplace_back相比push_back效率更高 9. list内特有的操作 在list中提供了以上特有的函数来实现逆置、排序等的操作这些操作是之前我们在string和vector当中没有提供的那么接下来我们就来了解这些函数的作用以及使用方法但张这些函数其实在实践当中的使用频率非常少因此我们只需要了解基本的使用方法即可 9.1 splice list::splice - C Reference 在此splice的作用是将一个list对象的值转移给另一个list的对象并且转移之后被转移对象会变为空 例如以下示例 #includeiostream #includelist using namespace std;int main() {listint mylist1, mylist2;listint::iterator it;// set some initial values:for (int i 1; i 4; i)mylist1.push_back(i); // mylist1: 1 2 3 4for (int i 1; i 3; i)mylist2.push_back(i * 10); // mylist2: 10 20 30it mylist1.begin();it; // points to 2mylist1.splice(it, mylist2); // mylist1: 1 10 20 30 2 3 4// mylist2 (empty)// it still points to 2 (the 5th element)return 0; } 再来看以下示例 有一种LRU算法即最近最少使用算法 在此有一个数组要将使用到的元素位置移到数组的头部那么该如何实现呢 在此就可以使用到splice函数来实现 #includeiostream #includelist using namespace std;int main() {listint a { 1,2,3,4,5,6,7,8,9 };int x;while (cin x){auto found find(a.begin(), a.end(), x);if (found ! a.end()){a.splice(a.begin(), a, found);}for (auto x : a){cout x ;}cout \n;}return 0; } 9.2 remove list::remove - C Reference 在此erase的作用是将list内的值为指定值的元素删除可以认为remove是find和erase的结合体在之前使用erase删除指定值的元素时要先使用find查找到相应迭代器的位置再将相应的迭代器传给erase才能实现而remove就可以直接实现以上的操作 例如以下示例 #includeiostream #includelist using namespace std;int main() {int myints[] { 17,89,7,14 };listint mylist(myints, myints 4);mylist.remove(89);cout mylist contains:;for (auto x : mylist){cout x ;}cout \n;return 0; } 注在此的remove_if涉及到仿函数在此先不做讲解到之后stack和queue再介绍仿函数是什么 9.3  merge find - C Reference 在此在list内提供了merge来实现两个链表的归并在此要求是两个链表是有序的才能进行归并并且将一个list对象归并到另一个对象之后这个list对象就为空了 例如以下示例 #includeiostream #includelist using namespace std;int main() {listint lt1({1,3,5,7,9});listint lt2;lt2.push_back(2);lt2.push_back(4);lt2.push_back(6);lt2.push_back(8);lt1.merge(lt2);//(lt2 is now empty)for (auto x : lt2){cout x ;}cout \n;for (auto x : lt1){cout x ;}cout \n;return 0; } 9.4 unique list::unique - C Reference 在此list提供了unique来实现去重将list对象内重复的元素只保留一个其他重复的去除掉在此前提list内的数据是有序的才能去去重 例如以下示例 #includeiostream #includelist using namespace std;int main() {listint lt1({ 1,2,3,3,3,5,7,8,9 });lt1.unique();for (auto x : lt1){cout x ;}cout \n;return 0; } 9.5 sort list::sort - C Reference 在list还提供了对对象内元素进行排序的函数sort在此sort默认排的是升序 例如以下示例 #includeiostream #includelist using namespace std;int main() {listint lt1;lt1.push_back(9);lt1.push_back(-2);lt1.push_back(3);lt1.push_back(-10);for (auto x : lt1){cout x ;}cout \n;lt1.sort();for (auto x : lt1){cout x ;}cout \n;return 0; } 以上sort默认能实现升序是因为形象的缺省值默认是less在此less是一个仿函数, 在此我们只要先了解到仿函数是一个类就可以了能实现比较是由于在类内部实现了()的运算符重载在之后的stack与queue章节将会详细讲解 在此使用sort时要实现降序就要使用另外的一个仿函数greater 例如以上示例要排成降序 #includeiostream #includelist using namespace std;int main() {listint lt1;lt1.push_back(9);lt1.push_back(-2);lt1.push_back(3);lt1.push_back(-10);for (auto x : lt1){cout x ;}cout \n;//使用匿名对象创建一个greater类型的对象作为sort的实参lt1.sort(greaterint());for (auto x : lt1){cout x ;}cout \n;return 0; } 在此你可以会有一个疑惑之前在vector当中要进行排序调用算法库内的sort就可以实现了那么为什么在list内要单独实现一个sort不是直接调用算法库内的就好了吗 在此我们要了解的是之前是按照使用修改的角度将迭代器分为普通迭代器、const迭代器、反向迭代器、const反向迭代器其实迭代器还可以从功能的角度分为单向迭代器、双向迭代器、随机迭代器 在此单向迭代器的特点是只支持双向迭代器的特点是只支持和–随机迭代器的特点是支持、–还有和-。经典的单向迭代器有一个叫做forward_list的容器经典的双向迭代器有list经典的随机迭代器有之前我们学习过的string、vector 在此提供算法库内的sort文档就可以看出该sort要传随机迭代器这是由于算法库内的sort的底层是快速排序这就需要支持随机访问因此由于list不是随机迭代器因此就无法使用算法库内的sort 在此我们还要了解到的是以上的三种迭代器其实是继承的关系由于我们还没有学习到C中的继承也可以认为三种迭代器是以下所示的集合关系 那么在了解了以上的知识之后接下来看看list内的sort相比算法库内的sort效率如何呢 在此我们通过以下两段代码来测试 先来看以下这段代码 #includeiostream #includelist #includevector #includealgorithm using namespace std;void test_op1() {srand(time(0));const int N 1000000;listint lt1;listint lt2;vectorint v;for (int i 0; i N; i){auto e rand() i;lt1.push_back(e);v.push_back(e);}int begin1 clock();// 排序sort(v.begin(), v.end());int end1 clock();int begin2 clock();lt1.sort();int end2 clock();printf(vector sort:%d\n, end1 - begin1);printf(list sort:%d\n, end2 - begin2); }int main() {test_op1();return 0; } 以上代码输出结果如下所示在以上代码当中我们使用rand生成100万的整型数据分别存储在vector对象和list对象内之后vector对象调用算法库内的sort进行排序list使用其内部的成员函数sort进行排序之后通过输出的结果就可以看出这两个sort的效率有什么差别  通过输出结果就可以看出算法库内的sort要快不少 再来看以下代码 #includeiostream #includelist #includevector #includealgorithm using namespace std;void test_op2() {srand(time(0));const int N 1000000;listint lt1;listint lt2;for (int i 0; i N; i){auto e rand();lt1.push_back(e);lt2.push_back(e);}int begin1 clock();// 拷贝vectorvectorint v(lt2.begin(), lt2.end());// 排序sort(v.begin(), v.end());// 拷贝回lt2lt2.assign(v.begin(), v.end());int end1 clock();int begin2 clock();lt1.sort();int end2 clock();printf(list copy vector sort copy list sort:%d\n, end1 - begin1);printf(list sort:%d\n, end2 - begin2); }int main() {test_op2();return 0; } 你可能觉得第一段代码的结果还不够直观那么就再来看以上的第二段代码以上代码先比第一段代码的区别是一开始生成的100万个随机数是先存放在list对象内之后再拷贝给vector对象再调用算法库内的sort进行排序。那么这种情况下是不是直接使用list内的sort效率会比拷贝再使用算法库内的sort效率要高呢 接下来来看以上代码的输出结果 通过以上的输出结果就可以看出先拷贝再使用算法库内的sort都比直接使用list内的sort效率要高这就可以说明算法库内的sort是要比list内的sort要高效不少的。因此再之后要使用到大量数据的排序时最好使用vector 9.6 reverse list::reverse - C Reference 在list还提供了逆置list对象元素函数reverse 例如以下示例 #includeiostream #includelist using namespace std;int main() {listint lt1;lt1.push_back(9);lt1.push_back(-2);lt1.push_back(3);lt1.push_back(-10);for (auto x : lt1){cout x ;}cout \n;//lt1.sort(greaterint());lt1.reverse();for (auto x : lt1){cout x ;}cout \n;return 0; } 以上就是本篇的全部内容了接下来在下一篇当中将带来list的模拟实现在模拟实现中我们将感受到list内的迭代器和之前学习的容器的具体不同未完待续……