一.string容器
1.string理解
string
是C++中风格的字符串,在C语言中并没有string的内置数据类型,这是因为string
的本质是一个类。
我们知道在C语言中我们使用char []
来存储一个字符串,其实也就是char *
来管理字符串,他的本质是一个指针。
C++中string
是一个类,类内部封装了char *
的成员属性,管理这个字符串,是一个char *
型的容器。
特点:
string 类内部封装了很多成员方法:
例如: 查找find,拷贝copy,删除delete 替换replace,插入insert
string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
2.string构造函数
c++
string(); //无参构造,创建一个空字符串
string(const char* s); //把C语言风格的字符串转换成C++风格
string(const string& str); //拷贝构造
string(int n,char c); //用n个字符c初始化
string
构造函数灵活多用,根据实际情况选择。
3.字符串赋值操作
c++
string& operator=(const char* s); //把char*类型字符串赋值给当前字符串
string& operator=(const string &s); //类似于拷贝构造
string& operator=(char c); //可以把一个字符赋值给当前字符串
string& assign(const char*s); //成员函数,赋值
string& assign(const char*s,int n); //成员函数重载,把字符串s前n个字符赋值给当前字符串
string& assign(const string &s); //成员函数重载,类似于拷贝构造
string& assign(int n,char c); //用n个字符c赋值给当前字符串
上面部分基于运算符=
重载,下面基于叫assign
的成员函数。
string的赋值方式很多,operator=
这种方式是比较实用的。
4.字符串拼接
c++
string& operator+=(const char* str); //重载+=,当前字符串后拼接一个C语言风格字符串
string& operator+=(const char c); //重载+=,当前字符串后可以拼接单个字符
string& operator+=(const string& str); //重载+=,当前字符串后面拼接另一个字符串
string& append(const char* s); //成员函数,当前字符串后拼接一个C语言风格字符串
string& append(const char* s,int n); //成员函数,把字符串s前n个字符连接到当前字符串后面
string& append(const string &s); //成员函数,当前字符串后面拼接另一个字符串
string& append(const string &s,int pos,int n); //成员函数,把字符串s从pos开始的n个字符连接到当前字符串结尾
上面部分基于运算符+=
重载,下面基于叫append
的成员函数。
5.字符串查找和替换
c
int find(const string& str,int pos=0) const; //查找str第一次出现的位置,从pos开始查找
int find(const char* s,int pos=0) const; //查找s第一次出现的位置,从pos开始查找
int find(const char* s,int pos,int n)const; //从pos位置查找s的前n个字符第一次出现的位置
int find(const char c,int pos=0) const; //查找字符c出现的第一次位置
int rfind(const string& str,int pos=npos) const; //查找str最后一次出现位置,从pos开始查找
int rfind(const char* s,int pos=npos) const; //查找s最后一次出现位置,从pos开始查找
int rfind(const char* s,int pos,int n) const; //从pos查找s的前n个字符最后一次位置
int rfind(const char c,int pos=0) const; //查找字符c最后一次出现的位置
string& replace(int pos,int n,const string& str); //替换从pos开始的n个字符为字符串str
string& replace(int pos,int n,const char* s ); //替换从pos开始的n个字符为字符串s
1.参数列表中
int pos=0
是默认参数的形式;2.
find
和rfind
的区别:find
是从左到右查找,而rfind
是从右到左查找,从右到左的第一次相较于正常顺序(从左到右)也就是最后一次;3.
find
和rfind
的返回值都是int
类型,表示下标,下标从0开始,若字符串中没有找到相关字符串,返回-1;4.
replace
替换,要么用C++风格字符串string
替换,要么用C语言风格字符串char *
替换。
6.字符串比较
比较方式:遍历每个字符,按ASCll
码表的值进行比较
=
,返回0>
,返回1<
,返回-1
c++
int compare(const string& s) const; //与字符串比较
int compare(const char* s) const; //与字符串比较
1.只有字符串每个字符对应的
ASCll
码表对应的值相等,才会等于2.字符串比较一般只用来判断两个字符串是否相等,比较其大小没有什么意义
7.字符存取
c++
char& operator[](int n); //通过[]方式获取字符
char& at(int n); //通过成员函数获取字符
单个字符的存取,即对字符串中某个字符进行读或写的操作。
8.插入和删除
c++
string& insert(int pos,const char* s); //从pos位置开始插入字符串s
string& insert(int pos,const string& str); //从pos位置开始插入字符串str
string& insert(int pos,int n,char c); //在指定位置插入n个字符c
string& erase(int pos,int n=npos); //删除从pos开始的n个字符
插入和删除下标都是从0开始。
9.子串
c++
string substr(int pos=0,int n=npos) const; //返回由pos开始的n个字符组成的字符串
二.vector容器
1.vector理解
vector
数据结构与数组非常相似,也被称为单端数组
vector
与普通数组的区别:数组是静态空间,而vector
可以动态扩展
动态扩展:并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝到新空间,释放原空间
vector容器的迭代器是支持随机访问的迭代器
2.vector构造函数
c++
vector<T> v; //采用模板实现类实现,默认构造函数
vector v_1(v.begin(),v.end()); //将v[begin(),end()]区间中的元素拷贝给当前v_1容器,区间构造
vector(n,elem); //将n个elem元素拷贝给当前容器
vector(const vector &vec); //拷贝构造函数
进行一下练习:
c++
#include <iostream>
#include <vector>
using namespace std;
template<typename T>
void print(vector<T> v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
cout << *it <<" ";
cout << endl;
}
void test01()
{
//第一种构造
vector<int> v1;
for (int i = 0; i < 10; i++)
v1.push_back(i);
print(v1);
//第二种区间构造
vector<int> v2(v1.begin(), v1.end());
print(v2);
//第三种构造
vector<int> v3(10, 2);
print(v3);
//第四种构造:拷贝构造
vector<int> v4(v3);
print(v4);
}
int main()
{
test01();
return 0;
}
3.vector赋值操作
c++
vector& operator=(const vector &vec); //重载等号操作符,类似于拷贝构造
assign(beg,end); //类似于区间拷贝构造
assign(n,elem); //将n个elem拷贝赋值给当前对象
重载=
或成员函数assign()
完成赋值操作,但一般我们在构造的时候就会完成赋值操作。
4.vector容量与大小
c++
empty(); //判断容器是否为空
capacity(); //返回容器的容量
size(); //返回容器中元素的个数
resize(int num); //重新指定容器的大小为num,若容器变长,则以默认值0填充新位置
//如果容器变短,则末尾超出容器大小的元素被删除
resize(int num,elem); //重新指定容器的大小为num,若容器变长,则以元素elem填充新位置
//如果容器变短,则末尾超出容器大小的元素被删除
5.vector插入和删除
c++
push_back(ele); //尾部插入元素ele
pop_back(); //删除最后一个元素
insert(const_iterator pos,ele); //迭代器指定位置pos插入元素ele
insert(const_iterator pos,int count,ele); //迭代器指定位置pos插入count个元素ele
erase(const_iterator pos); //删除迭代器指向的元素
erase(const_iterator start,const_iterator end); //删除迭代器从start到end之间的元素
clear(); //删除容器中的所有元素
注意:插入和删除的参数是一个迭代器,可以简单的把迭代器理解为一个广义指针。
6.vector数据存取
c++
at(int idx); //成员函数返回索引idx所指的数据
operator[idx]; //重载[]返回索引idx所指的数据
front(); //返回容器中的第一个数据元素
back(); //返回容器中的最后一个数据元素
除了用迭代器访问容器vector
中的元素,[]
和at()
也可以实现数据存取。
7.vector互换容器
c++
swap(vec); //将容器vec与自身元素交换
交换元素很好理解,但我们需要知道它的一般用途:实际上交换函数往往用来收缩空间使用。
比如现在有一个容器v
的大小1000,根据系统动态扩展,其容量可能为1300,而现在如果我们进行resize
重新设置大小为3,但其容量并不会减少,所以我们需要用该函数来进行收缩内存空间,原理是与一个匿名对象交换元素,这样操作后原来v
的容量和大小都变成了3:
c++
vector<int>(v).swap(v); //与匿名对象交换元素
8.vector预留空间
目标:减少vector动态扩展的次数
c
reserve(int len); //预留len的内存容量,预留位置不初始化,元素不可访问
练习:
c++
#include <iostream>
#include <vector>
using namespace std;
template<typename T>
void print(vector<T> v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
cout << *it <<" ";
cout << endl;
}
void test01()
{
//第一种构造
int num = 0;//表示动态扩展的次数
int* p = NULL;
vector<int> v1;
v1.reserve(100000); //注释这一行代码,看一下结果差异
for (int i = 0; i < 100000; i++)
{
v1.push_back(i);
if (p != &v1[0])
{
p = &v1[0];
num++;
}
}
cout << num << endl;
}
int main()
{
test01();
return 0;
}
如果数据量比较大,可以一开始就使用reverse
预留足够的空间
三.deque容器
1.deque理解
双端数组,可以对头端和尾端进行插入和删除操作
deque
与vector
区别:
vector
对于头部的插入删除效率低,数据量越大,效率越低deque
相对而言,对头部的插入删除速度回比vector
快vector
访问元素时的速度会比deque
快,这和两者内部实现有关
deque
工作原理:
deque
内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据- 中控器维护的是每个缓冲区的地址,使得使用
deque
时像一片连续的内存空间
简单说一下deque
在前后端操作的原理:中控器维护的是缓冲区的地址,如果在头端(尾端)插入一个元素,那么就在对应的第一个(最后一个)缓冲区插入该数据,若缓冲区满,那么就增加一个新的缓冲区添加进入中控器;如果在头端(尾端)删除一个元素,那么就在对应的缓冲区进行删除即可。同时,由于需要借助中控器进行数据操作,所以其操作速度没有vector
那么快而已。
deque
容器的迭代器也是支持随机访问的。
2.deque构造函数
c++
deque<T> deqT; //默认构造形式
deque(beg,end); //构造函数将[beg,end)区间的元素拷贝给当前对象(左开右闭)
deque(n,elem); //将n个elem拷贝给当前对象
deque(const deque &deq); //拷贝构造函数
3.赋值操作
c++
deque& operator=(const deque &deq); //重载=
assign(beg,end); //将[beg,end)区间中的数据拷贝赋值给本身
assign(n,elem); //将n个elem拷贝赋值给本身
deque
与vector
的赋值操作类似。
4.deque大小操作
c++
deque.empty(); //判断容器是否为空
deque.size(); //返回容器中元素的个数
deque.resize(int num); //重新指定容器的大小为num,若容器变长,则以默认值0填充新位置
//如果容器变短,则末尾超出容器大小的元素被删除
deque.resize(int num,elem); //重新指定容器的大小为num,若容器变长,则以元素elem填充新位置
//如果容器变短,则末尾超出容器大小的元素被删除
deque
同vector
一样,但是其没有容量的概念,因为它是可以无限扩充的。
5.插入和删除
两端插入或删除操作:
c++
push_back(elem); //在容器尾部添加一个数据
push_front(elem); //在容器头部插入一个数据
pop_back(); //删除容器的最后一个数据
pop_front(); //删除容器的第一个数据
可以通过
deque
的理解概念图得出来。
指定位置操作:
c++
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位置的数据,返回下一个数据的位置
同vector
一样,在指定位置插入即pos
都是迭代器(广义指针)。
6.deque数据存取
c++
at(int dex); //返回索引idx所指的数据
operator[]; //返回索引idx所指的数据
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素
7.deque排序
c++
sort(beg,end); //把区间内的元素进行排序
beg
和end
也是迭代器,这里的排序是从小到大排序,需要导入算法的头文件。
其实sort()
是算法里面的内容,而不像上面其他是成员函数。
四.案例练习
1.案例描述
有5名选手:选手ABCDE
,10个评委分别对每一名选手打分,去除最高分,去除评委中最低分,取平均分。
2.实现步骤
- 1.创建五名选手,放到
vector
中 - 2.遍历
vector
容器,取出来每一个选手,执行for
循环,可以把10个评分打分存到deque
容器中 - 3.
sort
算法对deque
容器中分数排序,去除最高和最低分 - 4.
deque
容器遍历一遍,累加总分 - 5.获取平均分
3.实现代码
c++
#include <iostream>
#include <vector>
#include <string>
#include <deque>
#include <algorithm>
using namespace std;
//选手类
class Person
{
public:
Person(string name, int score)
{
this->m_name = name;
this->m_score = score;
}
string m_name;
int m_score;
};
//创建选手
void creatper(vector<Person> &v)
{
string namestr = "ABCDE";
for (int i = 0; i < 5; i++)
{
string name = "选手";
name += namestr[i];
int score = 0;
Person p(name, score);
v.push_back(p);
}
}
void setscore(vector<Person>& v)
{
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
{
//把评委分数放入deque中
deque<int> d;
for (int i = 0; i < 10; i++)
{
int score = rand() % 41 + 60;
d.push_back(score);
}
cout << it->m_name << "的分数为:" << endl;
for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++)
{
cout<<*dit<<" ";
}
cout << endl;
//去除最大值和最小值
sort(d.begin(), d.end());
d.pop_back();
d.pop_front();
//取平均分
int sum = 0;
for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++)
{
sum += *dit;
}
int avg = sum / d.size();
it->m_score = avg;
}
}
void showscore(vector<Person>& v)
{
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
{
cout << it->m_name << "的最后成绩是:" << it->m_score << endl;
}
}
int main()
{
//1.创建5个选手
vector<Person> v;
creatper(v);
//2.开始打分
setscore(v);
//3.显示分数
showscore(v);
return 0;
}