C++ 输入输出 / 库函数 / STL 笔记总结

一、输入输出

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);
}

其他

  1. 在输入时,ctrl + z 表示输入结束。
  2. 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" ,那么返回016. double atof(char *s);
将字符串s中的内容转换成实数返回。
例如,"12. 34"就会转换成12. 34。
如果S的格式不是一个实数,则返回017. 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);

【注意】

  1. 是结构名,不是operator;
  2. { 前面的 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;
    }
};
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值