多重映射 ------ multimap
多重映射就是在映射的基础上允许key重复(一对多)
使用需要包含头文件:#include
特性
1.允许key重复
A - 100
A - 200
A - 300
2.不支持关于key的下标运算
新增成员函数
//获取匹配的上限 -----> 返回首个大于给定key的迭代器
iterator upper_bound(const key_value &key);
//获取匹配的下限 -----> 返回首个不小于给定key的迭代器
iterator lower_bound(const key_value &key);
//同时获取上下线
pair<iterator,iterator> equal_range(const key_value &key);
A - 100
B - 200 ----- B的下限
B - 300
B - 400
C - 500 ----- B的上限
//find查找是返回任意一个匹配key的元素,一般不使用 //获取匹配的上限 -----> 返回首个大于给定key的迭代器 iterator upper_bound(const key_value &key); //获取匹配的下限 -----> 返回首个不小于给定key的迭代器 iterator lower_bound(const key_value &key); //同时获取上下线 pair<iterator,iterator> equal_range(const key_value &key); A - 100 B - 200 ----- B的下限 B - 300 B - 400 C - 500 ----- B的上限 //find查找是返回任意一个匹配key的元素,一般不使用
集合 -------- set
集合就是没有value的映射
使用需要包含头文件:#include
使用:
set<key类型> set对象
set<key类型,比较器类型> set对象;
多重集合
多重集合是没有value的多重映射
允许key重复,其余和set一样
使用需要包含头文件:#include
C++11新增关联容器
C++新增了4种无序的关联容器,内部使用哈希表管理key,实现常数时间的查找。
unordered_set
unordered_map
unordered_multiset
unordered_multimap
补充 ----- 快速排序
算法:
1.从待排序序列中任意挑选一个元素,作为基准
2.将所有小于基准的元素放到基准之前,所有大于基准的元素放到基准之后,等于基准的元素随意
这个过程叫做分组
3.采用递归,分别对基准之前和基准之后的分组继续进行分组
直到每个分组内的元素不多于一个为止
算法实现
1.记录该组第一个(L)和最后一个(R)的下标,取出左边作为基准
2.用右边指向的元素和基准比较,大于等于基准将R左移;小于基准将R指向的元素赋值给L指向的元素
换左边和基准比较
3.用左边指向的元素和基准比较,小于等于基准将L右移;大于基准将L指向的元素赋值给R指向的元素
换右边和基准比较
4.重复2~3步,直到L和R重合,将基准赋值给重合的位置,完成一次分组
评价
空间复杂度:O(logN)
时间复杂度:O(NlongN) 最差 --- O(N^2)
如果每次分组都能做到均匀分组,可以达到最快排序速度
属于非稳定排序
快速排序的优化
快速排序的速度取决于分组是否均匀,为了尽可能达到均匀分组,要找到一个尽可能接近中间值的数。
通常可以找三个数中的中间值数,三个数可以选第一个,最后一个和中间那个
C++11新特性
新的初始化语法
C++11扩展了大括号进行初始化的范围,可以用于所有的基本类型和类类型。初始化时"="可加可不加。
int x = {5};
short s_arr[5] {4,5,6,7,8};
int *pa = new int[4] {1,2,3,4};
class A{
public:
//......
private:
int id;
double weight;
};
A a1{1,4.3};
A a2{5,7.6};
新的声明语法
auto
C++11扩展了auto的用法,实现了自动类型推断
auto num = 10;//int
auto pt = #//int *
int add(int,int);
auto pf = add;//函数指针类型 int(*)(int,int);
list<int> li;
auto it = li.begin();//迭代器类型 list<int>::iterator
decltype
关键字decltype的作用是将变量的类型声明为表达式指定的类型,语法如下:
decltype(x) y;//x是一个表达式,y是变量名
int n;
double x;
decltype(x*n) p;//p是double类型
decltype(&x) q;//q是double *类型
//模板中使用
template <typename T,typename K>
void func(T t,K k)
{
decltype(t*k) tk;//tk就是t*k的类型
//....
}
返回值类型后置
C++11新增了一种返回值类型后置的语法,将函数的返回值写到参数列表之后,语法如下:
double add(double,int);
auto add(double,int) -> double;//返回double
//模板中使用
template <typename T,typename K>
auto func(T t,K k) -> decltype(t*k)
{
return t*k;
}
模板别名
C++新增了使用 using= 创建别名的语法。
等价于typedef的用法
using IT = array<char,10>::iterator;//相当于typedef
using=可以用于模板实例化
template <typename T>
using arr12 = array<T,12>;
array<double,12> <=====> arr12<double>
arrar<string,12> <=====> arr12<string> template <typename T> using arr12 = array<T,12>; array<double,12> <=====> arr12<double> arrar<string,12> <=====> arr12<string>
新的for循环用法
C++11扩展了for的用法,简化了对于数组/容器的遍历,比如:
int arr[] = {1,2,3,4,5,6,7,8,9};
for(int x:arr)
cout<< x<<" ";
cout<<endl;
//和auto结合使用
for(auto x:arr)
cout<< x<<" ";
cout<<endl;
vector<int> vi(arr,arr+9);
//修改容器
for(auto &x:vi)
x = rand();
//遍历容器
for(auto x:vi)
cout<< x<<" ";
cout<<endl;
右值引用
C++原有的引用属于左值引用,C++11扩展了右值引用,就是使用引用指向一个右值类型的数据。
int x = 10;
int y = 33;
//右值引用
int &&r1 = 11;
int &&r2 = x+y;
double &&r3 = sqrt(3.0);
//右值引用和常量的区别在于可以获取右值引用的地址
移动语义c
如果类中有动态分配的内存,C++的拷贝过程必须重新分配新的内存,并且拷贝内存中的数据。在某些时候(比如返回值返回),拷贝完成之后原对象已经不再需要,如果还是沿用之前的拷贝操作,相当于将一个对象拷贝到另一个位置,再释放原对象。这种操作显然不恰当,移动语义就是在这种情形下,并不去拷贝原始的数据,只是修改数据的记录。
vector<int> func() {
vector<int> vi;
//...
return vi;
}
使用情形:
在使用右值初始化或者返回对象会调用移动构造函数,使用右值赋值会调用移动赋值运算符重载函数。
//移动构造函数
类名(对象的右值引用)
{
//...
}
//移动赋值运算符重载函数
本对象引用 operaotr=(对象的右值引用)
{
//...
}
//编译默认会优化移动操作,可以添加编译选项 -fno-elide-constructors,去掉编译对构造类函数的优化
λ(Lambda)表达式/函数
Lambda函数也叫匿名函数,提供了一种新的实现传递函数作为参数的用法,也就是可以在传递参数时直接定义函数。
语法
函数对象的参数](重载操作符的参数)->返回值类型
{
函数体
}
格式
[]中用来传递外部数据进Lambda表达式,多个数据用,分开,可以传引用
()中是使用Lambda表达式的形参
返回值类型是void或者函数体只有一条return语句是可以省略
C++书籍
C++ primer plus
C++并发编程 ----- 多任务
Effective C++