STL常用容器
3.5 stack容器
3.5.1stack基本概念
stack(栈)是一种先进后出(First In Last Out,FILO)的数据结构,只有一个出口。
栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为。
把栈顶的元素拿出来,才可以访问到下一个元素,但是遍历是不会对数据内容发生修改的。
但是栈可以判断容器是否为空,并且可以返回元素个数(在入栈的时候记录)。
栈中进入数据称为入栈(push),弹出数据称为出栈(pop)。
比如弹匣、地铁乘客上下顺序等都属于栈。
3.5.2 stack常用接口
构造函数:
stack<T> stk; // 默认构造
stack(const stack &stk); // 拷贝构造
赋值操作
stack & operator=(const stack&stk); // 重载等号赋值
数据存取:
push(elem); // 栈顶添加元素
pop(); // 栈顶移除第一个元素
top(); // 返回栈顶元素
大小操作:
empty(); // 判断堆栈是否为空
size(); // 返回栈的大小
示例:
#include<iostream>
using namespace std;
#include<stack>
void test01()
{
stack<int> stk;
stk.push(10);
stk.push(20);
stk.push(30);
stk.push(40);
cout << "栈的大小为:" << stk.size() << endl;
// 查看栈中的数据 而不是遍历
while(!stk.empty())
{
cout << stk.top() << " ";
stk.pop();
}
cout << endl;
cout << "栈的大小为:" << stk.size() << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
3.6 queue容器
3.6.1 queue基本概念
queue(队列)是一种先进先出(First In First Out, FIFO)的数据结构,有两个出口。
队尾只能输入数据,入数据的过程称之为入队(push);队头只能输出数据,出数据的过程称之为出队(pop)。
只有队头和队尾的元素能被外界访问,因此也不允许遍历。
同样可以判断队列是否为空和返回队列的大小。
3.6.2 常用接口
构造函数:
queue<T> q; // 默认构造
queue(const queue &que); // 拷贝构造
赋值操作
queue & operator=(const queue&que); // 重载等号赋值
数据存取:
push(elem); // 队列尾部添加元素
pop(); // 队头移除第一个元素本身,不是迭代器
back(); // 返回最后一个元素
front(); // 返回第一个元素
大小操作:
empty(); // 判断队列是否为空
size(); // 返回队列的大小
示例:
#include<iostream>
using namespace std;
#include<queue>
#include<string>
class Person
{
public:
Person(string name,int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
void test01()
{
queue<Person> q;
// 准备数据
Person p1("赵",10);
Person p2("钱",20);
Person p3("孙",30);
Person p4("李",40);
// 入队
q.push(p1);
q.push(p2);
q.push(p3);
q.push(p4);
cout << "queue容器的大小为:" << q.size() << endl;
// 查看队头和队尾,并执行出队
while(!q.empty())
{
// 查看队头
cout <<"队头:"<< q.front().m_Name<< " "
<< q.front().m_Age<<" ";
// 查看队尾
cout <<"队尾:"<< q.back().m_Name<< " "
<< q.back().m_Age<<endl;
q.pop();
}
cout << "queue容器的大小为:" << q.size() << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
3.7 list容器
3.7.1 list基本概念
功能:将数据进行链式存储
链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。链表由一系列结点组成,结点由一个存储数据元素的数据域和一个存储下一个结点的指针域组成。
链表的优点是可以对任意位置快速插入或删除元素。缺点是遍历速度没有数组快,而且占用的空间更大。
STL中的链表是一个双向循环链表。
因为链表的存储方式不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器。
List优点:
动态存储分配,不会造成内存浪费和溢出
链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素。
List缺点:
链表灵活,但是空间(指针域)和时间(遍历)额外耗费较大
List的一个重要性质是,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector中是不成立的。
Vector中,当容量满了之后,需要开辟新的空间,这个时候迭代器指向的还是原来的位置,已经失效了,但是在list是不会这样的。
3.7.2 list构造函数
功能:创建list容器
函数原型:
list<T> lst; // 默认构造
list(beg,end); // 构造函数将区间[beg,end)之间的元素拷贝给本身
list(n,elem); // n个elem
list(const list &lst); // 拷贝构造
示例:
#include<iostream>
using namespace std;
#include<list>
void printList(const list<int> &L)
{
for(list<int>::const_iterator lit =L.begin();lit!=L.end();lit++)
{
cout << *lit << " ";
}
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);
//区间构造
list<int> L2(L1.begin(),L1.end());
printList(L2);
// n个elem
list<int> L3(3,33);
printList(L3);
// 拷贝构造
list<int> L4(L3);
printList(L4);
}
int main()
{
test01();
system("pause");
return 0;
}
执行结果:
3.7.3 list赋值和交换
功能:给list容器进行赋值,以及交换list容器
函数原型:
assign(beg,end);
assign(n,elem);
list& operator=(const list &lst);
swap(lst);
示例:
#include<iostream>
using namespace std;
#include<list>
void printList(const list<int> &L)
{
for(list<int>::const_iterator lit=L.begin();lit!=L.end();lit++)
{
cout << *lit << " ";
}
cout << endl;
}
void test01()
{
list<int> L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
cout << "L1:";
printList(L1);
// 等号赋值
list<int> L2;
L2 = L1;
cout << "L2:";
printList(L2);
// assign赋值
list<int> L3;
L3.assign(L2.begin(),L2.end());
cout << "L3:";
printList(L3);
list<int> L4;
L4.assign(4,44);
cout << "L4:";
printList(L4);
// 交换
cout << "交换L4和L1"<<endl;
L4.swap(L1);
cout << "L4:";
printList(L4);
cout << "L1:";
printList(L1);
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
3.7.4 list大小操作
函数原型
empty(); // 判断容器是否为空,空返回true,非空返回false
size(); // 返回容器中元素的个数(大小,当前存放数据个数)
resize(int num); // 重新指定容器的长度为num
// 若容器变长,则以默认值0填充新位置;如果变短,则末尾超出容器长度的元素被删除
resize(int num,elem); // 重新指定容器的长度为num
// 若容器变长,则以elem填充新位置;如果变短,则末尾超出容器长度的元素被删除
示例:
include<iostream>
using namespace std;
#include<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;
for(int i=0;i<5;i++)
{
L1.push_back(i);
}
printList(L1);
if(L1.empty())
cout << "容器为空!"<<endl;
else
cout << "容器不为空,容器的大小为:" << L1.size()<< endl;
// 重新指定大小
L1.resize(10);
printList(L1);
L1.resize(15,1);
printList(L1);
L1.resize(3);
printList(L1);
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
3.7.5 list 插入和删除
函数原型:
需要注意的是位置是通过迭代器给定的。
push_back(elem); // 在容器尾部添加一个数据
push_front(elem); // 在容器头部插入一个数据
pop_back(); // 删除容器最后一个数据
pop_front(); // 删除容器第一个数据
insert(pos,elem); // 在pos位置插入一个elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem); // 在pos位置插入n个elem数据,无返回值
insert(pos,beg,end); // 在pos位置插入[beg,end)区间的数据,无返回值
clear(); // 清空容器的所有数据
erase(beg,end); // 删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos); // 删除pos位置的数据,返回下一个数据的位置。
remove(elem); // 删除容器中所有与elem值匹配的元素
示例:
#include<iostream>
using namespace std;
#include<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> L;
//尾插
L.push_back(10);
L.push_back(20);
L.push_back(30);
//头插
L.push_front(100);
L.push_front(200);
L.push_front(300);
printList(L);
// 尾删
L.pop_back();
printList(L);
// 头删
L.pop_front();
printList(L);
//insert 插入
list<int>::iterator it = L.begin();
it++;
L.insert(it,3,33);
printList(L);
// 删除
it = L.begin();
L.erase(it);
printList(L);
// 移除
L.remove(33);
printList(L);
L.clear();
printList(L);
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
3.7.6 list数据存取
函数原型:
front(); // 返回第一个元素
back(); // 返回最后一个元素
这里没有了at
和[]
的相关操作,这是因为list是链表,在空间上是不连续的。而且 list的迭代器也不支持跳跃式(随机)访问,只能前移或者后移。但是可以通过多次++
或--
,使迭代器偏移来访问其他元素。
示例:
#include<iostream>
using namespace std;
#include<list>
void test01()
{
list<int> L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
cout << "第一个元素为:" << L1.front() << endl;
cout << "最后一个元素为:" << L1.back() << endl;
// 验证迭代器不支持随机访问
list<int>::iterator it = L1.begin();
//it = it + 1; // 报错!不支持随机访问,可以+1,就可以+3(跳跃式)
it++;
}
int main()
{
test01();
system("pause");
return 0;
}
3.7.7 list反转和排序
函数原型:
reverse(); // 反转链表
sort(); // 链表排序
示例
#include<iostream>
using namespace std;
#include<list>
void printList(const list<int> &L)
{
for(list<int>::const_iterator it = L.begin();it!=L.end();it++)
{
cout << *it << " ";
}
cout << endl;
}
// 降序 == 回调函数
// 形参列表的数据类型时list容器中存放的数据类型
bool myCompare(int v1,int v2)
{
// 降序就是v1 > v2,即满足v1>v2时,返回true,否则返回false
return v1>v2;
}
void test01()
{
list<int> L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
printList(L1);
// === 反转 ===
L1.reverse();
printList(L1);
// === 排序 --- 默认是升序
L1.push_back(100);
L1.push_front(200);
printList(L1);
//sort(L1.begin(),L1.end());
// 所有不支持随机访问的迭代器的容器,不可以用标准算法(全局函数)
// 不支持随机访问的迭代器的容器,内部会对应提供一些算法(成员函数)
L1.sort();
printList(L1);
// === 降序 ===
// 自己写一个函数myCompare传给sort即可
L1.sort(myCompare);
printList(L1);
}
int main()
{
test01();
system("pause");
return 0;
}
执行结果:
3.7.8 排序案例
案例描述:将person自定义数据类型进行排序,person的属性包括姓名、年龄、身高
排序规则:按照年龄进行升序,如果年龄相同,按照身高进行降序
/* === list容器排序案例 --- 自定义数据类型排序 ===
案例描述:将person自定义数据类型进行排序,person的属性包括姓名、年龄、身高
排序规则:按照年龄进行升序,如果年龄相同,按照身高进行降序 */
#include<iostream>
using namespace std;
#include<list>
class Person
{
public:
Person(string name, int age, int height)
{
this->m_Name = name;
this->m_Age = age;
this->m_Height = height;
}
string m_Name; // 姓名
int m_Age; // 年龄
int m_Height; // 身高
};
void printList(const list<Person> &L)
{
for (list<Person>::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << "姓名:" << (*it).m_Name << " "
<< "年龄:" << it->m_Age << " "
<< "身高:" << it->m_Height << endl;
}
}
// 指定排序规则
bool comparePerson(Person &p1, Person& p2)
{
if (p1.m_Age == p2.m_Age)
return p1.m_Height > p2.m_Height; // 身高降序
else
return p1.m_Age < p2.m_Age; // 年龄升序
}
int main()
{
// 创建容器
list<Person> L;
// 准备数据
Person p1("刘备", 20, 160);
Person p2("关羽", 19, 170);
Person p3("张飞", 18, 168);
Person p4("曹操", 20, 170);
Person p5("赵云", 19, 160);
Person p6("孙权", 22, 170);
// 插入数据
L.push_back(p1);
L.push_back(p2);
L.push_back(p3);
L.push_back(p4);
L.push_back(p5);
L.push_back(p6);
printList(L);
// 排序
cout << "=============================" << endl;
cout << "排序后:" << endl;
//L.sort();
//自定义数据数据类型排序需要通过回调函数指定排序规则
L.sort(comparePerson);
printList(L);
system("pause");
return 0;
}
执行结果: