一、string(字符串)
1、string的构造
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1; //无参构造函数
string s2("helloworld"); //有参构造函数
string s3(10,'a'); //输出10个a 构造函数重载
string s4(s2); //拷贝构造函数
cout << s2 << endl; //重载了输出运算符
cin >> s1; //重载了输入运算符
cout << s1 << endl;
s1 += "helloworld"; //重载了
if(s1 == s2)
{
}
s1 = s1 + s2;
return 0;
}
2、string的使用
1、string存取字符操作
#include <iostream>
#include <exception>
using namespace std;
int main()
{
string s("helloworld");
cout << s[1] << endl; //通过运算符重载,可以访问string对象的第一个字符,否则要s.data[i]
s[1] = 'x'; //修改字符串的第一个变量
cout << s << endl;
cout << s.at(1) << endl; //通过成员函数来访问
//cout << s[20] << endl; //越界访问,程序会异常结束
try{
cout << s.at(10) << endl; //越界访问会抛出异常
}
catch(exception &e)
{
cout << e.what() << endl;
}
return 0;
}
2、获取字符串地址c.str()
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
char buf[32] = {0};
string s("helloworld");
strcpy(buf,s.c_str()); //对象里的成员函数c_str() 返回字符串首地址
cout << buf << endl;
return 0;
}
3、string拷贝成员函数s.copy(buf,num,num)
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
int main()
{
char buf[32] = {0};
string s("helloworld");
s.copy(buf,5); //拷贝到哪里 拷贝几个字节 从第几个位置开始拷贝(默认为0)
cout << buf << endl;
memset(buf,0,sizeof(buf));
s.copy(buf,4,5);
cout << buf << endl;
return 0;
}
4、string求长度和判断空
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s("helloworld");
cout << s.length() << endl; //调用s.length()成员函数
if(s.empty())
{
cout << "字符串为空" << endl;
}
else
{
cout << "字符串不为空" << endl;
}
return 0;
}
5、string赋值(自动清空)
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("helloworld");
s1 = "cs"; //重载了=运算符
cout << s1 << endl;
const char *s = "this is test";
s1.assign(s);
cout << s1 << endl;
s1.assign(s,7); //s前7个字符复制给s1
cout << s1 << endl;
s1.assign(5,'a'); //把五个a赋值给s1
cout << s1 << endl;
string s2("hey boy");
s1.assign(s2); //把对象s2赋值给s1
cout << s1 << endl; //运算符重载
s1.assign(s2,4,3); //从第四个字节开始复制3个字节
cout << s1 << endl;
return 0;
}
6、string连接
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("helloworld");
s1 += "1234"; //重载了+=运算符
cout << s1 << endl;
string s2("abcd");
s1 += s2; //将字符串对象连接在字符串对象后面
cout << s1 << endl;
const char *s = "haha"; //将字符串指针接在字符串对象后面
s1.append(s);
cout << s1 << endl;
s1.append(s,2); //将s指向的字符串的前2个字节连接在s1后面
cout << s1 << endl;
s1.append(s2); //用成员函数将一个对象接在一个对象后面
cout << s1 << endl;
s1.append(s2,4,2); //从s2对象开始,向后4个字符开始,接入2个字符
cout << s1 << endl;
s1.append(10,'x'); //把10个x接在s1后面
cout << s1 << endl;
return 0;
}
7、string比较
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("helloworld");
string s2("helloboy");
const char *s = "hellogirl";
if(s1.compare(s2) > 0)
{
cout << s1 << " > " << s2 << endl;
}
if(s1.compare(s) > 0)
{
cout << s1 << " > " << s << endl;
}
return 0;
}
8、string获取子串
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s("helloworld");
cout << s.substr() << endl; //默认 子串是字符串本身
cout << s.substr(5,5) << endl; //从5开始打印5个字节
return 0;
}
9、string的查找和替换
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
int main()
{
int p;
string s1("helloworld");
string s2("world");
p = s1.find('o'); //s1.find('o',2)添加参数从第几个开始找
cout << p << endl;
p = s1.find('x');
cout << p << endl; //不存在返回-1
p = s1.find('o',5);
cout << p << endl;
p = s1.find("ll"); //查找字符串
cout << p << endl;
p = s1.find(s2); //查找对象
cout << p << endl;
p = s1.rfind('o'); //从后往前找、标号不从后往前
cout << p << endl;
//替换
s1.replace(5,5,"xxx"); //从第5个字符开始,向后5个字符,替换成xxx
cout << s1 << endl;
s1.replace(5,5,s2); //替换对象
cout << s1 << endl;
string s3("helloworldhelloworldhelloworldhelloworld");
p = s3.find("world");
while(p != -1)
{
s3.replace(p,strlen("world"),"x");
p = s3.find("world",p + strlen("x"));
}
cout << s3 << endl;
return 0;
}
10、string区间删除和插入
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("helloworld");
string s2("12345");
s1.insert(0,"this is "); //往第0个位置插入字符串
cout << s1 << endl;
s1.insert(10,s2); //往第10个位置插入对象s2
cout << s1 << endl;
s1.insert(0,5,'x'); //往第0个位置插入5个x
cout << s1 << endl;
//删除
s1.erase(0,20); //从第0个位置删除20个字符
cout << s1 << endl;
return 0;
}
二、vector(动态数组,无容量限制,元素不需要连续)
特点:尾部添加或移除元素非常快速。但是在中部或头部插入元素或移除元素比较费时
1、vector的构造(模板类,显示构造)
int data[10] = {1,2,3,4,5,6,7,8,9,10};
vector<int> v1; //创建数组对象(模板类)//无参构造函数
vector<int> v2(data,data + 10); //初始化数组 左闭右开 从a的首地址,到a的最后元素地址+1
vector<int> v3(10,1); //用10个1初始化
2、vector的使用
1、vector扩充容量和显示当前容量
v1.resize(10); //扩充v1容量 添加10个元素
v2.size(); //显示元素个数
vector.empty(); //判断容器是否为空
vector.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
2、vector的赋值(地址)(自动清空)
vector.assign(beg,end); //将[beg, end)区间中的数据拷贝赋值给本身。注意该区间是左闭右开的区间。
vector.assign(n,elem); //将n个elem拷贝赋值给本身。
vector& operator=(const vector &vec); //重载等号操作符
vector.swap(vec); // 将vec与本身的元素互换
3、vector的数据存取
vec.at(idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常。
vec[idx]; //返回索引idx所指的数据,越界时,运行直接报错
4、迭代器
vector<int>容器中的迭代器iterator类创建it指针对象
//正向迭代器
for(vector<int>::iterator it = v3.begin();it != v3.end();it++) //v3.end()是最后一个的后一个
{
cout << *it << " ";
}
cout << endl;
//反向迭代器
for(vector<int> ::reverse_iterator rit = v2.rbegin();rit != v2.rend();rit++) //rend是最后一个前一个地址
{
cout << *rit << " ";
}
cout << endl;
//只读迭代器,只能访问,不能修改数值
for(vector<int> :: const_iterator cit = v2.begin();cit != v2.end();cit++)
{
cout << *cit << " ";
}
cout << endl;
5、vector的插入(地址)(自动增加容量)
vector.insert(pos,elem); //在pos位置(用v.begin 不是下标)插入一个elem元素的拷贝,返回新数据的位置。
vector.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
vector.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值
#include <iostream>
#include <vector>
#include <string.h>
using namespace std;
int main()
{
const char *str = "this is ";
const char *s = "helloworld";
vector<char> v(s,s + strlen(s));
for(vector<char> :: iterator it = v.begin();it != v.end();it++)
{
cout << *it;
}
cout << endl;
v.insert(v.begin(),'x'); //在起始位置插入x字符
for(vector<char> :: iterator it = v.begin();it != v.end();it++)
{
cout << *it;
}
cout << endl;
v.insert(v.end(),5,'x'); //在某位置 插入 5 个x字符
for(vector<char> :: iterator it = v.begin();it != v.end();it++)
{
cout << *it;
}
cout << endl;
v.insert(v.begin(),str,str + strlen(str));
for(vector<char> :: iterator it = v.begin();it != v.end();it++)
{
cout << *it;
}
cout << endl;
return 0;
}
6、vector的删除(地址(不是下标)的元素)
vector.clear(); //移除容器的所有数据
vec.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
vec.erase(pos); //删除pos位置的数据,返回下一个数据的位置。
v.erase(v.end() - 5,v.end()); //删除区间
for(vector<char> :: iterator it = v.begin();it != v.end();it++)
{
cout << *it;
}
cout << endl;
v.erase(v.begin() + 8); //删除某个地址的元素
for(vector<char> :: iterator it = v.begin();it != v.end();it++)
{
cout << *it;
}
cout << endl;
三、deque(双端数组)(头和尾都会移动)
1、deque的构造
#include <iostream>
#include <deque>
using namespace std;
int main()
{
int data[5] = {1,2,3,4,5};
deque<int> d1;
deque<int> d2(data,data + 5); //构造函数将[beg, end)区间中的元素拷贝给本身。注意该区间是左闭右开的区间。
deque<int> d3(10,1); //构造函数将n个elem拷贝给本身。
deque<int> d4(d3); //拷贝构造
for(deque<int> :: iterator it = d2.begin(); it != d2.end();it++)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
2、deque的使用
1、deque末尾的添加移除操作
deque.push_back(elem); //在容器尾部添加一个数据
deque.push_front(elem); //在容器头部插入一个数据
deque.pop_back(); //删除容器最后一个数据
deque.pop_front(); //删除容器第一个数据
d2.push_back(6); //结尾添加元素6
d2.push_front(0); //开头添加元素
for(deque<int> :: iterator it = d2.begin(); it != d2.end();it++)
{
cout << *it << " ";
}
cout << endl;
d2.pop_back();
d2.pop_front();
for(deque<int> :: iterator it = d2.begin(); it != d2.end();it++)
{
cout << *it << " ";
}
cout << endl;
2、deque的数据存取
deque.at(idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range。
deque[idx]; //返回索引idx所指的数据,如果idx越界,不抛出异常,直接出错。
deque.front(); //返回第一个数据。
deque.back(); //返回最后一个数据
d2.front() = 1000; //开头结尾变1000
d2.back() = 1000;
for(deque<int> :: iterator it = d2.begin(); it != d2.end();it++)
{
cout << *it << " ";
}
cout << endl;
3、deque与迭代器
上述代码,存在了迭代器
4、deque的赋值(地址)(自动清空)
deque.assign(beg,end); //将[beg, end)区间中的数据拷贝赋值给本身。注意该区间是左闭右开的区间。
deque.assign(n,elem); //将n个elem拷贝赋值给本身。
deque& operator=(const deque &deq); //重载等号操作符
deque.swap(deq); // 将vec与本身的元素互换
5、deque的容量大小(元素个数)
deque.size(); //返回容器中元素的个数
deque.empty(); //判断容器是否为空
deque.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
6、deque的插入(地址)
deque.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
deque.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
deque.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值。
7、deque的删除(地址)(返回下一个数据的位置)
deque.clear(); //移除容器的所有数据
deque.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
deque.erase(pos); //删除pos位置的数据,返回下一个数据的位置(返回的地址是被删掉的元素的地址,下一个元素被挪到被删掉的元素的位置),用迭代器接收
deque<int> :: iterator it = d2.erase(d2.begin()); //返回下一个元素的位置
cout << *it << endl;
8、假设 deqInt 包含1,3,2,3,3,3,4,3,5,3,删除容器中等于3的元素(返回下一个数据的位置)
#include <iostream>
#include <deque>
using namespace std;
int main()
{
int data[10] = {2,1,3,3,4,5,6,7,3,3};
deque<int> d(data,data + 10);
for(deque<int>:: iterator it = d.begin();it != d.end();)
{
if(*it == 3)
{
it = d.erase(it);
}
else
{
it++;
}
}
for(deque<int>::iterator it = d.begin();it != d.end();it++)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
四、list(双向链表,只能一个接一个访问)
list不可以随机存取元素,所以不支持at.(pos)函数与[]操作符。It++(ok) it+5(err)
1、list的构造
list(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身。注意该区间是左闭右开的区间。
list(n,elem); //构造函数将n个elem拷贝给本身。
list(const list &lst); //拷贝构造函数。
list<Student> l; //创建无参对象
2、list的使用
1、list头尾的添加移除操作
list.push_back(elem); //在容器尾部加入一个元素
list.pop_back(); //删除容器中最后一个元素
list.push_front(elem); //在容器开头插入一个元素
list.pop_front(); //从容器开头移除第一个元素
Student s1(1,"aaa");
Student s2(2,"bbb");
Student s3(3,"ccc");
Student s4(4,"ddd");
Student s5(5,"eee");
Student s6(6,"fff");
list<Student> l; //创建无参对象
l.push_back(s1); //尾插法
l.push_back(s2); //尾插法
l.push_back(s3); //尾插法
l.push_back(s4); //尾插法
l.push_front(s5); //头插法
for(list<Student>::iterator it = l.begin();it != l.end(); it++) //it指向首元素
{
it->show();
}
cout << "***" << endl;
l.pop_front(); //删除第一个节点
l.pop_back(); //删除最后一个节点
for(list<Student>::iterator it = l.begin();it != l.end(); it++) //it指向首元素
{
it->show();
}
2、list的数据存取
list.front(); //返回第一个元素。
list.back(); //返回最后一个元素。
cout << "第一个元素是: ";
l.front().show(); //最前面一个元素是
cout << "最后一个元素是:";
l.back().show();
3、list与迭代器(非连续内存的迭代器不支持 it = it + 1)
list.begin(); //返回容器中第一个元素的迭代器。
list.end(); //返回容器中最后一个元素之后的迭代器。
list.rbegin(); //返回容器中倒数第一个元素的迭代器。
list.rend(); //返回容器中倒数最后一个元素的后面的迭代器。
4、list的赋值
list.assign(beg,end); //将[beg, end)区间中的数据拷贝赋值给本身。注意该区间是左闭右开的区间。
list.assign(n,elem); //将n个elem拷贝赋值给本身。
list& operator=(const list &lst); //重载等号操作符
list.swap(lst); // 将lst与本身的元素互换。
5、list的大小
list.size(); //返回容器中元素的个数
list.empty(); //判断容器是否为空
list.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
list.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
66 cout << "链表扩充..." << endl;
67 l.resize(5,s6); //用s6扩充
68 for(list<Student>::iterator it = l.begin();it != l.end(); it++) //it指向首元素
69 {
70 it->show();
71 }
6、list的插入
list.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
list.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
list.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值。
75 Student s[5] = {Student(10,"a"),Student(11,"b"),Student(12,"c"),Student(13,"d"),Student(14,"e")};
76 l.insert(l.begin(),s[0]); //往链表开始位置插入s0
77 for(list<Student>::iterator it = l.begin();it != l.end(); it++) //it指向首元素
78 {
79 it->show();
80 }
81
82 cout << "***" << endl;
83 l.insert(l.end(),5,s[4]); //往链表结尾插入5个s[4]
84 for(list<Student>::iterator it = l.begin();it != l.end(); it++) //it指向首元素
85 {
86 it->show();
87 }
88
89 cout <<"*****" << endl;
90 l.insert(l.end(),s,s+3); //往链表结尾插入区间
91 for(list<Student>::iterator it = l.begin();it != l.end(); it++) //it指向首元素
92 {
93 it->show();
94 }
7、list的删除
list.clear(); //移除容器的所有数据
list.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
list.erase(pos); //删除pos位置的数据,返回下一个数据的位置。
lst.remove(elem); //删除容器中所有与elem值匹配的元素。(比较结构体,重载运算符)
cout << "删除一个区间" << endl;
list<Student>::iterator it = l.begin();
for(int i = 0 ;i < 5;i++)
{
it++;
}
l.erase(it,l.end());
for(list<Student>::iterator it = l.begin();it != l.end(); it++) //it指向首元素
{
it->show();
}
cout << "删除一个位置" << endl;
l.erase(l.begin());
for(list<Student>::iterator it = l.begin();it != l.end(); it++) //it指向首元素
{
it->show();
}
cout << "删除具体的元素" << endl;
//删除匹配的元素
l.remove(s1); //s1 == s2 没有运算符重载
for(list<Student>::iterator it = l.begin();it != l.end(); it++) //it指向首元素
{
it->show();
}
8、list的反序排列
lst.reverse(); //反转链表,比如lst包含1,3,5元素,运行此方法后,lst就包含5,3,1元素。
五、stack(栈和队列不存在遍历,不存在迭代器,不存在s.begin())
一、static的构造
stack采用模板类实现, stack对象的默认构造形式: stack <T> stkT;
stack <int> stkInt; //一个存放int的stack容器。
stack <float> stkFloat; //一个存放float的stack容器。
stack <string> stkString; //一个存放string的stack容器。
stack对象的拷贝构造与赋值
stack(const stack &stk); //拷贝构造函数
stack& operator=(const stack &stk); //重载等号操作符
二、static的使用
1、stack的push()与pop()方法
stack.push(elem); //往栈头添加元素
stack.pop(); //从栈头移除第一个元素
1 #include <iostream>
2 #include <stack>
3 #include <stdlib.h>
4 #include <time.h>
5
6 using namespace std;
7
8 int main()
9 {
10 stack<int> s;
11
12 srand(time(NULL));
13
14 int num;
15 for(int i= 0;i < 10;i++)
16 {
17 num = rand() % 10;
18 s.push(num);
19 cout << num << "进栈成功" << endl;
20 }
21
22 cout << "栈顶元素是 " << s.top() << endl;
24 while(!s.empty()) //判断栈是否为空
25 {
26 cout << s.top() << "出栈成功" << endl;
27 s.pop();
28 }
2、获取栈顶元素
s.top();成员函数
3、stack的大小
stack.empty(); //判断堆栈是否为空
stack.size(); //返回堆栈的大小
六、queue
一、队列的构造函数
queue采用模板类实现,queue对象的默认构造形式:queue<T> queT; 如:
queue<int> queInt; //一个存放int的queue容器。
queue<float> queFloat; //一个存放float的queue容器。
queue<string> queString; //一个存放string的queue容器。
queue(const queue &que); //拷贝构造函数
queue& operator=(const queue &que); //重载等号操作符
二、queue的使用
1、queue的push()与pop()方法
queue.push(elem); //往队尾添加元素
queue.pop(); //从队头移除第一个元素
queue<int> q;
for(int i = 0;i < 10;i++)
{
cout << i << "进队成功" << endl;
q.push(i);
}
cout << "队头元素 " << q.front() << endl;
cout << "队尾元素 " << q.back() << endl;
cout << "队列大小 " << q.size() << endl;
for(int i = 0;i < 10;i++)
{
q.pop();
}
return 0;
2、queue的数据存取
queue.back(); //返回最后一个元素
queue.front(); //返回第一个元素
3、queue的大小
queue.empty(); //判断队列是否为空
queue.size(); //返回队列的大小
七、Priority_queue(优先队列 实现排序功能)
一、priority_queue构造函数
priority_queue<int,vector<int>,less<int> > //空格防止输入输出符号 后面两个参数是默认参数
greater<int> //less greater是模板类 从小到大排序
二、priority_queue的使用
进队出队和queue一样
只有对头top(),没有队尾,不存在先入先出
队头先出队
1、基本变量类型
#include <iostream>
#include <queue>
#include <stdlib.h>
#include <time.h>
using namespace std;
int main()
{
//priority_queue<int> p;
//priority_queue<int,vector<int>,less<int> > p; //等价于priority_queue<int> p
//priority_queue<int,vector<int>,greater<int> > p;
priority_queue<int,deque<int>,greater<int> > p; //速度不一样
srand(time(NULL));
for(int i = 0;i < 10;i++)
{
int num = rand() % 20;
p.push(num);
cout << num << "进队成功" << endl;
}
cout << "队头元素" << p.top() << endl;
cout << "队列长度" << p.size() << endl;
while(!p.empty())
{
cout << p.top() << "出队" <<endl;
p.pop();
}
return 0;
}
2、自定义变量类型(重载 小于 大于号)
#include <iostream>
#include <queue>
#include <string>
using namespace std;
class Student
{
friend class compare;
private:
int id;
string name;
public:
Student(int i,string n);
void show() const;
bool operator>(const Student &s) const;
};
Student::Student(int i,string n)
{
id = i;
name = n;
}
void Student :: show() const
{
cout << "id " << id << " name " << name << endl;
}
bool Student::operator>(const Student &s) const
{
return (this->id > s.id ? true : false) ;
}
class compare
{
public:
bool operator()(const Student &s1,const Student &s2) const
{
return s1.id < s2.id;
}
};
int main()
{
Student s1(1,"zz");
Student s2(4,"ff");
Student s3(3,"bb");
Student s4(7,"cc");
Student s5(9,"ee");
Student s6(5,"dd");
//priority_queue<Student> p; //重载小于号
//priority_queue<Student,vector<Student>,greater<Student> > p; //重载大于号
priority_queue<Student,vector<Student>,compare> p;
//compare是函数对象
/*
compare c; //函数对象 仿函数 伪函数
c.operator()(s1,s2);
c(s1,s2); //重载()
*/
p.push(s1);
p.push(s2);
p.push(s3);
p.push(s4);
p.push(s5);
p.push(s6);
while(!p.empty())
{
p.top().show();
p.pop();
}
return 0;
}
3、函数对象 仿函数 伪函数
class compare
{
public:
bool operator()(const Student &s1,const Student &s2) const
{
return s1.id < s2.id;
}
};
//compare是函数对象
/*
compare c; //函数对象 仿函数 伪函数
c.operator()(s1,s2);
c(s1,s2); //重载()
*/
八、set(集合)
1、set的特点
1、set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按一定的顺序排列。元素插入过程是按排序规则插入,所以不能指定插入位置。
2、set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树。在插入操作和删除操作上比vector快。
3、set不可以直接存取元素。(不可以使用at.(pos)与[]操作符)。
4、multiset与set的区别:set支持唯一键值,每个元素值只能出现一次;而multiset中同一值可以出现多次。
5、不可以直接修改set或multiset容器中的元素值,因为该类容器是自动排序的。如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素。
#include <set>
2、set的构造和使用
1、set的构造、插入(运算符重载)、迭代
set.insert(elem); //在容器中插入元素。
set.begin(); //返回容器中第一个数据的迭代器。
set.end(); //返回容器中最后一个数据之后的迭代器。
set.rbegin(); //返回容器中倒数第一个元素的迭代器。
set.rend(); //返回容器中倒数最后一个元素的后面的迭代器
#include <iostream>
#include <set>
#include <string>
using namespace std;
class Student
{
private:
int id;
string name;
public:
Student(int i,string n);
void show() const;
bool operator<(const Student &s) const;
};
Student::Student(int i,string n)
{
id = i;
name = n;
}
void Student::show() const
{
cout << "id " << id << " name " << name << endl;
}
bool Student::operator < (const Student &s) const
{
return this->id < s.id;
}
int main()
{
Student s1(3,"aa");
Student s2(1,"zz");
Student s3(5,"bb");
Student s4(4,"gg");
Student s5(2,"ff");
//创建set对象
set<Student> s;
//set插入 stu对象
s.insert(s1); //对象排序出错
s.insert(s2);
s.insert(s3);
for(set<Student>::iterator it = s.begin();it != s.end();it++)
{
it->show();
}
return 0;
}
2、set构造时改变排序
set<Student,greater<Student> >s; //重载大于号
3、set的大小
set.size(); //返回容器中元素的数目
set.empty();//判断容器是否为空
4、set的删除
set.clear(); //清除所有元素
set.erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
set.erase(beg,end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
set.erase(elem); //删除容器中值为elem的元素。排序按照id排序,查找按照id查找
cout << "set的删除" << endl;
s.erase(s.begin());
for(set<Student>::iterator it = s.begin();it != s.end();it++)
{
it->show();
}
cout << "删除一个区间" << endl;
s.erase(--(s.end()),s.end());
for(set<Student>::iterator it = s.begin();it != s.end();it++)
{
it->show();
}
cout << "删除具体元素" << endl;
Student stu(3,"zz");
s.erase(stu); //排序按照id排序,查找按照id查找
for(set<Student>::iterator it = s.begin();it != s.end();it++)
{
it->show();
}
5、set的查找(排序按照id排序,查找按照id查找)
set.find(elem); //查找elem元素,返回指向elem元素的迭代器。
set.count(elem); //返回容器中值为elem的元素个数。对set来说,要么是0,要么是1。对multiset来说,值可能大于1。
set.lower_bound(elem); //返回第一个>=elem元素的迭代器。
set.upper_bound(elem); // 返回第一个>elem元素的迭代器。
set.equal_range(elem); //返回容器中与elem相等的上下限的两个迭代器。上限是闭区间,下限是开区间,如[beg,end)。 上限是等于或大于elem的,下限是大于elem的值
以上函数返回两个迭代器,而这两个迭代器被封装在pair中。
以下讲解pair的含义与使用方法。
cout << "set的查找" << endl;
set<Student,greater<Student> >::iterator it = s.find(s6);
if(it == s.end())
{
cout << "对象不存在" << endl;
}
else
{
it->show();
}
cout << "set的统计" << endl;
cout << s.count(s6) << endl;
cout << "low_bound" << endl;
it = s.lower_bound(s6);
if(it == s.end())
{
cout << "不存在" << endl;
}
else
{
it->show();
}
6、pair的使用(类型,模板类,有两个成员变量)
pair译为对组,可以将两个值视为一个单元。
pair<T1,T2>存放的两个值的类型,可以不一样,如T1为int,T2为float。T1,T2也可以是自定义类型。
pair.first是pair里面的第一个值,是T1类型。
pair.second是pair里面的第二个值,是T2类型。
cout << "equal_range" << endl;
pair<set<Student,greater<Student> >::iterator,set<Student,greater<Student> >::iterator > p;
p = s.equal_range(s3);
p.first->show();
p.second->show();
九、map(关联式容器,一个map是一个键值对序列)
1、map特点(一个节点存放一对数据,一个序号,一个对象)
1、map是标准的关联式容器,一个map是一个键值对序列,即(key,value)对。它提供基于key的快速检索能力。
2、map中key值是唯一的。集合中的元素按一定的顺序排列(根据key排序)。元素插入过程是按排序规则插入,所以不能指定插入位置。
3、map的具体实现采用红黑树变体的平衡二叉树的数据结构。在插入操作和删除操作上比vector快。
4、map可以直接存取key所对应的value,支持[]操作符,如map[key]=value。
5、multimap与map的区别:map支持唯一键值,每个键只能出现一次;而multimap中相同键可以出现多次。multimap不支持[]操作符。
#include <map>
2、map的构造和使用
1、map的构造
map/multimap采用模板类实现,对象的默认构造形式:
map<T1,T2> mapTT;
multimap<T1,T2> multimapTT;
如:
map<int, char> mapA;
map<string,float> mapB;
//其中T1,T2还可以用各种指针类型或自定义类型
map(const map &mp); //拷贝构造函数
map& operator=(const map &mp); //重载等号操作符
map.swap(mp); //交换两个集合容器
2、map的数据插入和迭代器
map.insert(...); //往容器插入元素,返回pair<iterator,bool>
在map中插入元素的三种方式:(前三种插入方式,如果数据已经存在,则出错,返回错误)
假设 map<int, string> mapStu;
一、通过pair的方式插入对象
mapStu.insert( pair<int,string>(3,"小张") );
二、通过pair的方式插入对象
mapStu.inset(make_pair(-1, “校长-1”));
三、通过value_type的方式插入对象
mapStu.insert( map<int,string>::value_type(1,"小李") );
四、通过数组的方式插入值(不安全,将原有数据覆盖)
mapStu[3] = “小刘";
mapStu[5] = “小王";
前三种方法,采用的是insert()方法,该方法返回值为pair<iterator,bool>
bool返回是否插入成功,pair返回已经存在的键值对的地址。
第四种方法非常直观,但存在一个性能的问题。插入3时,先在mapStu中查找主键为3的项,若没发现,则将一个键为3,值为初始化值的对组插入到mapStu中,然后再将值修改成“小刘”。若发现已存在3这个键,则修改这个键对应的value。
string strName = mapStu[2]; //取操作或插入操作
只有当mapStu存在2这个键时才是正确的取操作,否则会自动插入一个实例,键为2,值为初始化值。
map<int,string> m; //key是int类型 value是string类型
m.insert(pair<int,string>(3,"aa")); //通过pair对组,组合一组数据
m.insert(pair<int,string>(1,"zz"));
m.insert(make_pair(5,"cc")); //通过make_pair组合一对数据插入
m.insert(make_pair(4,"ff"));
m.insert(map<int,string>::value_type(6,"hh")); //通过map内部静态成员函数插入
m.insert(map<int,string>::value_type(2,"dd"));
//重载下标运算符
m[8] = "uu";
m[7] = "ee";
3、map的大小
map.size(); //返回容器中元素的数目
map.empty();//判断容器是否为空
4、map的删除
map.clear(); //删除所有元素
map.erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
map.erase(beg,end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
map.erase(keyElem); //删除容器中key为keyElem的对组。
38 cout << "map删除指定的位置" << endl;
39 m.erase(m.begin());
40 for(map<int,string>::iterator it = m.begin();it != m.end();it++)
41 {
42 cout << "学号" << it->first << " 姓名 " << it->second << endl;
43 }
44
45 cout << "map删除区间" << endl;
46 m.erase(--(m.end()),m.end());
47 for(map<int,string>::iterator it = m.begin();it != m.end();it++)
48 {
49 cout << "学号" << it->first << " 姓名 " << it->second << endl;
50 }
51
52 cout << "map删除具体元素" << endl;
53 m.erase(4); //根据key值删除
54 for(map<int,string>::iterator it = m.begin();it != m.end();it++)
55 {
56 cout << "学号" << it->first << " 姓名 " << it->second << endl;
57 }
5、map的查找
map.find(key); 查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end();
map.count(keyElem); //返回容器中key为keyElem的对组个数。对map来说,要么是0,要么是1。对multimap来说,值可能大于1。
6、multimap
Multimap:1个key值可以对应多个value
Multimap 案例:
公司有销售部 sale (员工2名)、技术研发部 development (1人)、财务部 Financial (2人)
人员信息有:姓名,年龄,电话、工资等组成
通过 multimap进行 信息的插入、保存、显示
分部门显示员工信息
#include <iostream>
#include <map>
using namespace std;
class Employee
{
private:
int id;
string name;
public:
Employee(int i,string n)
{
id = i;
name = n;
}
void show()
{
cout << "工号: " << id << " 姓名: " << name << endl;
}
};
int main()
{
Employee e1(1,"aa");
Employee e2(2,"aa");
Employee e3(3,"aa");
Employee e4(4,"aa");
Employee e5(5,"aa");
Employee e6(6,"aa");
Employee e7(7,"aa");
Employee e8(8,"aa");
multimap<string,Employee> m;
//销售部门有三个员工
m.insert(make_pair("sale",e1));
m.insert(make_pair("sale",e2));
m.insert(make_pair("sale",e3));
//研发部门一个员工
m.insert(make_pair("development",e4));
//财务部门4个员工
m.insert(make_pair("financial",e5));
m.insert(make_pair("financial",e6));
m.insert(make_pair("financial",e7));
m.insert(make_pair("financial",e8));
for(multimap<string,Employee>::iterator it = m.begin();it != m.end();it++)
{
cout << "部门: " << it->first << endl;
it->second.show();
}
return 0;
}
十、容器的共性机制
1、容器的共同能力
理论提高:
1、所有容器提供的都是值(value)语意,而非引用(reference)语意。容器执行插入元素的操作时,内部实施拷贝动作。所以STL容器内存储的元素必须能够被拷贝(必须提供拷贝构造函数)。
2、除了queue与stack外,每个容器都提供可返回迭代器的函数,运用返回的迭代器就可以访问元素。
3、通常STL不会丢出异常。要求使用者确保传入正确的参数。
4、每个容器都提供了一个默认构造函数跟一个默认拷贝构造函数。
5、如已有容器vecIntA。
vector<int> vecIntB(vecIntA); //调用拷贝构造函数,复制vecIntA到vecIntB中。
6、与大小相关的操作方法(c代表容器):
c.size(); //返回容器中元素的个数
c.empty(); //判断容器是否为空
7、比较操作(c1,c2代表容器):
c1 == c2 判断c1是否等于c2
c1 != c2 判断c1是否不等于c2
c1 = c2 把c2的所有元素指派给c1
2、各个容器的使用时机
1、 deque的使用场景:比如排队购票系统,对排队者的存储可以采用deque,支持头端的快速移除,尾端的快速添加。如果采用vector,则头端移除时,会移动大量的数据,速度慢。
vector与deque的比较:
一:vector.at()比deque.at()效率高,比如vector.at(0)是固定的,deque的开始位置却是不固定的。
二:如果有大量释放操作的话,vector花的时间更少,这跟二者的内部实现有关。
三:deque支持头部的快速插入与快速移除,这是deque的优点。
2、list的使用场景:比如公交车乘客的存储,随时可能有乘客下车,支持频繁的不确实位置元素的移除插入。
3、set的使用场景:比如对手机游戏的个人得分记录的存储,存储要求从高分到低分的顺序排列。
4、map的使用场景:比如按ID号存储十万个用户,想要快速要通过ID查找对应的用户。二叉树的查找效率,这时就体现出来了。如果是vector容器,最坏的情况下可能要遍历完整个容器才能找到该用户。
十 一、算法
1、算法的概述
1、算法部分主要由头文件<algorithm>,<numeric>和<functional>组成。
2、<algorithm>是所有STL头文件中最大的一个,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、反转、排序、合并等等。
3、<numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。
4、<functional>中则定义了一些模板类,用以声明函数对象。
STL提供了大量实现算法的模版函数,只要我们熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法模板,就可以完成所需要的功能,从而大大地提升效率。
2、算法中函数对象和谓词
1、函数对象和谓词定义
函数对象:
重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象。一个类对象,表现出一个函数的特征,就是通过“对象名+(参数列表)”的方式使用一个类对象,如果没有上下文,完全可以把它看作一个函数对待。
这是通过重载类的operator()来实现的。
标准库中的很多算法都可以使用函数对象或者函数来作为自定的回调行为。
谓词:
一元函数对象:函数参数1个;二元函数对象:函数参数2个。
一元谓词函数参数1个,函数返回值是bool类型,可以作为一个判断式
谓词可以是一个仿函数,也可以是一个回调函数。
二元谓词 函数参数2个,函数返回值是bool类型
2、预定义函数对象和函数适配器
标准模板库STL提前定义了很多预定义函数对象,头文件 <functional> 必须包含。
1、算术函数对象
加法:plus<Types>
减法:minus<Types>
乘法:multiplies<Types>
除法divides<Tpye>
求余:modulus<Tpye>
取反:negate<Type>
2、关系函数对象
等于equal_to<Tpye>
equal_to<string> stringEqual;
sres = stringEqual(sval1,sval2);
不等于not_equal_to<Type>
大于 greater<Type>
大于等于greater_equal<Type>
小于 less<Type>
小于等于less_equal<Type>
3、逻辑函数对象
逻辑与 logical_and<Type>
逻辑或logical_or<Type>
逻辑非logical_not<Type>
3、函数适配器
1、概念
函数对象适配器是完成一些配接工作,这些配接包括绑定(bind),否定(negate),以及对一般函数或成员函数的修饰,使其成为函数对象
2、常用函数函数适配器
标准库提供一组函数适配器,用来特殊化或者扩展一元和二元函数对象。常用适配器是:
1、绑定器(binder): binder通过把二元函数对象的一个实参绑定到一个特殊的值上,将其转换成一元函数对象。C++标准库提供两种预定义的binder适配器:bind1st和bind2nd,前者把值绑定到二元函数对象的第一个实参上,后者绑定在第二个实参上。
2、取反器(negator) : negator是一个将函数对象的值翻转的函数适配器。标准库提供两个预定义的ngeator适配器:not1翻转一元预定义函数对象的真值,而not2翻转二元谓词函数的真值。
常用函数适配器列表如下:
bind1st(op, value)
bind2nd(op, value)
not1(op)
not2(op)
mem_fun_ref(op)
mem_fun(op)
ptr_fun(op)
#include <iostream>
#include <vector>
#include <string.h>
#include <algorithm>
using namespace std;
bool Equal(string str)
{
return str == "bb";
}
int main()
{
vector<string> v;
v.push_back("aa");
v.push_back("bb");
v.push_back("cc");
v.push_back("dd");
//vector<string>::iterator it = find_if(v.begin(),v.end(),Equal);
vector<string>::iterator it = find_if(v.begin(),v.end(),bind1st(equal_to<string>(),"bb"));
if(it == v.end())
{
cout << "不存在" << endl;
}
else
{
cout << *it << endl;
}
return 0;
}
3、常用算法
1、遍历算法
1、for_each(不能修改数据)
2、transform(可以修改数据)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void show(int x)
{
cout << x << endl;
}
class print
{
public:
void operator()(int x)
{
cout << x << endl;
}
};
int main()
{
int array[5] = {1,2,3,4,5};
vector<int> v(array,array + 5);
//不能修改数据
for_each(v.begin() , v.end() , show); //遍历(读取每一个参数,然后传参给show) 从哪开始,从哪结束,函数名地址(遍历时的操作)
for_each(v.begin() , v.end() , print()); // print()函数对象形式遍历
//transform 遍历过程中可以修改数据
string s("helloworld");
transform(s.begin() , s.end() , s.begin(), ::toupper); //从哪开始,到哪儿结束,修改完成后放在哪,怎么修改
cout << s << endl;
return 0;
}
2.查找算法
1、adjacent_find
查找一对相邻重复元素
2、binary_search
在有序序列中查找value,找到则返回true。注意:在无序序列中,不可使用。
3、count/count_if
count:利用等于操作符,把标志范围内的元素与输入值比较,返回相等的个数。
count_if:利用自定义操作符,把标志范围内的元素与输入值比较,返回满足条件的元素个数
例:
序列1 3 5 7 9 11 13
统计等于3的元素的个数,使用count
统计大于3的元素的个数,使用count_if
4、find/find_if
find: 利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较。当匹配时,结束搜索,返回该元素的迭代器。
find_if:利用自定义条件,对指定范围内的元素与输入值进行比较,当匹配时,结束搜索,返回该元素的迭代器。
例:
序列:1 3 5 7 9 11 13
查找元素3,找到返回该元素的迭代器,用find
查找大于3的元素,找到返回该元素的迭代器,用find_if
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool GreaterTwo(int x)
{
return x > 2;
}
class GreaterThree
{
public:
bool operator()(int x)
{
return x > 3;
}
};
int main()
{
int arr[6] = {1,2,2,3,4,4};
vector<int> v(arr,arr + 6);
//查找一对重复且相邻的数
vector<int>::iterator it = adjacent_find(v.begin(),v.end());
if(it == v.end())
{
cout << "不存在重复且相邻的" << endl;
}
else
{
cout << *it << endl;
}
bool ret = binary_search(v.begin(),v.end(),5); //有序的序列中查找
if(ret == false)
{
cout << "不存在" << endl;
}
else
{
cout << "存在这个数" << endl;
}
int num = count(v.begin(),v.end(),2);
cout << num << endl;
num = count_if(v.begin(),v.end(),GreaterTwo); //把每一个元素传给greatertwo 谓词 回调函数 返回值bool
cout << num << endl;
it = find(v.begin(),v.end(),3); //比较对象,重载==
if(it == v.end())
{
cout << "元素不存在" << endl;
}
else
{
cout << *it << endl;
}
it = find_if(v.begin(),v.end(),GreaterThree()); //函数对象
if(it == v.end())
{
cout << "元素不存在" << endl;
}
else
{
cout << *it << endl;
}
return 0;
}
3、排序算法
1、merge
merge: 合并两个有序序列,存放到另一个序列。
2、sort
sort: 以默认升序的方式重新排列指定范围内的元素。若要改排序规则,可以输入比较函数。
3、random_shuffle
对指定范围内的元素随机调整次序
4、reverse
#include <iostream>
#include <vector>
#include <algorithm>
#include <stdlib.h>
#include <time.h>
using namespace std;
void show(int x)
{
cout << x << " ";
}
int main()
{
vector<int> v1;
vector<int> v2;
srand(time(NULL));
for(int i = 0;i < 5;i++)
{
v1.push_back(rand() % 10);
v2.push_back(rand() % 10);
}
sort(v1.begin(),v1.end(),less<int>());
sort(v2.begin(),v2.end(),less<int>());
cout << "v1:" << endl;
for_each(v1.begin(),v1.end(),show);
cout << endl;
cout << "v2:" << endl;
for_each(v2.begin(),v2.end(),show);
cout << endl;
vector<int> v3;
v3.resize( v2.size() + v1.size());
merge(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin());
for_each(v3.begin(),v3.end(),show);
cout << endl;
//退化成指针
//cout << sizeof(v1.begin()) << endl;
//打乱操作
random_shuffle(v1.begin(),v1.end());
cout << "v1:" << endl;
for_each(v1.begin(),v1.end(),show);
cout << endl;
//逆序
reverse(v2.begin(),v2.end());
cout << "v2:" << endl;
for_each(v2.begin(),v2.end(),show);
cout << endl;
return 0;
}
4、拷贝和替换算法
1、copy
vector<int> vecIntA;
vecIntA.push_back(1);
vecIntA.push_back(3);
vecIntA.push_back(5);
vecIntA.push_back(7);
vecIntA.push_back(9);
vector<int> vecIntB;
vecIntB.resize(5); //扩大空间
copy(vecIntA.begin(), vecIntA.end(), vecIntB.begin()); //vecIntB: {1,3,5,7,9}
2、replace
replace(beg,end,oldValue,newValue): 将指定范围内的所有等于oldValue的元素替换成newValue。
vector<int> vecIntA;
vecIntA.push_back(1);
vecIntA.push_back(3);
vecIntA.push_back(5);
vecIntA.push_back(3);
vecIntA.push_back(9);
replace(vecIntA.begin(), vecIntA.end(), 3, 8); //{1,8,5,8,9}
3、replace_if
replace_if : 将指定范围内所有操作结果为true的元素用新值替换。
用法举例:
replace_if(vecIntA.begin(),vecIntA.end(),GreaterThree,newVal)
其中 vecIntA是用vector<int>声明的容器
GreaterThree 函数的原型是 bool GreaterThree(int iNum)
//把大于等于3的元素替换成8
vector<int> vecIntA;
vecIntA.push_back(1);
vecIntA.push_back(3);
vecIntA.push_back(5);
vecIntA.push_back(3);
vecIntA.push_back(9);
replace_if(vecIntA.begin(), vecIntA.end(), GreaterThree, 8); // GreaterThree的定义在上面。
4、swap
swap: 交换两个容器的元素
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void show(int x)
{
cout << x << " ";
}
int main()
{
vector<int> v1(5,1);
vector<int> v2(5,2);
v2.resize(6);
copy(v1.begin(),v1.end(),++(v2.begin()));
for_each(v2.begin(),v2.end(),show);
cout << endl;
swap(v1,v2);
cout << "v2:" << endl;
for_each(v2.begin(),v2.end(),show);
cout << endl;
return 0;
}
5、算数和生成算法
1、accumulate
accumulate: 对指定范围内的元素求和,然后结果再加上一个由val指定的初始值。
包含头文件<numeric>
vector<int> vecIntA;
vecIntA.push_back(1);
vecIntA.push_back(3);
vecIntA.push_back(5);
vecIntA.push_back(7);
vecIntA.push_back(9);
int iSum = accumulate(vecIntA.begin(), vecIntA.end(), 100); //iSum==125
2、fill
fill: 将输入值赋给标志范围内的所有元素。
vector<int> vecIntA;
vecIntA.push_back(1);
vecIntA.push_back(3);
vecIntA.push_back(5);
vecIntA.push_back(7);
vecIntA.push_back(9);
fill(vecIntA.begin(), vecIntA.end(), 8); //8, 8, 8, 8, 8
6、集合算法
set_union: 构造一个有序序列,包含两个有序序列的并集。
set_intersection: 构造一个有序序列,包含两个有序序列的交集。
set_difference: 构造一个有序序列,该序列保留第一个有序序列中存在而第二个有序序列中不存在的元素。
vector<int> vecIntA;
vecIntA.push_back(1);
vecIntA.push_back(3);
vecIntA.push_back(5);
vecIntA.push_back(7);
vecIntA.push_back(9);
vector<int> vecIntB;
vecIntB.push_back(1);
vecIntB.push_back(3);
vecIntB.push_back(5);
vecIntB.push_back(6);
vecIntB.push_back(8);
vector<int> vecIntC;
vecIntC.resize(10);
//并集
set_union(vecIntA.begin(), vecIntA.end(), vecIntB.begin(), vecIntB.end(), vecIntC.begin()); //vecIntC : {1,3,5,6,7,8,9,0,0,0}
//交集
fill(vecIntC.begin(),vecIntC.end(),0);
set_intersection(vecIntA.begin(), vecIntA.end(), vecIntB.begin(), vecIntB.end(), vecIntC.begin()); //vecIntC: {1,3,5,0,0,0,0,0,0,0}
//差集
fill(vecIntC.begin(),vecIntC.end(),0);
set_difference(vecIntA.begin(), vecIntA.end(), vecIntB.begin(), vecIntB.end(), vecIntC.begin()); //vecIntC: {7,9,0,0,0,0,0,0,0,0}