C++ 手册

闲杂

1
2
3

Array < Stack<int> > array_stack;
//在C++98中,要求至少用一个空白符将两个>符号分开,以免与运算符>>混淆,C++11不要求这样做

花括号与分号

在结构体与类定义大括号后面需要分号;
其余可要可不要
if else、try catch等组合语句如果在中间加了分号会将一个语句块分成两个

cmath

1
2
3
4
5
6
7
8
9
#include <cmath>
or
#include <math.h>

ceil(),floor() 向上、向下取整,不在命名std里面。

ceil(5/2); // 2
ceil(5.0/2); // 3
floor(5.0/2); // 2:

数组

对于内置数据类型元素的数组,必须使用()来显示指定程序执行初始化操作,否则程序不执行初始化操作:

1
2
3
int *pia = new int[10]; // 每个元素都没有初始化

int *pia2 = new int[10] (); // 每个元素初始化为0

类类型元素的数组,则无论是否使用(),都会自动调用其默认构造函数来初始化:

1
2
3
string *psa = new string[10];  // 每个元素调用默认构造函数初始化

string *psa = new string[10](); // 每个元素调用默认构造函数初始化

std::find()

返回范围 [first, last) 中满足特定判别标准的首个元素迭代器,查找失败则返回end迭代器

std::sort()

1
2
3
4
5
6
7
8
9
10
11
12
// 自定义函数必须写在最外吗,否则无法通过
// 在类内部, 可以写成静态函数.
bool mysort(int a, int b){ return a>b; } //降序
int main(){
std::vector<int> v = {2,3,5,787,8,5};

std::sort(v.begin(), v.end(), mysort);

for(auto iter = v.begin(); iter!= v.end(); iter++){
std::cout<<* iter<<std::endl;
}
}

lamda 表达式:

1
2
3
std::sort(s.begin(), s.end(), [](int a, int b) {
return b < a;
});

重载operator()运算符

1
2
3
4
5
6
7
struct {
bool operator()(int a, int b) const
{
return a < b;
}
} customLess;
std::sort(s.begin(), s.end(), customLess); //升序

std::reverse()

反转[first, last)范围中的元素顺序. (借助 std::swap()实现)

1
2
3
std::vector<int> v{1,2,3};
std::reverse(std::begin(v), std::end(v));
std::reverse(v.begin(), v.end());

vector

常用操作

截取vector中的一部分作为一个新的vector

1
2


清空:clear()

在最后添加元素:push_back()

初始化

1
2
3
vector<string> v3(5, "hello"); // 创建有5个值为“hello”的string类对象的容器

std::vector<int> v = {7, 5, 16, 8};

判断某元素是否存在

1
2
3

vector<string> vStr;
int nRet = std::count(vStr.begin(), vStr.end(), "abc");//返回向量中,“abc”元素的个数

at:访问指定字符,有边界检查 str.at(1)

front:访问首字符 (C++11) str.front()

back:访问最后的字符(C++11)Cpp

string类

常用操作

截取子串

s.substr(pos, n) 截取s中从pos开始(包括0)的n个字符的子串,并返回

s.substr(pos) 截取s中从从pos开始(包括0)到末尾的所有字符的子串,并返回

替换子串

s.replace(pos, n, s1) 用s1替换s中从pos开始(包括0)的n个字符的子串

查找子串

s.find(s1) 查找s中第一次出现s1的位置,并返回(包括0)

s.rfind(s1) 查找s中最后次出现s1的位置,并返回(包括0)

s.find_first_of(s1) 查找在s1中任意一个字符在s中第一次出现的位置,并返回(包括0)

s.find_last_of(s1) 查找在s1中任意一个字符在s中最后一次出现的位置,并返回(包括0)

s.fin_first_not_of(s1) 查找s中第一个不属于s1中的字符的位置,并返回(包括0)

s.fin_last_not_of(s1) 查找s中最后一个不属于s1中的字符的位置,并返回(包括0)

判断字符串string里面是否含有某个字符串

利用string::size_type string::find(string &);函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

