序列式容器-vector/deque/list
1.序列式容器-vector/queue/list三种主要构造方式与四种遍历方式。
知识点:
1.三种容器的初始化方式都是一样的
2.vector/deque可以支持下标运算符访问,list不允许,list容器里面没有重载[]运算符。
vector容器的三种构造函数
vector容器构造函数:--plus deque和list,构造函数源码基本差不多。
1.vector( size_type count,
const T& value,//初始化count个value,如果value不写,默认为0
const Allocator& alloc = Allocator());//空间适配器2.迭代器返回,左闭右开,迭代器--->广义的指针
template< class InputIt > //迭代器方式进行初始化
vector( InputIt first, InputIt last,
const Allocator& alloc = Allocator() );3.大括号形式进行初始化构造
vector( std::initializer_list<T> init,//给出初始化列表
const Allocator& alloc = Allocator() );
vector容器的4种遍历方式
1.数组下标形式--不支持list
2.使用迭代器-->广义指针
3.auto自动推导迭代器形式
4.通用遍历形式:auto &elem;//用elem去操作迭代器里面的数据
ps:可以把vector全部改成deque或者list,改成list的时候需要把数组下标形式注释掉,list不支持下标访问
//1.vector容器的构造和遍历
void test01()
{
//1.初始化count个value
vector<int> num(10,1);//10个1
vector<int> num1(10);//value默认为0
//2.以迭代器的方式进行初始化
int p[10] = {1,2,3,4,5,6,7,8,9,10};
vector<int> num3(p,p+10);//左闭右开
//3.以列表形式进行初始化
vector<int>num4 = {10,100,1000,9,99,999,1,11,111,20};
//1.第一种遍历方式:数组下标形式
for(int i=0;i<num.size();i++)
{
cout<<num[i]<<" ";
}//输出结果:1 1 1 1 1 1 1 1 1 1
cout<<endl;
//2.第二种遍历方式:以迭代器的方式进行遍历
for(vector<int>::iterator it=num3.begin();it!=num3.end();it++)
{
cout<<*it<<" ";
}//1 2 3 4 5 6 7 8 9 10
cout<<endl;
//3.第三种遍历方式:auto自动推导迭代器类型
for(auto it = num3.begin();it!=num3.end();it++)
{
cout<<*it<<" ";
}//1 2 3 4 5 6 7 8 9 10
//4.auto&通用类型
cout<<endl;
for(auto &elem: num4)
{
cout<<elem<<" ";
}
//10 100 1000 9 99 999 1 11 111 20
cout<<endl;
}
运行结果
1 1 1 1 1 1 1 1 1 1 //数组下标方式进行遍历
1 2 3 4 5 6 7 8 9 10 //迭代器方式
1 2 3 4 5 6 7 8 9 10 //auto自动推导迭代器类型
10 100 1000 9 99 999 1 11 111 20 //auto通用引用类型遍历
2.序列式容器-vector/deque/list的插入和删除
1.vector/deque/list尾插和尾删结果都一样的
-pushback(val)
-pop_back()
2.deque/list支持头插头删,结果一样,vector不支持,由于vector在头部进行插入的话,后面的元素需要总体往后移,时间复杂度是O(n),因此没有提供头部插入的接口。
3.可以用一个通用打印函数模板来对三者的打印。
#include <iostream>
using namespace std;
#include <vector>
#include <deque>
#include <list>
template <typename T>
void print(const T &con)
{
for(auto &elem:con)
{
cout<<elem<<" ";
}
cout<<endl;
}
void test01()
{
vector<int> number = {1,2,3,4,56,7,7,8,8,13};
print(number);//1 2 3 4 56 7 7 8 8 13
cout<<"vector尾部插入20和30:"<<endl;
number.push_back(20);
number.push_back(30);
print(number);
cout<<"vector删除尾部元素:"<<endl;
number.pop_back();
print(number);
}
void test02()
{
list<int> number = {1,2,3,4,56,7,7,8,8,13};
cout<<"原列表:";
print(number);//1 2 3 4 56 7 7 8 8 13
cout<<"list尾部插入20和30:"<<endl;
number.push_back(20);
number.push_back(30);
print(number);
cout<<"list删除尾部元素:"<<endl;
number.pop_back();
print(number);
cout<<"list头部插入元素:"<<endl;
number.push_front(999);
number.push_front(888);
print(number);
cout<<"list头部删除元素:"<<endl;
number.pop_front();
print(number);
}
void test03()
{
deque<int> number = {1,2,3,4,56,7,7,8,8,13};
cout<<"原队列:";
print(number);//1 2 3 4 56 7 7 8 8 13
cout<<"deque尾部插入20和30:"<<endl;
number.push_back(20);
number.push_back(30);
print(number);
cout<<"deque删除尾部元素:"<<endl;
number.pop_back();
print(number);
cout<<"deque头部插入元素:"<<endl;
number.push_front(999);
number.push_front(888);
print(number);
cout<<"deque头部删除元素:"<<endl;
number.pop_front();
print(number);
}
int main()
{
test01();
cout<<endl<<endl;
test02();
cout<<endl<<endl;
test03();
return 0;
}
运行结果
1 2 3 4 56 7 7 8 8 13
vector尾部插入20和30:
1 2 3 4 56 7 7 8 8 13 20 30
vector删除尾部元素:
1 2 3 4 56 7 7 8 8 13 20
原列表:1 2 3 4 56 7 7 8 8 13
list尾部插入20和30:
1 2 3 4 56 7 7 8 8 13 20 30
list删除尾部元素:
1 2 3 4 56 7 7 8 8 13 20
list头部插入元素:
888 999 1 2 3 4 56 7 7 8 8 13 20
list头部删除元素:
999 1 2 3 4 56 7 7 8 8 13 20
原队列:1 2 3 4 56 7 7 8 8 13
deque尾部插入20和30:
1 2 3 4 56 7 7 8 8 13 20 30
deque删除尾部元素:
1 2 3 4 56 7 7 8 8 13 20
deque头部插入元素:
888 999 1 2 3 4 56 7 7 8 8 13 20
deque头部删除元素:
999 1 2 3 4 56 7 7 8 8 13 20
3.细谈vector
3.1 vector的底层实现有多少个指针?
1.vector里面有两个函数,一个是size(),一个capacity();
2.vector有个头指针,一个尾指针,size()大小就是两指针的差值;
3.还有个指针指向capacity,用来进行扩容。
sizeof(vector<类型>)=24;//三个指针大小24
3.2 at和下标访问有什么区别?
用下标访问,有越界的风险,不安全;at加了一个异常检查,会抛出异常。
3.3 打印第一个元素的地址的方式
&number;//这返回的是个迭代器
&number[0];
&*number.begin();
int *pdata = number.data();
3.4 vector中insert扩容原理
1.vector在进行insert的时候,每次都需要重写置位迭代器的位置,因为有可能因为进行扩容,之前的迭代器失效了。
2.deque在进行insert范围插入的时候,会去判断是往前面插入还是往后面进行插入,根据size()/2的大小来判断。
3.list的插入是以链表形式
vector insert扩容原理
1.push_back可以进行size()的两倍进行扩容,因为每次只插入一个元素。
2.insert插入的元素是不确定的,进行两倍扩容就不合适了insert的元素个数为t;
1.t <= capacity-size,此时不会扩容
2.capacity - size<t<size时,按照2*size进行扩容
3.capacity-size<t,size<t,按照size+t个数进行扩容
4.capacity-size<t, capacity<t; 按照size+t扩容
/*
vector insert扩容原理
1.push_back可以进行size()的两倍进行扩容,因为每次只插入一个元素。
2.insert插入的元素是不确定的,进行两倍扩容就不合适了
insert的元素个数为t;
1.t <= capacity-size,此时不会扩容
2.capacity - size<t<size时,按照2*size进行扩容
3.capacity-size<t,size<t,按照size+t个数进行扩容
4.capacity-size<t, capacity<t; 按照size+t扩容
*/
#include <iostream>
using namespace std;
#include <vector>
template <class T>
void Print(T &val)
{
for(auto &elem: val)
{
cout<<elem<<" ";
}
cout<<endl;
}
template <class T>
void Show_CapacityAndSize(T &number)
{
Print(number);
cout<<"capacity:c = "<<number.capacity()<<endl;
cout<<"size: s = "<<number.size()<<endl;
cout<<endl;
}
void test01()
{
vector<int> number = {1,3,5,7,9,11,13,15,18};
Show_CapacityAndSize(number);
number.push_back(14);
number.push_back(999);
Show_CapacityAndSize(number);
//push_back每次插入一个元素,可以按照2*size的方式进行扩容
cout<<"使用insert进行插入,查看insert扩容原理:"<<endl;
auto it = number.begin();
it++;
it++;
cout<<"*it="<<*it<<endl;
cout<<"1.插入个数t<=capacity-size:空间足够,不会扩容:"<<endl;
it = number.insert(it,20);
it = number.insert(it,68);
Show_CapacityAndSize(number);//capacity = 18,size=13,t=2
cout<<"2.t:capacity - size< t < size:插入个数大于当前可存储的空间,小于size:"
<<endl<<"按照2*size进行扩容."<<endl;
it = number.insert(it,8,11);
Show_CapacityAndSize(number);//size=13+8=21; capacity=2*13=26,t = 8
cout<<"3.capacity-size<t且t>capacity或者t>size情况:"
<<endl<<"按照t+size方式进行扩容。"<<endl;
cout<<"插入个数:t>size,扩容方式:t+size:"<<endl;
it = number.insert(it,24,7);
cout<<"*it="<<*it<<endl;//t>size的情况
Show_CapacityAndSize(number);//size = 24+21=45,capacity =21+24=45,t=24
cout<<"插入个数:t>capacity,扩容方式:t+size:"<<endl;
it = number.insert(it,80,90);//size = 45+80=125=capacity,t = 80
Show_CapacityAndSize(number);
}
int main()
{
test01();
return 0;
}
运行结果
1 3 5 7 9 11 13 15 18
capacity:c = 9
size: s = 91 3 5 7 9 11 13 15 18 14 999
capacity:c = 18
size: s = 11使用insert进行插入,查看insert扩容原理:
*it=5
1.插入个数t<=capacity-size:空间足够,不会扩容:
1 3 68 20 5 7 9 11 13 15 18 14 999
capacity:c = 18
size: s = 132.t:capacity - size< t < size:插入个数大于当前可存储的空间,小于size:
按照2*size进行扩容.
1 3 11 11 11 11 11 11 11 11 68 20 5 7 9 11 13 15 18 14 999
capacity:c = 26
size: s = 213.capacity-size<t且t>capacity或者t>size情况:
按照t+size方式进行扩容。
插入个数:t>size,扩容方式:t+size:
*it=7
1 3 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 11 11 11 11 11 11 11 11 68 20 5 7 9 11 13 15 18 14 999
capacity:c = 45
size: s = 45插入个数:t>capacity,扩容方式:t+size:
1 3 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 11 11 11 11 11 11 11 11 68 20 5 7 9 11 13 15 18 14 999
capacity:c = 125
size: s = 125
3.5 vetor中的erase、clear()、和shrink_to_fit()
1.使用erase删除对应元素的迭代器有个坑,需要避免
2.clear()和shrink_to_fit()配合时候回收vector空间
3.vector中的clear()/shrink_to_fit()和deque容器的使用方法是一样的,list中只有clear(),在clear()后,内存已被回收。
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
void print(const T &con)
{
for(auto &elem: con)
{
cout<<elem<<" ";
}
cout<<endl;
}
void test01()
{
vector<int> number = {1,3,6,6,5,7};
print(number);
for(auto it=number.begin();it!=number.end();it++)
{
if(6==*it)
{
number.erase(it);//以迭代器的方式来删除
//只会删除第一个6,为什么?
//迭代器被删除后,后面一个6的迭代器顶替了当前被删除的元素
//it进行++,因此有个6没被删除
//这样进行删除是有问题的
it--;//加个这个可以解决
}
}
print(number);
cout<<endl<<"vector元素清空:"<<endl;
number.clear();//只是清空vector中的数据,没有回收内存
cout<<"number.size()="<<number.size()<<endl;//0
cout<<"number.capacity()="<<number.capacity()<<endl;//6
number.shrink_to_fit();//使用shrink_to_fit回收内存
cout<<"使用shrink_to_fit()回收空间"<<endl;
cout<<"number.size()="<<number.size()<<endl;//0
cout<<"number.capacity()="<<number.capacity()<<endl;//0
}
//改进
void test02()
{
vector<int> number = {1,3,6,6,5,7};
print(number);
for(auto it=number.begin();it!=number.end();)
{
if(6==*it)
{
it = number.erase(it);//以迭代器的方式来删除
//只会删除第一个6,为什么?
//迭代器被删除后,后面一个6的迭代器顶替了当前被删除的元素
//it进行++,因此有个6没被删除
//it--;//这样进行删除是有问题的
}
else
{
++it;
}
}
print(number);
}
int main()
{
test01();
//test02();
return 0;
}
/*
1 3 6 6 5 7
1 3 5 7
vector元素清空:
number.size()=0
number.capacity()=6
使用shrink_to_fit()回收空间
number.size()=0
number.capacity()=0
*/
3.6 vector/deque/list emplace_back的使用
往容器尾部进行插入,识别右值
template< class... Args >
void emplace_back( Args&&... args );
#include <iostream>
#include <list>
using namespace std;
#include <vector>
class Point
{
public:
Point(int ix=0,int iy = 0)
:_ix(ix),_iy(iy)
{}
void print()const
{
cout<<_ix<<","<<_iy<<endl;
}
private:
int _ix;
int _iy;
};
void test01()
{
vector<Point> p1;
p1.emplace_back(Point(1,2));
auto it = p1.begin();
it->print();
list<Point> l1;
l1.emplace_back(Point(2,3));
l1.begin()->print();
}
int main()
{
test01();
return 0;
}
/*
1,2
2,3
*/
4.list操作
1.sort()排序:
1.number.sort(std::less<int>());//从小到大
2.number.sort(std::greater<int>()); //从大到小
3.number.sort(CompareList<int>()); //自定义仿函数
2.reverse();链表反转3.unique()去重:需要首先对list进行排序,再进行去重。
4.number1.merge(number2);//1.对已排好序的两个链表进行合并,两个链表有序(小->大),合并后的结果才会有序
//2.合并后,number2链表被清空
//3.对于两个从大到小的进行排序的列表进行排序,想要合并后的列表还是从大到小有 序,需要传入自定义Compare函数或者直接调用标准库函数
Merge接口参考:
void merge( list& other );
(1)
void merge( list&& other );
(1) (since C++11)
template <class Compare>
void merge( list& other, Compare comp );
(2)
template <class Compare>
void merge( list&& other, Compare comp );
#include <iostream>
#include <list>
using namespace std;
template <typename T>
void print(const T &con)
{
for(auto &elem: con)
{
cout<<elem<<" ";
}
cout<<endl;
}
//函数对象(仿函数)
template <typename T>
struct CompareList
{
bool operator()(const T &lhs,const T &rhs)const
{
return lhs>rhs;
}
};
void test01()
{
list<int> number = {1,212,12,3,413,56,87,12,43,21,42};
print(number);
cout<<"对list进行排序:"<<endl;
//number.sort();
//print(number);
// number.sort(less<int>());
// print(number);
// number.sort(less<int>());
// print(number);
number.sort(CompareList<int>());
print(number);
cout<<"list反转:"<<endl;
number.reverse();
print(number);
cout<<"number.unique()去重:"<<endl;
number.unique();//去重只针对排序好的list有效,如果没排好序,不能完全去重
print(number);
cout<<"merge测试:"<<endl;
list<int> number2 = {2,22,3,33,4, 44,5,55,6,66,7,77};
number2.sort();
//number.merge(number);
number.merge(number2);
print(number);
print(number2);//此时number2为空了
}
void test02()//合并两个从大到小的链表,合并后的链表还是从大到小排序
{
list<int> number = {1,212,12,3,413,56,87,12,43,21,42};
list<int> number2 = {2,22,3,33,4, 44,5,55,6,66,7,77};
number.sort(greater<int>());
number2.sort(greater<int>());
cout<<"合并两个从大到小排序的链表:number.merge(number2,greater<int>()):"<<endl;
number.merge(number2,greater<int>());
print(number);
}
int main()
{
test01();
cout<<endl;
test02();
return 0;
}
/*
1 212 12 3 413 56 87 12 43 21 42
对list进行排序:
413 212 87 56 43 42 21 12 12 3 1
list反转:
1 3 12 12 21 42 43 56 87 212 413
number.unique()去重:
1 3 12 21 42 43 56 87 212 413
merge测试:
1 2 3 3 4 5 6 7 12 21 22 33 42 43 44 55 56 66 77 87 212 413
合并两个从大到小排序的链表:number.merge(number2,greater<int>()):
413 212 87 77 66 56 55 44 43 42 33 22 21 12 12 7 6 5 4 3 3 2 1
*/
5.list: splice:把一个链表的元素移到另外一个链表。
接口参考:
void splice( const_iterator pos, list& other );
(1)
void splice( const_iterator pos, list&& other );
(1) (since C++11)
void splice( const_iterator pos, list& other, const_iterator it );
(2)
void splice( const_iterator pos, list&& other, const_iterator it );
(2) (since C++11)
void splice( const_iterator pos, list& other,
const_iterator first, const_iterator last);//按范围取
(3)
void splice( const_iterator pos, list&& other,
const_iterator first, const_iterator last );
#include <iostream>
#include <list>
using namespace std;
template <class T>
void show_Value(T &con)
{
for(auto &elem: con)
{
cout<<elem<<" ";
}
cout<<endl;
}
void test01()
{
list<int> number1 = {8,12,3,45,6,123};
list<int> number3 = {33,44,66,99,11};
auto it = number1.begin();
it++;
number1.splice(it,number3);
show_Value(number1);//8 33 44 66 99 11 12 3 45 6 123
show_Value(number3);//空
}
void test02()
{
list<int> number1 = {1,2,3,4,5,6,7,8};
list<int> number2 = {2,4,6,8,10,12,14};
auto it1 = number1.begin();
it1++;//2
auto it2 = number2.begin();
it2++;//4的迭代器
it2++;//6的迭代器
number1.splice(it1, number2,it2);//把number2 迭代器it2插入到number1 it1的位置
show_Value(number1);//1 6 2 3 4 5 6 7 8
show_Value(number2);// 4 8 10 12 14
}
void test03()
{
list<int> number1 = {1,2,3,4,5,6,7,8};
list<int> number2 = {2,4,6,8,10,12,14};
auto it1 = number1.begin();
it1++;//2
auto it2 = number2.begin();
it2++;//4的迭代器
it2++;//6的迭代器
auto it_end = number2.end();
//it_end--;
number1.splice(it1,number2,it2,it_end);//左闭右开,number2[it2,end)插入到number1
show_Value(number1);//1 6 8 10 12 14 2 3 4 5 6 7 8
show_Value(number2);//2 4
}
int main()
{
//test01();
//test02();
test03();
return 0;
}