文章目录
本文是我在学习C++过程当中的心得和学习笔记,在学习C++时已经有C语言的基础,因此入门知识省略了一部分。文章包含了C++的入门基础内容和核心进阶内容,并附上了学习的代码,仅供大家参考。如果有问题,有错误欢迎大家留言。剩余的内容可以通过
这篇文章
找到。
一、 STL标准模板库
长久以来,软件界一直希望建立—种可重复利用的东西C++的面向对象和泛型编程思想,目的就是复用性的提升,大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作为了建立数据结构和算法的一套标准,诞生了STL。
STL(Standard Template Library,标准模板库)。STL从广义上分为:
容器(container)、算法(algorithm)、迭代器(iterator)
。容器和算法之间通过迭代器进行无缝连接。STL几乎所有的代码都采用了模板类或者模板函数。
STL的容器就是将运用最广泛的一些数据结构实现出来,例如数组,链表,树,栈,队列,集合,映射表等等。
算法有拷贝,替换,查找,计数,寻找极值等等。迭代器则非常类似与指针提供一种方法,依序寻访某个容器所含的各个元素。
二、各大容器
2.1 vector容器
c++中的vector容器和数组非常相似,
也称为单端数组,仅支持尾部插入(头部插入需要将所有元素后移,然后插入效率低)
。与普通数组不同之处在于其可以动态扩展。动态扩展不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间。
vector容器的迭代器是支持随机访问的迭代器。
2.1.1 vector的初始化
vector构造函数如下
:
vector的初始化
:
- 不带参数的构造函数初始化
//初始化一个size为0的vector
vector<int> abc;
- 带参数的构造函数初始化
//初始化size,但每个元素值为默认值
vector<int> abc(10); //初始化了10个默认值为0的元素
//初始化size,并且设置初始值
vector<int> cde(10,1); //初始化了10个值为1的元素
- 通过数组地址初始化
int a[5] = {1,2,3,4,5};
//通过数组a的地址初始化,注意地址是从0到5(左闭右开区间)
vector<int> b(a, a+5);
- 通过同类型的vector初始化
vector<int> a(5,1);
//通过a初始化
vector<int> b(a);
- 通过insert初始化
//insert初始化方式将同类型的迭代器对应的始末区间(左闭右开区间)内的值插入到vector中
vector<int> a(6,6);
vecot<int> b;
//将a[0]~a[2]插入到b中,b.size()由0变为3
b.insert(b.begin(), a.begin(), a.begin() + 3);
参考文章
vector的几种初始化及赋值方式
。
遍历程序如下
:
/*
容器:vecor
算法:for_each
迭代器:vector<int>::iterator
*/
void my_print1(int val)
{
cout << val << " ";
}
void my_print(vector <int>&v)
{
通过迭代器访问容器中的数据
vector<int>::iterator itBegin = v.begin(); // 起始迭代器,指向容器中的第一个元素
vector<int>::iterator itEnd = v.end(); // 结束迭代器,指向容器中的最后一个元素的下一个位置
第一种遍历方式
//while (itBegin != itEnd)
//{
// cout << *itBegin << " ";
// itBegin++;
//}
// 第二种遍历方式
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
// 第三种遍历方式
//for_each(v.begin(), v.end(), my_print1);
cout << endl;
// 第四种遍历方式,使用auto
//for (auto e : v)
//{
// cout << e << " ";
//}
//cout << endl;
}
void test1()
{
// vector构造
vector<int>v1; // 创建vector容器,默认构造,无参构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i); // 向容器中插入数据
}
vector<int>v2(v1.begin(), v1.end()); // 通过区间方式进行构造,相当于让v1等于v2
vector<int>v3(10, 100);
vector<int>v4(v3); // 拷贝构造
my_print(v1);
my_print(v2);
my_print(v3);
my_print(v4);
// vector赋值
v3 = v2;
v4.assign(v1.begin(), v1.end());
my_print(v3);
my_print(v4);
v4.assign(10, 100);
my_print(v4);
}
2.1.2 vector通用函数
vector容量、大小、插入、删除和访问元素:
void my_print(vector <int>&v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test1()
{
vector<int>v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
my_print(v1);
if (v1.empty())
{
cout << "v1为空" << endl;
}
else
{
cout << "v1不为空" << endl;
cout << "v1的容量为:" << v1.capacity() <<endl;
cout << "v1的大小为:" << v1.size() << endl;
}
cout << "重新指定大小:" << endl;
v1.resize(15, 100); // 如果重新指定的长度比原来长了,默认用0填充,这里是用100填充
my_print(v1);
v1.resize(5);
my_print(v1); // 如果短了,超出部分就删除掉
cout << "删除:" << endl;
v1.pop_back(); // 尾删
v1.erase(v1.begin()); // 传入迭代器参数
my_print(v1);
cout << "插入:" << endl;
v1.insert(v1.begin(), 100); // 第一个参数是迭代器
v1.insert(v1.begin(), 2, 50);
my_print(v1);
cout << "访问元素:" << endl;
cout << v1[0] << " " << v1.at(1) << endl;
cout << "第一个元素为:" << v1.front() << endl;
cout << "最后一个元素为:" << v1.back() << endl;
cout << "清空:" << endl;
v1.clear();
my_print(v1);
}
2.1.3 vector自定义类型+嵌套vector
vector+自定义类型程序如下
:
class Person
{
public:
Person(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
void showPerson()
{
cout << "姓名:" << this->m_name << "年龄:" << this->m_age << endl;
}
string m_name;
int m_age;
};
void test1()
{
vector <Person>v;
Person P1("Tom", 20);
Person P2("Tom", 20);
Person P3("Tom", 20);
v.push_back(P1); // 向容器中插入数据
v.push_back(P2);
v.push_back(P3);
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
{
cout << "姓名:" << (*it).m_name << " 年龄:" << (*it).m_age << endl;
//cout << "姓名:" << it->m_name << " 年龄:" << it->m_age << endl;
}
}
void test2()
{
vector <Person*>v;
Person P1("Tom", 20);
Person P2("Tom", 20);
Person P3("Tom", 20);
v.push_back(&P1); // 向容器中插入数据
v.push_back(&P2);
v.push_back(&P3);
for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) // it是一个二级指针,指向Person*的指针,解引用后是指向P1/P2/P3的指针,指针取数据的操作符为->
{
cout << "::姓名:" << (*it)->m_name << " 年龄:" << (*it)->m_age << endl;
}
}
嵌套vector程序如下
:
void test1()
{
vector <vector <int> >v;
// 创建小容器
vector <int>v1;
vector <int>v2;
vector <int>v3;
// 向小容器中添加数据
for (int i = 0; i < 3; i++)
{
v1.push_back(i + 1);
v2.push_back(i + 2);
v3.push_back(i + 3);
}
// 将小容器插入到大容器中
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
// 通过大容器,把所有数据都遍历一遍
for (vector < vector <int> >::iterator it = v.begin(); it != v.end(); it++)
{
// (*it) 是小容器 vector<int>
for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
{
cout << *vit << " " << endl;
}
cout << endl;
}
}
2.1.4 vector与swap函数
利用swap函数实现收缩内存
:
void my_print(vector <int>&v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test1()
{
vector<int>v1, v2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
v2.push_back(10 - i);
}
cout << "交换前:" << endl;
my_print(v1);
my_print(v2);
v1.swap(v2);
cout << "交换后:" << endl;
my_print(v1);
my_print(v2);
}
// 实际应用
// 巧用swap可以收缩内存空间
void test2()
{
vector<int>v;
for (int i = 0; i < 10000; i++)
{
v.push_back(i);
}
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
v.resize(3);
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
vector<int>(v).swap(v); // vector<int>(v)创建一个匿名对象,大小为当前v的大小,然后两个容器指针互换,匿名对象用完后空间就释放了,v的指针指向容量为3的内存空间。
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
}
利用swap函数预留空间
:
void test1()
{
vector<int>v1;
int* p = NULL;
int num = 0;
//v1.reserve(10000); // 多次开辟新空间,较为繁琐,可以利用reserve预留空间,以防数据容量过大,重新开辟为新空间的操作
for (int i = 0; i < 10000; i++)
{
v1.push_back(i);
if (p != &v1[0]) // 统计一共开辟了多少次空间
{
p = &v1[0];
num++;
}
}
cout << "num = " << num << endl;
}
2.2 string容器
string是C++风格的字符串,而string本质上是一个类。string和char*(char
是一个指针)区别:string是一个类,类内部封装了char
,管理这个字符串,是一个char*型的容器。因此string内部的索引和数组一样是从0开始。
string类内部封装了很多成员方法,例如:查找find,拷贝copy,删除delete替换replace,插入insert。string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
void test1()
{
string s1; // 默认构造
const char* str = "hello world";
string s2("hello c++");
string s3(str);
string s4(4, 'a');
cout << "s2 = " << s2 << endl;
cout << "s3 = " << s3 << endl;
cout << "s4 = " << s4 << endl;
}
void test1()
{
string s1;
s1 = "hello world";
string s2 = s1;
string s3;
s3 = 'a';
string s4;
s4.assign("hello world");
cout << "s1 = " << s1 << endl;
cout << "s2 = " << s2<< endl;
cout << "s3 = " << s3 << endl;
cout << "s4 = " << s4 << endl;
}
总结:string赋值的方式很多,一般只是用=方式赋值,简单实用。
string字符串拼接:
void test1()
{
string s1 = "我";
s1 += "爱你"; // 添加一个字符串""
s1 += ':'; // 添加一个字符''
string s2 = "中国";
s1 += s2;
string s3 = "I";
s3.append(" love you");
string s4 ;
s3.append(": China abcde", 7); // 将前七个字符加入字符串
s3.append("1234!6789", 4,1); // 第五个字符开始,往后一个添加如字符串
cout << "s1 = " << s1 << endl;
cout << "s2 = " << s2<< endl;
cout << "s3 = " << s3 << endl;
cout << "s4 = " << s4 << endl;
}
string字符串查找和替换:
void test1()
{
string s1 = "abcdef"; // 索引从零开始
int pos = s1.find("de");
if (pos == -1)
{
cout << "未找到字符串" << endl;
}
else
{
cout << "找到字符串pos = " << pos << endl;
}
pos = s1.rfind("de"); // rfind是从右往左差,find从左往右找
cout << "找到字符串pos = " << pos << endl;
// 从索引1起,三个字符,替换成1111,即bcd换成1111
s1.replace(1, 3, "1111");
cout << "s1 = " << s1 << endl;
}
string字符串比较实质上是依次按字符的ASCII进行对比,若相等返回0,大于返回1,小于返回-1。若第一个字符相等,则比较第二个,直至比较出大小或者字符串结束。
void test1()
{
string s1 = "dbc";
string s2 = "abc";
if ( s1.compare(s2) == 0)
{
cout << "相等" << endl; // 一般是比较是否相等
}
else if(s1.compare(s2) == 1)
{
cout << "大于" << endl;
}
else
cout << "小于" << endl;
}
string字符串存取:
void test1()
{
string s1 = "hello world";
// 1、通过[]访问单个字符
for (int i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
}
cout << endl;
// 2、通过at方式访问单个字符
for (int i = 0; i < s1.size(); i++)
{
cout << s1.at(i) << " ";
}
cout << endl;
// 修改单个字符
s1[0] = 'x';
s1.at(1) = 't';
cout << s1 << endl;
}
string字符串插入和删除:
void test1()
{
string s1 = "hello world";
s1.insert(1, "111"); // 插入,从索引1的位置开始插入
cout << s1 << endl;
s1.erase(1, 3); // 删除,从索引1的位置开始删除
cout << s1 << endl;
}
string字符串的子串获取:
// 从字符串中获取子串
void test1()
{
string s1 = "hello world";
string subStr1 = s1.substr(1, 3); // 从索引1位置开始,往后3个字符
cout << "subStr1 = " << subStr1 << endl;
// 实用操作 ---- 从邮件地址中获取用户名信息
string email = "zhangsan@sina.com";
int pos = email.find("@");
string username = email.substr(0, pos);
cout << "username = " << username << endl;
}
2.3 deque容器
双端数组,可以对头端进行插入删除操作。
deque容器和vector容器很相似,这里只给出函数原型。
构造函数原型:
赋值函数原型:
deque没有容量的概念,除了没有容量以外其他全部和vector一样:
插入、删除:
元素的存取:
deque排序:
void my_print(const deque <int>& d)
{
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test1()
{
deque<int>d1;
d1.push_back(2);
d1.push_back(55);
d1.push_back(18);
d1.push_back(99);
my_print(d1);
sort(d1.begin(), d1.end()); // 默认升序,对于支持随机访问的迭代器的容器(vector deque),都可以利用sort算法直接进行排序
cout << "排序后:" << endl;
my_print(d1);
}
2.4 stack容器
stack是一种先进后出的容器。栈可以为空,叫空栈。可以返回元素个数。栈中进入数据叫入栈,出数据叫出栈。
void test1()
{
stack<int>s;
s.push(10);
s.push(20);
s.push(30);
s.push(40);
cout << "栈的大小为:" << s.size() << endl;
while (!s.empty())
{
cout << "栈顶元素为:" << s.top() << endl;
s.pop();
}
cout << "栈的大小为:" << s.size() << endl;
}
2.5 queue容器
queue是一种先进先出的容器。队列允许从一端新增元素,另一端移除元素。只有对头和队尾才能被外界使用,因此队列不允许有遍历行为。队列中进数据叫入队,出数据叫出队。
2.6 list容器
数据以链式存储,list是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链实现的。链表由一系列节点组成, 节点包括存储数据的数据域和存放下一个数据地址的指针域。
链表的优点:采用动态内存分配,不会造成内存浪费和溢出;可以对任意位置进行插入或者删除元素,修改指针即可,不需要移动大量元素。缺点:容器的遍历速度没有数组快,占用空间比数据大。
链表当中的迭代器只支持前移和后移,属于双向迭代器。在STL容器当中,list和vector容器是最常用的两个容器。
list构造函数:
list赋值函数:
list大小操作函数:
list插入和删除函数:
list数据存取函数,不支持at和[ ]操作。原因是list本质链表,不是用连续线性空间存储数据的,迭代器也是不支持随机访问的。
void test1()
{
list<int>L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
// L1[0]; // 报错,不支持用[]访问list容器中的元素
// L1.at(0); // 报错,不支持用at访问list容器中的元素
cout << "第一个元素为:" << L1.front() << endl;
cout << "最后一个元素为:" << L1.back() << endl;
list<int>::iterator it = L1.begin();
it++;
it--;
//it = it + 1; // 报错,如果成立那么也就可以+2,跳着访问(随机访问),而链表不允许随机访问。
//原因是list本质链表,不是用连续线性空间存储数据的,迭代器也是不支持随机访问的。
}
反转和排序:
所有不支持随机访问迭代器的容器,不可以用标准算法,例如sort( ),不支持随机访问迭代器的容器,内部会提供对应的一些算法。这里只能用L.sort();,不能用sort(L.begin(), L.end());。
2.7 set容器
所有元素在插入时会自动被排序。
set容器的大小和交换:
set容器的插入和删除:
set容器的查找和计算元素个数:
set容器和multiset的区别:
set容器排序规则。默认排序规则为从小到大,如何改变排序规则?
class Mycompare
{
public:
bool operator()(int v1, int v2) const
{
return v1 > v2;
}
};
void my_print1(const set <int>& s)
{
for (set<int>::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void my_print2(const set <int, Mycompare>& s)
{
for (set<int, Mycompare>::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test1()
{
set<int>s1;
s1.insert(10);
s1.insert(40);
s1.insert(20);
s1.insert(50);
s1.insert(30);
my_print1(s1);
set<int, Mycompare>s2;
s2.insert(10);
s2.insert(40);
s2.insert(20);
s2.insert(50);
s2.insert(30);
my_print2(s2);
}
set容器自定义类型的自定义排序:
class Person
{
public:
Person(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
string m_name;
int m_age;
};
class Mycompare
{
public:
bool operator()(const Person &p1, const Person &p2) const
{
return p1.m_age > p2.m_age;
}
};
void test1()
{
set<Person, Mycompare>s;
Person p1("张三", 18);
Person p2("李四", 98);
Person p3("王五", 8);
Person p4("赵六", 28);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
for (set<Person, Mycompare>::iterator it = s.begin(); it != s.end(); it++)
{
cout << "姓名:" << it->m_name << " 年龄:" << it->m_age << endl;
}
}
2.8 pair对组
成对出现的数据,利用对组可以返回两个数据。不需要包含头文件。
void test1()
{
pair<string, int>p("Tom", 20);
cout << "姓名:" << p.first << " 年龄: " << p.second << endl;
pair<string, int>p2 = make_pair("Jerry", 18);
cout << "姓名:" << p2.first << " 年龄: " << p2.second << endl;
}
2.9 map容器
map容器中所有的元素都是pair,pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值),所有元素都会根据元素的键值自动排序。
map/multimap属于关联式容器,底层结构是用二叉树实现。优点:可以根据key值快速找到value值。
map中不允许容器中有重复key值元素,multimap允许容器中有重复key值元素。
void my_print(map<int, int>& m)
{
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key = " << (*it).first << " value = " << it->second << endl;
}
}
void test1()
{
map<int, int>m;
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(2, 20));
m.insert(pair<int, int>(3, 30));
m.insert(pair<int, int>(4, 40));
my_print(m);
}
map容器的大小和交换:
map容器的插入、删除、排序:
class mycompare
{
public:
bool operator()(int v1, int v2) const
{
return v1 > v2;
}
};
void my_print(map<int, int,mycompare>& m)
{
for (map<int, int, mycompare>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key = " << (*it).first << " value = " << it->second << endl;
}
}
void test1()
{
map<int, int, mycompare>m;
m.insert(pair<int, int>(1, 10)); // 第一种插入
m.insert(make_pair(2, 20)); // 第二种插入
m.insert(map<int, int>::value_type(3, 30)); // 第三种插入
m[4] = 40; // 第四种插入
my_print(m);
}
map容器的查找和计数:
/*
函数原型:
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end();
count(key); //统计key的元素个数
*/
三、 STL函数对象
class Myadd
{
public:
int operator()(int v1, int v2) const
{
return v1 + v2;
}
};
// 1、函数对象在使用时,可以向普通函数那样调用,可以有参数,可以有返回值
void test1()
{
Myadd myadd;
cout << myadd(10, 10) << endl;
}
// 2、函数对象超出普通函数的概念,函数对象可以有自己的状态
class Myprint
{
public:
Myprint()
{
this->counter = 0;
}
void operator()(string test)
{
cout << test << endl;
this->counter++;
}
int counter;
};
void test2()
{
Myprint myprint;
myprint("hello world");
myprint("hello world");
myprint("hello world");
cout << "myprint调用次数为: " << myprint.counter << endl;
}
// 3、函数对象可以作为参数传递
void doprint(Myprint &mp, string test)
{
mp(test);
}
void test3()
{
Myprint myprint;
doprint(myprint, "hello c++");
}
3.1 谓词
class GreaterFive
{
public:
bool operator()(int val) // 一元谓词
{
return val > 5;
}
};
void test1()
{
vector<int>v;
for (int i = 1; i < 6; i++)
{
v.push_back(i*10);
}
vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
if (it == v.end())
{
cout << "未找到" << endl;
}
else
{
cout << "找到了大于5的数字为:" << *it << endl;
}
}
class mycompare
{
public:
bool operator()(int val1, int val2) const // 二元谓词
{
return val1 > val2;
}
};
void myprint(vector <int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end();it++)
{
cout << (*it) << " ";
}
cout << endl;
}
void test1()
{
vector<int>v;
v.push_back(10);
v.push_back(40);
v.push_back(20);
v.push_back(30);
v.push_back(50);
myprint(v);
sort(v.begin(), v.end()); // 默认是升序
myprint(v);
sort(v.begin(), v.end(), mycompare());
//sort(v.begin(), v.end(), greater<int>()); // 两句作用相同
myprint(v);
}
3.2 内建函数对象
STL内建了一些函数对象,可以分为算术仿函数,关系仿函数,逻辑仿函数,这些仿函数所产生的对象,用法和一般函数完全相同,使用内建函数对象,需要引用头文件functional。
void test1()
{
negate<int>n;
cout << n(50) << endl;
plus<int>p; // 二元运算默认是同种型相加
cout << p(10, 20) << endl;
}
void myprint(vector <bool>& v)
{
for (vector<bool>::iterator it = v.begin(); it != v.end();it++)
{
cout << (*it) << " ";
}
cout << endl;
}
void test1()
{
vector<bool>v;
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(false);
myprint(v);
// 利用逻辑非 将容器v搬运到容器v2中 并执行取反操作
vector<bool>v2;
v2.resize(v.size());
transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());
myprint(v2);
}
四、 STL常用算法
4.1 遍历算法
for_each是实际开发当中最常用的遍历算法,需要熟练掌握。transform具体用法可见:3.4.10.2 内建函数对象,_func是搬运的规则。
// 普通函数
void normal_print(int val)
{
cout << val << " ";
}
// 仿函数
class print2
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void myprint(vector <bool>& v)
{
for (vector<bool>::iterator it = v.begin(); it != v.end();it++)
{
cout << (*it) << " ";
}
cout << endl;
}
void test1()
{
vector<int>v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
for_each(v.begin(), v.end(), normal_print); // 普通函数只要把函数名放进去
cout << endl;
for_each(v.begin(), v.end(), print2()); // 仿函数要把函数对象放进去
cout << endl;
}
void test1()
{
vector<int>v;
v.push_back(1);
v.push_back(5);
v.push_back(3);
v.push_back(6);
v.push_back(9);
v.push_back(4);
v.push_back(4);
v.push_back(2);
// 查找相邻重复元素
vector<int>::iterator it = adjacent_find(v.begin(), v.end());
if (it == v.end())
{
cout << "找不到重复元素" << endl;
}
else
{
cout << "找到相邻重复元素为:" << *it << endl;
}
}
class Person
{
public:
Person(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
bool operator==(const Person& p)
{
if (this->m_age == p.m_age)
{
return true;
}
else
{
return false;
}
}
string m_name;
int m_age;
};
void test1()
{
vector<int>v;
v.push_back(10);
v.push_back(40);
v.push_back(30);
v.push_back(40);
v.push_back(20);
v.push_back(40);
int num = count(v.begin(), v.end(), 40);
cout << "40的元素个数为: " << num << endl;
}
void test2()
{
vector<Person>v;
Person p1("张三", 35);
Person p2("李四", 35);
Person p3("王五", 35);
Person p4("赵六", 40);
Person p5("李二", 30);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
Person p6("柳大", 35);
int num = count(v.begin(), v.end(), p6);
cout << "和张三同岁数的人数:" << num << endl;
}
4.2 排序算法
void myprint(vector <int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end();it++)
{
cout << (*it) << " ";
}
cout << endl;
}
void test1()
{
srand((unsigned int)time(NULL)); // 需要用ctime头文件
vector<int>v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
random_shuffle(v.begin(), v.end());
myprint(v);
}
void myprint(vector <int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end();it++)
{
cout << (*it) << " ";
}
cout << endl;
}
void test1()
{
srand((unsigned int)time(NULL)); // 需要用ctime头文件
vector<int>v1;
vector<int>v2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
v2.push_back(i+1);
}
vector<int>vTarget;
vTarget.resize(v1.size() + v2.size());
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
myprint(vTarget);
// 注意两个容器必须是有序的,并且同时都是升序或者都是降序
}
4.3 拷贝和替换算法
使用copy算法在拷贝时,目标木器记得提前开辟空间。
替换的是将所有旧元素换成新元素。
交换的容器要同种类型。
4.4 算术算生成算法
算术生成算法属于小型算法,使用时包含的头文件为numeric。
void test1()
{
vector<int>v;
for (int i = 0; i <= 100; i++)
{
v.push_back(i);
}
int total = accumulate(v.begin(), v.end(), 0); // 参数三是起始累加值
cout << "total = " << total << endl;
}
利用fill可以将容器区间内的元素填充为指定的值。
4.5 集合算法
void myprint(vector <int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end();it++)
{
cout << (*it) << " ";
}
cout << endl;
}
void print1(const int val)
{
cout << val << " ";
}
void test1()
{
vector<int>v1;
vector<int>v2;
for (int i = 0; i < 10; i++)
{
v1.push_back(i); // 0-9
v2.push_back(i + 5); // 5-14
}
myprint(v1);
myprint(v2);
vector<int>vTarget;
vTarget.resize(min(v1.size(), v2.size()));
vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
for_each(vTarget.begin(), itEnd, print1);
cout << endl;
}
总结:求交集的两个集合必须为有序序列,目标容器开辟空间需要从两个容器中取小值,set_intersection返回值既是交集中最后一个元素的位置。
总结:求并集的两个集合必须为有序序列,目标容器开辟空间需要从两个容器相加,set_union返回值既是并集中最后一个元素的位置。
总结:求差集的两个集合必须为有序序列,目标容器开辟空间需要从两个容器中取较大值,set_difference返回值既是差集中最后一个元素的位置。