#include <iostream>
#include <string>
using namespace std;
int main()
{
string a="abcdefghigklmn";
string b="def";
string c="123";
string::size_type idx;

idx=a.find(b);//在a中查找b.
if(idx == string::npos )//不存在。
cout << "not found\n";
else//存在。
cout <<"found\n";
idx=a.find(c);//在a中查找c。
if(idx == string::npos )//不存在。
cout << "not found\n";
else//存在。
cout <<"found\n";
return 0;
}

c++字符串比较大小的两种方法

  • compare函数的使用:
1
2
3
4
5
6
7
8
9

#include <iostream>
using namespace std;
int main(){
string str1="hello";
cout<<str1.compare("helloo")<<endl;//返回-1;
cout<<str1.compare("hello")<<endl;//返回0 ;
cout<<str1.compare("hell")<<endl;//返回1;
}
  • 使用strcmp(aa1.c_str(),bb2.c_str())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <cstring>
using namespace std;
int main(){
char* str1="hello";
char* str2="hell";
char* str3="helloo";
char* str4="hello";

//原型extern int strcmp(const char* s1,const char* s2);
cout<<strcmp(str1,str2)<<endl;//返回1;
cout<<strcmp(str1,str3)<<endl;//返回-1;
cout<<strcmp(str1,str4)<<endl;//返回0.
}

统计字符串中某个字符出现了多少次

使用算法库里面的count函数,使用方法是count(begin,end,‘a’),其中begin指的是起始地址,end指的是结束地址,第三个参数指的是需要查找的字符

1
2
3
4
5
6
7
8
9
10
11
12

#include <iostream>
#include <algotirhm>
#include <string>
using namespace std;
int main()
{
string temp = "aaabcdaaa!!!";
int num = count(temp.begin(),temp.end(),'a');
cout <<"在字符串" << temp << "中," <<"字母a出现的次数是" << num << endl;
return 0
}

元素访问

at:访问指定字符,有边界检查 str.at(1)

front:访问首字符 (C++11) str.front()

back:访问最后的字符(C++11)Cpp

c_str:返回不可修改的C字符数组版本(带’\0’) str.c_str()

交换string的值

成员函数: string::swap(string& str) 主要用于交换两个string的值,用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

// swap strings
#include <iostream>
#include <string>

main ()
{
std::string buyer ("money");
std::string seller ("goods");

std::cout << "Before the swap, buyer has " << buyer;
std::cout << " and seller has " << seller << '\n';

seller.swap (buyer);

std::cout << " After the swap, buyer has " << buyer;
std::cout << " and seller has " << seller << '\n';

return 0;
}

/*
output:
Before the swap, buyer has money and seller has goods
After the swap, buyer has goods and seller has money
*/

非成员函数std::swap()可以将string内部的两个元素进行交互。同时,也可以对两个string进行交换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#include <iostream>
#include <string>

int main(){
std::string str1 = "abc";
std::swap(str1.at(0), str1.at(1));
std::cout<<str1<<std::endl;
std::string str2 = "def";
std::swap(str1, str2);
std::cout<<str1<<std::endl;
return 0;
}

/*
output:
bac
def
*/

vector

连续存储结果, 每个元素在内存上是连续的, 支持 高效的随机访问和尾端插入/删除操作, 但其他位置的插入/删除操作效率低下, 可以看做是一个数组, 但是与数组的区别为: 内存空间的扩展. vector 支持动态大小的数据存储, 而数组则必须指定大小, 但需要扩展空间时, 需要手动实现.

vector的内存分配原理及实现:
在STL内部实现时, 首先分配一个较大的内存空间预备存储, 即capacity()函数返回的大小, 当超过此分配的空间时, 会再重新分配一块更大的内存空间(VS6.0是两倍, VS2005是1.5倍). 通常默认的内存分配能完成大部分情况下的存储操作. 扩展空间的步骤:

  1. 配置一块新空间
  2. 将就元素一一移动到新地址中
  3. 把原来的空间释放掉

vector的数据安排以及操作方式, 与数组模板Array十分相似, 两者唯一的差别在于空间利用的灵活性, Array的空间扩展需要手动实现

deque

双端队列: double-end queue
连续存储结果, 即每个元素在内存上是连续的, 类似于vector, 不同之处在于, deque提供了两级数组结构, 第一级完全类似于vector, 代表实际容器, 另一级维护容器的首位地址, 这样, deque除了具有vector的所有功能之外, 还支持高校的首/尾端的插入/删除操作.

