不知道大家之前是否学习过数据结构中的栈及其相关概念,我们今天要学习的容器就是与数据结构中栈的特点非常相像的stack容器,它是一种先进后出的数据结构,并且只有一个出口。类比生活中的弹匣可以帮助你更好的理解这一种数据结构
基本概念:
stack容器中的各种接口操作与之前所学的容器非常相似,可进行类比学习
示例代码:
void test01()
{
stack<int>s;
//入栈
s.push(10);
s.push(20);
s.push(30);
s.push(40);
cout << "栈的大小:" << s.size() << endl;
//输出:栈的大小:4
//只要栈不为空,查看栈顶,并执行出栈操作
while (!s.empty())
{
//查看栈顶元素
cout << "栈顶元素为:" << s.top() << endl;
//出栈
s.pop();
}
cout << "栈的大小:" << s.size() << endl;
//输出:栈的大小:0
}
//输出:
栈顶元素为:40
栈顶元素为:30
栈顶元素为:20
栈顶元素为:10
总结:入栈push出栈pop,返顶top空empty,点头yes摇头no,来是come去是go,不好意思走错片场了~…总而言之,通过以上的输出结果,结合之前的入栈顺序,就可以很明显地发现stack容器是一种先进后出的数据结构
queue容器和stack容器有个最明显的区别就是——queue是一种先进先出的数据结构,同时它有两个出口。
基本概念:
都大差不差,上代码
示例代码:
void test01()
{
//创建队列
queue<Person>q;
Person p1("唐僧",30);
Person p2("孙悟空",1000);
Person p3("猪八戒",900);
Person p4("沙僧",800);
//入队
q.push(p1);
q.push(p2);
q.push(p3);
q.push(p4);
//判断只要队列不为空,查看队头,查看队尾,出队
while (!q.empty())
{
//查看队头
cout << "队头元素———姓名:" << q.front().m_Name << " 年龄:" << q.front().m_Age << endl;
//查看队尾
cout << "队尾元素———姓名:" << q.back().m_Name << " 年龄:" << q.back().m_Age << endl;
cout<<endl;
//出队
q.pop();
}
cout << "队列大小:" << q.size() << endl;
}
//输出:
队头元素———姓名:唐僧 年龄:30
队尾元素———姓名:沙僧 年龄:800
队头元素———姓名:孙悟空 年龄:1000
队尾元素———姓名:沙僧 年龄:800
队头元素———姓名:猪八戒 年龄:900
队尾元素———姓名:沙僧 年龄:800
队头元素———姓名:沙僧 年龄:800
队尾元素———姓名:沙僧 年龄:800
队列大小:0
总结:各种接口与之前学习的其它容器基本上无异,并且通过输出结果可以很明显发现queue是一种先进先出的数据结构
基本概念:
list的优点:
那么,代价是?
list的缺点:
总结:STL中List和vector是两个最常用被使用的容器,且相互优劣互补
和之前所学容器的构造方式一样,但在学前面几个容器的时候没演示,而list容器又比较重要,怕大家忘记,在此还是进行一下演示吧~
示例代码:
void printList(const list<int>&L)
{
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << (*it) << " ";
}
cout << endl;
}
void test01()
{
list<int>L1;
//添加数据
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
//遍历容器
printList(L1);
//输出:10 20 30 40
1、区间方式构造
list<int>L2(L1.begin(), L1.end());
printList(L2);
//输出:10 20 30 40
2、拷贝构造
list<int>L3(L2);
printList(L3);
//输出:10 20 30 40
3、n个elem构造
list<int>L4(10,250);
printList(L4);
//输出:250 250 250 250 250 250 250 250 250 250
}
别无二致
void test01()
{
//创建list容器
list<int>L1;
//添加数据
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
//输出:10 20 30 40
1、operator= 赋值
list<int>L2;
L2 = L1;
printList(L2);
//输出:10 20 30 40
2、区间赋值
list<int>L3;
L3.assign(L2.begin(), L2.end());
printList(L3);
//输出:10 20 30 40
3、n个elem赋值
list<int>L4;
L4.assign(10, 100);
printList(L4);
//输出:100 100 100 100 100 100 100 100 100 100
}
void test02()
{
list<int>L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
list<int>L2;
L2.assign(10, 100);
cout << "交换前:" << endl;
printList(L1);
printList(L2);
//输出:10 20 30 40
//输出:100 100 100 100 100 100 100 100 100 100
1、swap函数进行交换
L1.swap(L2);
cout << "交换后:" << endl;
printList(L1);
printList(L2);
//输出:100 100 100 100 100 100 100 100 100 100
//输出:10 20 30 40
}
依旧如故
void test01()
{
//创建list容器
list<int>L1;
//添加数据
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
//输出:10 20 30 40
//判断容器是否为空
if (L1.empty())
{
cout << "L1为空" << endl;
}
else
{
cout << "L1不为空" << endl;
cout << "L1元素个数为:"<<L1.size() << endl;
//输出:L1元素个数为:4
}
1、重新指定大小
L1.resize(10, 100);
printList(L1);
//输出:10 20 30 40 100 100 100 100 100 100
L1.resize(2);
printList(L1);
//输出:10 20
}
一点点不一样
void test01()
{
//创建list容器
list<int>L1;
//头插
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
//尾插
L1.push_front(100);
L1.push_front(200);
L1.push_front(300);
printList(L1);
//输出:300 200 100 10 20 30
//尾删
L1.pop_back();
printList(L1);
//输出:300 200 100 10 20
//头删
L1.pop_front();
printList(L1);
//输出:200 100 10 20
//insert插入
L1.insert(L1.begin(), 1000);
printList(L1);
//输出:1000 200 100 10 20
//insert迭代器偏移插入
list<int>::iterator it = L1.begin();
L1.insert(++it,500);
printList(L1);
//输出:1000 500 200 100 10 20
//迭代器偏移删除
it = L1.begin();
L1.erase(++it);
printList(L1);
//输出:1000 200 100 10 20
//先插入,后移除
L1.push_back(10000);
L1.push_back(10000);
L1.push_back(10000);
printList(L1);
//输出:1000 200 100 10 20 10000 10000 10000
//移除所有与其匹配的数据
L1.remove(10000);
printList(L1);
//输出:1000 200 100 10 20
//clear清空
L1.clear();
printList(L1);
//输出:
}
总结:稍许不一样的地方就是remove函数会移除所有与其匹配的数据,别的操作把单词记住并明确其释义就能掌握
list容器中的数据不可用[]和at()的方式进行随机访问,原因是list的本质是链表,不是用连续线性空间存储的,并且迭代器也是不支持随机访问的
示例代码:
void test01()
{
list<int>L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
1、list容器中提供front()来访问第一个元素
cout << "第一个元素为:" << L1.front() << endl;
//输出:10
2、list容器中提供back()来访问最后一个元素
cout << "最后一个元素为:" << L1.back() << endl;
//输出:40
那么如何验证迭代器是否是支持随机访问的呢?其实不用去死记硬背,在此提供一个小技巧来判断~
示例代码:
list<int>::iterator it = L1.begin();
//迭代器若支持以下写法,编译器不报错,则说明迭代器支持随机访问
it = it + 1;
//迭代器对于以下两种写法若都支持,则说明迭代器支持双向
//迭代器若只支持以下两种写法其中之一的,说明仅支持单向
it++;
it--;
总结:明确List容器的数据访问方式和之前所学容器不同的底层逻辑即可轻松记忆,同时对于迭代器是否支持随机访问这个知识点也不用死记硬背,用以上的示例代码即可轻松验证~
list容器中,反转操作与之前的容器一样,但排序操作则完全不一样,这是由于List容器的迭代器不支持随机访问而造成的
反转示例代码:
void test01()
{
//创建list容器
list<int>L1;
//头插
L1.push_back(20);
L1.push_back(10);
L1.push_back(50);
L1.push_back(40);
L1.push_back(30);
cout << "反转前:" << endl;
printList(L1);
//输出:20 10 50 40 30
//反转
L1.reverse();
cout << "反转后:" << endl;
printList(L1);
//输出:30 40 50 10 20
}
排序示例代码:
//降序排序回调函数
//注意:传入数据类型应与容器数据类型保持一致
bool myCompare(int val1, int val2)
{
return val1 > val2;
}
void test02()
{
//创建list容器
list<int>L2;
//头插
L2.push_back(20);
L2.push_back(10);
L2.push_back(50);
L2.push_back(40);
L2.push_back(30);
//排序
cout << "排序前:" << endl;
printList(L2);
//输出:20 10 50 40 30
1、升序排序(默认排序规则)
L2.sort();
cout << "升序排序后:" << endl;
printList(L2);
//输出:10 20 30 40 50
2、降序排序
L2.sort(myCompare);
cout << "降序排序后:" << endl;
printList(L2);
//输出:50 40 30 20 10
}
总结:无他,记忆即可
栈是沉入深海的锚,压着未解的逻辑层层堆叠;队列是候鸟迁徙的轨迹,数据循着队尾的羽翼向北方迁徙;链表是深秋的爬山虎,每个节点都攥着下一寸生长的脉络…不知道接下来的容器学习,又会带给我怎样的感受呢?