易忘的用法
一、输入输出
1、特殊输入输出格式
输出输入控制符:
%x:以16进制读入输出整型变量
%nd:以n字符宽度输出整数,宽度不足时用空格填充
%0nd:以n字符宽度输出整数,宽度不足时用0填充
2、cin
cin >> m >> n 是一个表达式,如果成功读入所有变量,返回true,否则返回 false。
//读到没有字符时停止输入
int n, m;
while (cin >> n >> m) {
printf("%d", n + m);
}
//读入每一个字符,不自动跳过空格
int c; //返回值是ASCII码值
while ((c = cin.get()) != EOF) {
cout<< (char)c; //强转
}
//读入一行到字符数组
char line[10];
int length = sizeof(line);
//getline不会读入\n, 但是会在输入流中去掉它
//所以length >= 输入时该行的字符数时,不需要getchar()去吸收\n
cin.getline(line, length); //最多读 length - 1 个,因为需要留一个位置给\0。
cin在读入char型数据时,会自动跳过空格,但是scanf就不会。
假如需要cin每个字符都读取,可以用cin. get()函数,但是该函数返回值不是char类型,而是ASCII码值,而且当没得字符读取时,会返回-1。
EOF就是 - 1,它本身不是什么输入截止的标志,只是get函数读不到会返回 - 1 而已。
3、scanf
scanf本身也只是一个函数,其返回值是成功读入的变量个数(为 int 类型),假如读不到就返回 - 1。
scanf 对字符数组读入字符串时,遇到空格或者换行符会停下,并且在末尾补上 ’ \0 ’ 。
char line[100];
scanf("%s", line); //这里不需要取址符号,因为line本身就是一个指向数组起始地址的指针
char str[1024];
scanf("%[^\n]", &str); //遇到换行符停止输入(可将里面的\n替换成任意需要停下的字符)
4、gets
头文件:stdio.h(c),cstdio(c++)
//读入一行到字符数组
//会自动在末尾补\0,不会读入\n,但和cin.getline一样会把\n流掉
//可以无限读取,不会判断上限,以回车结束读取,所以应该确保s的空间足够大,不然容易溢出
char s[10];
while (gets(s)) { //读不到后会返回 0,读的到就返回正数
printf("%s\n", s);
}
其他
- 在输入时,ctrl + z 表示输入结束。
- cin 和 scanf不要同时使用,cout 和 printf 也不要同时使用。
二、库函数 / 头文件
1、数学函数
# include<cmath>
int abs(int x);
double fabs(double x); //求浮点数绝对值
double cos(double x);
double sin(double x);
int ceil(double x); //求不小于x的最小整数 (向上取整),例如ceil(3.14)== 4。
//向下取整可以:(int) x
double sqrt(double x);
2、字符串函数
1 ~ 5 :连接、找c第一次出现、找c最后一次出现、找substr第一次出现、字符串比较
6 ~ 10 :大小写无关字符串比较、复制、求长度、转小写、转大写
11 ~ 15 :连接前n个、比较前n个、复制前n个、抽出以 * 分割的字符、字符串转整数
16 ~ 17 :字符串转浮点数、整数转 x 进制的字符串
#include<cstring>
1. char *strcat(char *dest, const char * src);
将字符串src连接到dest后面。执行后src不变,dest变长了。返回值是dest.
2. char *strchr(const char *str, int c);
寻找字符C在字符串str中第一次出现的位置。
如果找到,就返回指向该位置的 char * 指针;
如果str中不包含字符c,则返回NULL.
3. char *strrchr(const char *str, char c);
寻找字符c在字符串str中最后一次出现的位置。
如果找到,就返回指向该位置的char*指针;
如果str中不包含字符c,则返回NULL.
4. char *strstr(const char *str, const char *subStr);
寻找子串subStr在str中第一次出现的位置。
如果找到,就返回指向该位置的指针;
如果str中不包含字符串subStr,则返回NULL.
5. int strcmp(const char *s1,const char *s2);
字符串比较。如果s1小于s2,则返回负数;
如果s1等于s2,则返回0;s1大于s2,则返回正数。 (注意是以ASCII码值比较,Z < a)
6. int stricmp(const char *s1,const char *s2);
大小写无关的字符串比较。
如果sl小于s2,则返回负数;如果sl等于s2,则返回0; s1大于s2,则返回正数。
不同的编译器实现此函数的方法有所不同,有的编译器是将s1、s2都转换成大写字母后再比较;
有的编译器是将s1、s2都转换成小写字母再比较。
这样,在s1或s2中包含ASCII 码介于‘Z'和‘a’之间的字符时
(即 ‘ [ ’、‘ \ ’、‘’ ] 、' ^ ’、‘ _ ’、‘ ` ' 这6个字符)
不同编译器编译出来的程序,执行stricmp的结果就可能不同。
7. char *strcpy(char *dest, const char *src);
将字符串src复制到dest。返回值是dest。
8. int strlen(const char *s);
求字符串s的长度,不包括结尾的 ' \0 '.
(或者:int size(char *s);)
9. char *strlwr(char *str);
将str中的字母都转换成小写。返回值就是str.
10. char *strupr(char *str);
将str中的字母都转换成大写。返回值就是str。
11. char *strncat(char * dest, const char *src, int n);
将src的前n个字符连接到dest尾部。如果src长度不足n.则连接src全部内容。返回值是 dest.
12. int strncmp(const char *s1, const char *s2, int n);
比较s1前n个字符组成的子串和s2前n个字符组成的子串的大小。
若长度不足n,则取整个串作为子串。返回值和strcmp类似。
13. char *strncpy(char *dest, const char *src, int n);
复制src的前n个字符到dest.如果src长度大于或等于n,
该函数不会自动往dest中写入‘ \0 ';
若src长度不足n.则复制src的全部内容以及结尾的^\0’到dest,返回值是dest。
14. char * strtok(char *str, const char *delim);
连续调用该函数若干次,可以做到:
从str中逐个抽取出被字符串delim 中的字符分隔开的若干个子串。
用法:
char str[] = "- This, a sample string, OK.";
char *p = strtok(str, " ,.-"); //str将被空格、逗号、句号、横杠分隔出子串
while (p != NULL) {
cout<< p << endl;
p = strtok(NULL, " ,.-"); //后续调用,第一个参数必须是NULL!!!
}
15. int atoi(char *s);
将字符串s中的内容转换成一个整型数返回。
例如,如果字符串s的内容是“1234”,那么函数返回值就是1234。
如果s格式不是一个整数,如是"a12" ,那么返回0。
16. double atof(char *s);
将字符串s中的内容转换成实数返回。
例如,"12. 34"就会转换成12. 34。
如果S的格式不是一个实数,则返回0。
17. char *itoa(int value, char *string, int radix);
将整型值 value 以 radix 进制表示法写入string。例如 :
char szValue[ 20];
itoa(27, szValue, 10); //使得szValue的内容变为"27"
itoa(27, szValue, 16); //使得szValue的内容变为"1b”
3、内存操作库函数
头文件: #include
(1)置值函数 :void * memset(void * dest, int ch, int n);
将从dest开始的n个字节,都设置成ch。返回值是dest。ch只有最低的字节才起作用。
【常用方式】
memset(arr, 0, sizeof(arr));
【易错用法】
ch只有最低字节起作用,所以是拿ch的最低字节赋值给dest的每一个字节。又由于在下面的例子中,数组是int型,每一个数组元素占了4个字节,所以是arr[ i ] 的每一个字节都是 0x01,整合起来,arr [ i ] == 0x01010101 = 2 ^ 0 + 2 ^ 8 + 2 ^ 16 + 2 ^ 24 = 16843009。
int arr[10];
memset(arr, 1, sizeof(arr)); //本意是将数组所有元素变为1
for (int i = 0; i < 10; ++ i)
//最终会输出:16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009
cout<< arr[i]<< " ";
(2) 内存拷贝函数:void * memcpy (void * dest, void * src, int n);
将地址src开始的 n 个字节拷贝到地址dest。返回值是dest。
用法:
int a1[10];
int a2[10];
memcpy(a2, a1, 10 * sizeof(int)); //将a1的内容拷贝到 a2 中去
原理:
void * MyMemcpy(void * dest, const void * src, int n) {
char * pDest = (char *) dest;
char * pSrc = (char *) src;
for (int i = 0; i < n; ++ i) {
* (pDest + i) = * (pSrc);
}
}
但是在一些特殊情况,诸如内存空间有重叠的时候会有Bug。C++里的memcpy函数会相对
严谨些,但是思路类似。
三、指针
个人感觉,指针的世界里,sizeof ( T ) 才是单位长度 1.
形参是实参的一个拷贝,如果实参是值,那么传的是值;如果实参是地址,那么传的是地址。(函数)
指向结构变量的指针,访问其指向的结构变量的成员变量的表达方式:
① 指针->成员变量名
② (* 指针) . 成员变量名
四、STL
1、#include < algorithm >
(1)sort排序
①正序
sort (arr + a , arr + b);
对基本类型数组 arr 的【a , b)区间的元素从小到大排序。(a、b数组下标)
②逆序
sort (arr + a , arr + b , greater< T >());
对基本类型数组 arr 的【a , b)区间的元素从大到小排序。(a、b数组下标)
③自定义
sort (arr + a , arr + b , 排序规则结构名()) ;
或者传入比较函数:
int a[1000];
bool cmp(int a, int b) {return a > b;}
sort(a, a + 1000, cmp);
【注意】
- 是结构名,不是operator;
- { 前面的 const 写上去才万无一失,一般可能不写也不会有什么错,但是在某些编译器可能会出现看不懂的错误时,就可以来看看是不是这里漏写了。
模板:
struct 结构名
{
bool operator()(const T & a1, const T & a2) const {
// 若 a1 应该在 a2 面前,则返回true
// 否则返回false
}
};
例子:
struct Rule1 //从大到小排序
{
bool operator()(const int & a1, const int & a2) const {
return a1 > a2;
}
};
struct Rule2 //按个位数从小到大排序
{
bool operator()(const int & a1, const int & a2) const {
return a1 % 10 > a2 % 10;
}
};
(2) 二分查找
① binary_search
Ⅰ、 在正序的基本类型数组二分查找
binary_search (arr + a , arr + b , val);
二分查找区间在【a,b)的 “ 等于 ” val 的元素。找得到返回 true ,找不到返回 false。
(这里 x “等于” y 的含义是:a < b 和 b < a 都不成立,而不是绝对相等)
Ⅱ、 在自定义排序规则、任意类型数组中二分查找
binary_search (arr + a , arr + b , val , 排序规则结构名());
二分查找区间在【a,b)的 “ 等于 ” val 的元素。找得到返回 true ,找不到返回 false。
查找时的排序规则必须和排序时的规则一致。
② upper_bound 二分查找上界
Ⅰ、 在从小到大排好序的基本类型数组中查找
// 获得数组中val的上界
T * upper_bound(arr + a, arr + b, val); (具体可以在“下界找”)
// 获得vector中nums[idx]的上界的索引
int idx = upper_bound(nums.begin(), nums.end(), nums[idx]) - nums.begin();
返回一个指针 T * p, *p 是查找区间里下标最小的、大于 val 的元素;
若找不到, p 指向下标为 b 的元素。
Ⅱ、 在自定义排序规则、任意类型数组中查找上界
T * upper_bound(arr + a, arr + b, val, 排序规则结构名());
返回一个指针 T * p, *p是查找区间里下标最小的,按自定义排序规则,必须排在val后面的元素;
如果找不到, p 指向下标为 b 的元素。
③ lower_bound 二分查找下界
Ⅰ、在从小到大排好序的基本类型数组中查找
// 获得数组中val的下界
T * lower_bound(arr + a, arr + b, val);
int arr[] = {1,2,3,3,3,3,7,8};
int a = lower_bound(arr, arr + 8, 3) - arr;
int b = upper_bound(arr, arr + 8, 3) - arr;
cout << a << " " << b << endl; //输出:3 6(索引从1开始)
// 获得vector中nums[idx]的下界的索引
int idx = lower_bound(vec.begin(), vec.end(), nums[idx]) - vec.begin();
返回一个指针 T * p, *p 是查找区间里下标最小的、大于等于 val 的元素;
若找不到, p 指向下标为 b 的元素。
Ⅱ、在自定义排序规则、任意类型数组中查找上界
T * lower_bound(arr + a, arr + b, val, 排序规则结构名());
返回一个指针 T * p, *p是查找区间里下标最小的,按自定义排序规则,可以排在val后面的元素;
如果找不到, p 指向下标为 b 的元素。
(3)reverse
//翻转vector
reverse(a.begin(), a.end());
//翻转数组
reverse(a + 0, a + 100);
(4) random_shuffle
随机打乱,语法和reverse相同。
(5)unique
unique 用于去重,会返回去重后的尾迭代器,依然为前闭后开区间,即该尾迭代器是指向去重后末尾元素的下一个位置。
常用于离散化,且利用减法可以得到去重后的元素个数。
//对vector去重
int m = unique(a.begin(), a.end()) - a.begin();
//对数组去重
m = unique(a + 1, a + n + 1) - (a + 1);
2、string
(1)初始化
string s1("Hello");
string s2 = "World";
string s3(8, 'x'); //s3 == “xxxxxxxx”
s3.push_back('p');
s3.pop_back();
(2)常用方法
string s;
cin >> s; //遇到空格、换行、\t就停下
getline(cin, s); //读一整行
int length = s.length(); //也可以s.size()
cout << s.at(2) << endl; //等价于s[2],但是不同在于at函数如果超范围会抛出out_of_range异常(所以也会慢一点点)
string ss = s.substr(4, 5); //下标4开始的5个字符,如果不足5个,则复制到s的最后一个
s.swap(ss); //交换
s.erase(5); //删除下标5及之后的字符
//按照ASCII码排序
sort(s.begin(), s.end());
//将string转换成const char *类型,方便使用C语言一些和字符串有关的函数
//(C语言没有string)
const char *p = s.c_str(); //此时p指向s,且可以用p调用C语言里面有关于字符串的函数
char *pp = s.data(); //和c_str类型,只是返回的指针没有const,对s的修改可能会使pp出错
printf("%s\n", s.c_str());
//替换
s.replace(6, 3, "haha"); //从下标6开始的3个字符替换成“haha”
s.replace(6, 3, "haha", 1, 2); //从s下标6开始的3个字符替换成“haha”下标1开始的2个字符
//插入
s.insert(4, "88888"); //在s下标为4的位置插入88888
s.insert(4, "88888", 1, 3); //在s下标为4的位置插入88888下标1开始的3个字符
//查找,找不到都是返回静态常量string::npos
s.find("io"); //返回i从前往后第一次出现的索引
s.rfind("io"); //从后往前找,找得到依旧返回的是i的索引
s.find("io", 5); //从下标5开始找,包括下标5
s.find_first_of("abcd"); //从前往后找“abcd”中任何一个字符第一次出现的索引
s.find_last_of("abcd"); //"abcd"中任何一个字符最后一次出现的地方
s.find_first_not_of("abcd"); //从前往后找任意一个不是“abcd”中的字符的第一次出现的位置
s.find_last_not_of("abcd"); //从后往前找任意一个不是“abcd”中的字符的第一次出现的位置
//复制
string s2;
s2.assign(s); //复制s到s2
string s3;
s3.assign(s, 1, 3); //部分复制:将s中下标为1开始复制3个字符
//连接
s += s2;
s.append(s3);
s.append(s3, 3, 10); //将s3下标为3开始的10个字符添加到s的末尾,如果不够10个,则复制到s3的最后一个
//比较
int f1 = s.compare(s2); //s < s2, f1 == -1; s = s2, f1 == 0; s > s2, f1 = 1
int f2 = s.compare(1, 2, s2, 0, 7); //s下标1开始的2个字符,s2下标0开始的7个字符
int f3 = s.compare(1, 2, s2);
3、 pair
pair<T1, T2> 类型等价于:
struct {
T1 first;
T2 second;
};
//make_pair(int A, char B)可以生成pair<int, char> 变量
pair<int, int> pa = make_pair(val, index);
4、顺序容器
(1)vector
动态数组:
因为vector本质还是数组,要求元素的内存连续,而所谓的动态也不是真的动态,而是一般会在一开始多分配些存储空间,使得在vector尾部进行增删元素时大部分时候是O(1)的。除非vector的长度超过了预先分配的内存,就会重新开辟一个新的、比原来大(有的是大一倍,有的是比原来大50%)的储存空间,再把小vector的元素赋给新vector,所以需要O(n)。
int a[5] = {1, 2, 3, 4, 5};
vector<int> v1(4, 100); //v1里面存了4个100
vector<int> v2(a, a + 5); //将a数组里面下标【0,5)的元素复制给v2
vector<int> v3[200]; //一个大小为200的数组,每一个数组元素的类型都是vector<int>容器
vector<vector<int> > v4(3); //v4大小为3,且有些编译器会把…>>当成右移运算符,记得留个空格
//O(1)
int length = v1.size();
bool flag = v1.empty();
v1.clear();
vector<int>::iterator it;
it = v1.begin();
it = v1.end(); //指向最后一个元素的后一个位置
int a = v1.front();
int b = v1.back(); //最后一个元素
v1.push_back(val); //把val插在尾部
v1.pop_back(); //删掉尾部的一个元素
v1.insert(v1.begin() + 1, -2147483648); //插入一个数,使得v1[1] = -2147483648
(2)deque
双向队列:
元素内存连续存放,随机存取任何元素都是O(1)(但因为需要判断头尾指针位置,所以速度次于vector)。
在两端增删元素具有较佳性能(大多数O(1),和vector一样内存不够才O(n))
deque<int> d;
deque<int>::iterator it;
it = d.begin();
it = d.end();
int length = d.size();
bool flag = d.empty();
d.clear();
int val = d[2];
val = d.front();
val = d.back();
d.push_back(3);
d.push_front(5);
d.pop_front();
d.pop_back();
(3)list
双向链表:
元素内存不连续存放,在任何位置增删元素都是O(1),不支持随机存取。
5、容器适配器
(1)stack
栈 – 后进先出
stack<int> s;
int length = s.size();
bool flag = s.empty();
s.push(3);
s.pop();
int a = s.top(); //top返回的是引用,所以可以通过a修改栈顶的值
(2)queue
队列 – 先进先出
queue<int> q;
int length = q.size();
bool flag = q.empty();
q.push(5);
q.pop();
int val = q.front();
val = q.back(); //返回的是队尾的引用,可通过val修改队尾元素
(3)priority_queue
优先队列 – 最高优先级元素总是第一个出队 ,大根二叉堆
实现小根二叉堆:
①相反数:
把要插入元素取相反数,需要取出时再取反就可以得到原来的值。
②自定义结构体类型,重载小于号
struct rec {
int id, value;
bool operator < (const rec &a) const {
return value > a.value;
}
};
③
priority_queue<double, vector< double >, greater< double > > qqq;
priority_queue<int> q;
priority_queue< pair<int, double> > qq; //比较大小时,第一元是第一关键字,第二元是第二关键字
priority_queue<double, vector<double>, greater<double> > qqq; //小根堆
int length = q.size();
bool flag = q.empty();
q.push(5); //O(log n)
q.pop(); //O(log n)
int val = q.top(); //返回的也是引用,都是是const类型的,所以不可以修改
//优先队列不可以直接清空
while (!q.empty()) q.pop();
6、排序容器
(1)multiset
multiset 的迭代器,近似于指针,可以++ – != ==,但不可以比大小、加减整数、不可相减。(有一些容器的迭代器可以)、
st.find(val);
find函数并非绝对是在multiset容器中找到一个元素x,使得 x == val,而是使得 “ x必须排在val前面 ” 和 “ val 必须排在 x 前面 ” 都不成立,即:分不清 val 和 x 到底应该谁前谁后即视为相等。
【用法】
#include<set>
multiset<int> st; //自动降序排序,排序规则:若 a < b 为true,则 a 排在 b 前面
multiset<int, greater<int>> st2; //从大到小排
multiset<int, Rule> st3; //自定义排序规则,Rule为结构名,与二分自定义排序类似
//复杂度都是O(log(n))
st.insert(val); //添加元素,返回的类型是pair<iterator,bool>,插入成功bool为true,反之为false
st.find(val); //查找,返回的是迭代器,如果迭代器 == st.end() 说明找不到
st.erase(i); //删除迭代器 i 指向的元素
//时间复杂度都是O(1)
st.size(); //返回长度
st.empty();
//其他
st.clear();
st.count(x); //返回st中等于x的元素个数,O(k + log(n))
multiset<int>::iterator i, j; //迭代器,近似于指针
for (i = st.begin(), j = st.end(); i != j; ++ i) { // j 是指向st中最后一个元素后面的迭代器
cout << *i <<endl; //将st里自动排好序的元素按顺序输出
}
//复杂度都为log(n)
i = st.lower_bound(13); //返回最靠右的迭代器,使得[st.begin(), i)中的元素都在13前面
j = st.upper_bound(8); //返回最靠左的迭代器,使得[j, st.end())中的元素都在8后面
【举例】
#include <bits/stdc++.h>
#include <set>
using namespace std;
int main() {
multiset<int> st;
int a[10] = {1, 14, 12, 13, 7, 13, 21, 19, 8, 8};
for (int i = 0; i < 10; ++ i) {
st.insert(a[i]);
}
multiset<int>::iterator i, j;
for (i = st.begin(), j = st.end(); i != j; ++ i) {
cout<< *i << " "; //遍历输出1 7 8 8 12 13 13 14 19 21
}
cout<< endl;
i = st.find(22);
if (i == st.end())
cout<< "not found" << endl; //输出not found,说明找不到22
st.insert(22);
i = st.find(22);
if (i == st.end())
cout<< "not found" << endl;
else cout<< "found : " << *i << endl; // 输出found : 22
i = st.lower_bound(13);
cout<< *i << endl; //输出13
i = st.upper_bound(8);
cout<< *i << endl; //输出12
st.erase(i); //删除i指向的元素
for (i = st.begin(); i != j; ++ i) {
cout<< *i << " "; //1 7 8 8 13 13 14 19 21 22
}
return 0;
}
(2) set
set 和 multiset 的区别在于,set 不能有重复元素。
此处的a 、b重复是指,a必须排在b前面 和 b必须排在a前面都不成立。即:无法唯一确定a 、b排序顺序时,认为a和b是重复的(比如说自定义排序规则是按照个位上数字的大小排序,那么13和643是重复的,因为它们个位都是3)。
由此,set 插入元素可能会不成功。
此外,用法和注意事项和multiset差不多。
【举例】
#include <bits/stdc++.h>
using namespace std;
int main() {
set<int> st;
int a[10] = {1, 2, 3, 8, 7, 7, 5, 6, 8, 12};
for (int i = 0; i < 10; ++ i) {
st.insert(a[i]);
}
cout<< "st.size() == " << st.size() << endl; //输出:st.size() == 8
set<int>::iterator i, j;
for (i = st.begin(), j = st.end(); i != j; ++ i) {
cout<< *i << ' '; //输出:1 2 3 5 6 7 8 12
}
cout<< endl;
pair<set<int>::iterator, bool> result = st.insert(2);
//输出:2 already exists.
if (! result.second) {
cout << * result.first << " already exists." <<endl;
}
else cout<< *result.first << " inserted." << endl;
return 0;
}
(3)multimap
multimap 里的元素都是pair形式的,按照 first 排序(也可以按first进行查找),缺省时排序规则是 : a.first < b.first 为 true 时,则 a 排在 b 前面,即正序排序。
#include < map >
multimap <T1, T2> mp;
则 mp 里的元素都是如下类型:
struct {
T1 first; //关键字
T2 second; //值
};
(4) map
map 和 multimap 区别在于,map 不可以有重复元素(所以和set一样插入有可能失败),而且可以使用 [ ] 。
很多时候 map 被当作Hash表使用,但是因为 map 是基于平衡树实现,所以略慢于使用Hash函数实现的传统Hash表。
从C++11开始,STL中新增了unordered_map等基于Hash的容器,但是部分算法竞赛/编译器不支持C++11标准,所以有时候还是会使用map。
//map[ first ]的结果是,当有 first 时,返回它的second;
//如果没有 first,则会先生成 < first, 0 >,再返回second 。(此处的0是广义的零)
//所以如果map可能会保存一些无意义的零值二元组,建议先用find再[]
map<string, int> mp;
while (cin >> s) {
++ mp[s];
}
【用法】
map<string, int> h;
h.size();
h.empty();
h.clear();
h.begin();
h.end();
h.insert(make_pair("abc", 2)); //插入,参数为pair类型
map<string, int>::iterator it = h.begin(); //迭代器
pair<string, int> p = *it; //迭代器指向的是pair类型的元素
cout << p.first << ' ' << p.second << endl;
h.erase(it); //删除,等价于:h.erase(make_pair("abc", 2));
it = h.find("899"); //查找key为899的元素,并且也是返回迭代器,若不存在返回h.end()
7、无序容器
如果在 dev 发现不能编译这两个容器,可以在 Tools - Compiler Options - General 加入这个命令 " -std=c++11 " :
(1)unordered_set
//头文件
#include <unordered_set>
//定义
unordered_set<int> hash;
//操作
hash.size() //元素个数
hash.empty()
hash.max_size() //获取最大容量值
//迭代器操作
unordered_set<int>::iterator it;
unordered_set<int>::iterator ite_begin=c1.begin(); //返回头迭代器
unordered_set<int>::iterator ite_end=c1.end(); //返回尾部迭代器
//槽迭代器
unordered_set<int>::iterator local_iter_begin=c1.begin(1);
unordered_set<int>::iterator local_iter_end=c1.end(1);
//基本操作
find() //通过给定的主键查找元素 unorderedset<int>::iterator finditerator = hash.find(1);
count() //返回匹配给定主键元素的个数 hash.count(1);
insert() //插入函数 hash.insert(1);
erase() //删除 hash.erase(1);
clear() //清空 hash.clear();
swap() //交换 hash.swap(hash2);
//内存操作
hash.rehash(10)//设置槽位数
hash.reserve(1000)//改变容器的容量
(2)unordered_map
//头文件
#include <unordered_map>
//定义
unordered_map<string,int> hash;
//插入
hash["a1"]=2;
hash.insert(make_pair("e",7));
//搜索
if (hash.find("y") == hash.end()) {} // 找不到"y"会返回尾部迭代器
//遍历
unordered_map<string,int>::iterator i;
for (i = hash.begin(); i != hash.end(); ++ i)
cout << i->first << " " << i->second << endl;
//删除
hash.erase("pear"); //如果没找到也不会出错
8、bitset
可以把bitset看成多位二进制数,每八位占一个字节加粗样式,支持 ~、&、|、^、>>、<<、==、!=等位运算操作,且对于双目运算符操作时,需要两个bitset相同位数。
bitset<900> s; //s即为一个900位的二进制数
s[16] = 1; //将第16位赋值为1 (s[0]为最低位,s[899]为最高位)
int cnt = s.count(); //返回有多少位是1
bool flag = s.any(); //所有位都为0---false 至少一位为1---true
flag = s.none(); //所有位都为0---true 至少一位为1---false
s.set(); //所有位变为1
s.set(k, v); //把第k位变为v,即s[k] = v
s.reset(); //所有位变为0
s.reset(k); //等价于s[k] = 0
s.flip(); //等价于s = ~s;
s.flip(k); //对第k位取反,等价于s[k] ^= 1;
例题 Leetcode 面试题 05.08. 绘制直线
原题链接:https://leetcode-cn.com/problems/draw-line-lcci/
需要注意bitset输出是反着来的。
class Solution {
public:
bitset<32> temp;
vector<int> drawLine(int length, int w, int x1, int x2, int y) {
bitset<900000> s;
int start = y * w + x1, ending = y * w + x2; //绘画的区间
for (int i = start; i <= ending; ++ i) {
s[i] = 1;
}
vector<int> ans(length, 0);
for (int i = 0; i < length; ++ i) {
int idx = i * 32;
for (int j = 0; j < 32; ++ j) { //每32位则取出
temp[j] = s[idx + j];
}
/*
cout << temp << endl;
如果此时temp存的是 3 的二进制数,那么将会输出11000000000000000000000000000000
但是,temp[0] == 0,而不是 1,换句话说,输出是反着来的,最右边才是索引的起始点
*/
ans[i] = turn(); //二进制转十进制
}
return ans;
}
//将一个32位的二进制数转为int型整数
int turn() {
bool flag = false;
if (temp[0] == 1) { //判断是不是负数
flag = true;
for (int i = 0; i < 32; ++ i) {
temp[i] = (temp[i] + 1) % 2; //按位取反
}
}
int res = 0;
for (int i = 1; i <= 31; ++ i) {
res = res * 2 + temp[i];
}
if (flag) {
++ res; //负数的话,按位取反后需要+1
return -res;
}
else return res;
}
};