优点:

  1. 随机访问方便, 支持[]操作符和.at()访问函数
  2. 可在两端进行push, pop操作

缺点:
占用内存多

list

非连续存储结构, 具有两链表结构, 每个元素维护一对前向和后向指针, 因此支持前向/后向遍历. 支持高效的随机插入/删除操作, 但是随机访问效率低下, 且由于需要额外维护指针, 开销也比较大. 每一个节点都包括一个信息块info, 一个前驱指针Pre, 一个后驱指针Post.

优先:

  1. 不使用连续内存完成插入和删除操作
  2. 在内部方便的进行插入和删除操作
  3. 可以在两端push, pop

缺点:

  1. 不能进行随机访问, 即不支持[]操作符和.at()访问函数
  2. 相对于vector占用内存多

list与vector的区别

  1. vector为存储的对象分配一块连续的地址空间, 随机访问效率很高. 但是插入和删除需要移动大量的数据, 效率较低. 尤其当vector内部元素较复杂, 需要调用复制构造函数时, 效率更低.
  2. list中的对象是离散的, 随机访问需要遍历整个链表, 访问效率比vector低, 但是在list中插入元素, 尤其在首尾插入时, 效率很高.
  3. vector 是 单向的 的, 而 list 是双向的 (vector为什么单向???)
  4. vector 中的 iterator 在使用后就释放了, 但是 list 不同, 它的迭代器在使用后还可以继续使用, 是链表所特有的.

queue

deque

map

1、map简介

Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据 处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一 种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。
map是一类关联式容器。它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。
对于迭代器来说,可以修改实值,而不能修改key。

2、map的功能

  • 自动建立Key - value的对应。key 和 value可以是任意你需要的类型。
  • 根据key值快速查找记录,查找的复杂度基本是Log(N),如果有1000个记录,最多查找10次,1,000,000个记录,最多查找20次。
  • 快速插入Key -Value 记录。
  • 快速删除记录
  • 根据Key 修改value记录。
  • 遍历所有记录。
    -

    3. 使用map

1
#include <map>  //注意,STL头文件没有扩展名.h

4. map的构造函数

map共提供了6个构造函数,这块涉及到内存分配器这些东西,略过不表,在下面我们将接触到一些map的构造方法,这里要说下的就是,我们通常用如下方法构造一个map:

1
map<int, string> mapStudent;

5. 数据的插入

unordered_map

map的区别

在STL中, map对应的数据结构是红黑树, 红黑树是一种近似于平衡的二叉查找树, 里面的数据是有序的, 在红黑树上做查找的时间为 $O(lonN)$. 而unordered_map对应哈希表, 哈希表的特点就是查找效率高, 时间复杂度基本为 $O(1)$, 而额外空间复杂度较高.

基本使用

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <unordered_map>
#include <string>

int main(){
std::unordered_map<int, std::string> hmap;
hmap.insert(std::make_pair(1, "Scala"));
hmap.insert({3, "three"});
hmap.insert({ {4, "Four"}, {5, "Five"}});
cout<<hmap[1]
}

set

基于红黑树实现

unordered_set

基于哈希表实现, 会将传入的值处理成相应的键值, 该键值对应着一个特定的位置, 因此, unordered_set 的各项操作的复杂度平均为常数级(最差为线性), 同时, unordered_set 也是一种 set, 因此, 其关键字不能有重复(重复的会自动合并).

1
2
3
4
5
6
7
8
9
10
11
12
13
unordered_set<string> stringSet;
stringSet.insert("code");
stringSet.insert("fast");

string key1 = "fast";
stringSet.find(key1); // return iter, 指向 fast
string key2 = "slow"
stringSet.find(key2); // return stringSet.end()

vector<int> nums {1,2,3,4,5,6,7,8,9};
unordered_set<int> sets(nums.begin(), nums.end())
int key;
sets.count(key); // 返回拥有关键key的元素个数, 即只会返回1或0.

优先级

& | 的优先级较低, 通常情况下需要使用括号括起来.

三元运算符 ?: 的优先级也很低, 当其进行算数运算时, 也建议使用括号括起来.