从零开始学习c++的自我修养之常用STL

首先要记住c++的万能头是:

#include<bits/stdc++.h>

using namespace std;

输入为cin>>,输出为cout<<。endl表示换行,相当于c语言中的\n。相比于c语言中的scanf和printf显然方便不少,因为cin和cout在输入和输出时不需要标注数据类型的。

举个例子:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a,b;
    cin>>a>>b;
    cout<<a+b<<endl;
    return 0;
}

一、引用

我们知道,在内存中,变量名可以作为某一数据的第一标签的话,那么我们不妨就可以说引用是数据的第二标签,我感觉它更像是指针和变量名的混合体,这玩意既可以在不同函数体中充当贯通不同函数体,直指数据本身(类似指针)的身份标志,同时它的存储内容似乎也比指针简单粗暴,似乎存的就是数据的实际内容本身,而非一串地址。

同时,它和指针也存在两点明显区别:

1.不存在空引用,也意味着引用在创建时就需要被初始化。但指针不仅存在空指针,还可以在任意时刻被初始化。

2.一旦引用被被初始化为一个对象,就不能被指向另一个对象。而指针可以在任何时候被指向为另一个对象。

int i;

int& r=i;

 其中,&在这里被读作引用,r可以读作为“r是一个初始化为i的整型引用”。

i=5;

cout<<i<<","<<r;

 编译器输出结果为

5,5

 可以发现,引用r也是直接指向数据内容本身的。、

引用也可以作为函数的形参

#include <bits/stdc++.h>
using namespace std;
void swap2(int &a,int &b)
{
    int c=a;
    a=b;
    b=c;
}
int main()
{
    int a,b;
    cin>>a>>b;
    swap2(a,b);//交换两个数的值
    cout<<a<<" "<<b;
    return 0;
}

可以看到swap2函数中,a,b为两个引用,它们接受来自主函数中实参a,b的具体内容值,但却可以在主函数内实现两变量内容的交换,达成了和传入指针一样的效果。

同理,引用也可以作为函数的返回类型

#include <iostream>
 
using namespace std;
 
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
 
double& setValues(int i) {  
   double& ref = vals[i];    
   return ref;   // 返回第 i 个元素的引用,ref 是一个引用变量,ref 引用 vals[i]
 
 
}
 
// 要调用上面定义函数的主函数
int main ()
{
 
   cout << "改变前的值" << endl;
   for ( int i = 0; i < 5; i++ )
   {
       cout << "vals[" << i << "] = ";
       cout << vals[i] << endl;
   }
 
   setValues(1) = 20.23; // 改变第 2 个元素
   setValues(3) = 70.8;  // 改变第 4 个元素
 
   cout << "改变后的值" << endl;
   for ( int i = 0; i < 5; i++ )
   {
       cout << "vals[" << i << "] = ";
       cout << vals[i] << endl;
   }
   return 0;
}

二、STL(Standard Template Library)标准模块库Standard Te

STL 是⼀个 C++ 软件库,⾥⾯包含算法(algorithms)、容 器(containers)、函数(functions)、迭代器(iterators)。写代码的时候从里面调用各种工具就感觉很方便,比c语言人性化不少。

1.字符串(string)

c语言中处理字符串时多半需要用到字符数组,而c++中则不需要,可以直接用string来定义字符串类型的变量、函数、容器等。

创建string类型的变量

一.string s; 直接创建一个空的(大小为0)的string类型变量s。

二.在创建时就对string类型变量赋初值:string s="hello world";同时,变量s若一开始未赋初值,可以后续直接赋值,可以看出这里就有与c语言的不同之处,c语言中要么在定义时就把值赋进去,要么在后续代码中用gets或者scanf把字符串输入到字符数组中,并不支持后续赋值。

三.string s(int n,char c); 创建一个string类型的变量,由n个c组成,注意c是字符型要用单引号' '。

 读入string类型的变量:

一.cin>>s;遇到空格或回车都会停止。

二.getline(cin,s);空格也会读入,直到读到回车才会停止。

输出string类型的变量:

直接cout<<s;把s输出到一行。

各种字符串变量常用运算符:

赋值运算符:=   可以实现两个字符串变量的内容赋值,也可以把一个字符串赋值给一个字符串变量。

比较运算符:==  !=  <  >  <=  >=  比较的是两个字符串的字典序大小

连接运算符:+   +=可以将一个字符串变量(字符串)直接加到另一个字符串变量(字符串)后面。例如:

 c++中的很多函数的调用方式一般是        应用者.具体函数

 各种字符串变量常用函数:

1.  s[index] 返回字符串变量s中下标为index的字符,和c语言一样下标同样是从0开始。

2.  s.substr(p,n) 返回从s的下标p开始的n个字符组成的字符串,如果n省略就取到底。

3.  s.length() 返回字符串长度。

4.  s.empty() 判断s是否为空,空返回1,非空返回0。

5.  s.erase(p,n) 删除从s的下标p开始的n个字符,如果n省略就删到底,如果直接s.erase()就是把s清空。

6.  s.erase(s.begin()+i) 删除s中下标为i的字符 。

7.  s1.insert(p1,s2,p2,n) 在s1的p1处插入一段来自s2中,从s2的p2处开始的n个字符组成的字符串,其中,p2和n均可省略。

8.  s.insert(p,n,c) 在p处插入n个字符c 。

9.  s1.replace(p1,n1,s2,p2,n2) 把s1中从p1开始的长度为n1的字符串删除,然后再把s2中从p2开始的一段长度为n2的字符串插入到s1中的p1位置处。可以理解为是erase函数和insert函数的结合。

10.  s1.find(s2,p2)查找s2中从p2开始的一段字符串(该函数只能到尾)是否有在s1中出现过,若有,则返回第一次出现在s1中的首字符下标,若无则返回string::npos(可直接连等)。

2.动态数组(vector)

创建动态数组:

vector<type>v; 创建动态数组v,其类型为int,下举几例:

1.  vector<int>v; 创建一个存int类型的动态数组v。

2.  vector<double>v{1,1,2,3,5,8}; 创建一个存double类型的动态数组v,长度为6,其中1,1,2,3,5,8分别存在v[0]至v[5]中。

3.  vector<long long>v(20); 创建一个存long long类型的动态数组v,长度为20,v[0]~v[19]默认为0。

4.  vector<string>v(20,"zzuacm"); 创建一个存string类型的动态数组v,长度为20,存的都是"zzuacm",注意了,这里每个元素的类型都是string,这种命名方式相当于每个元素存的都是“zzuacm”,即20个“zzuacm”;

5.  vector<int>v[3]; 相当于存int的二维数组,一共三行,每行的列可变。

6.vector<vector<int> >v{{1,2},{1},{1,2,3}}; 存int的二维数组,行和列都可变,初始状态为:

 动态数组相关函数:

1.  v[index] 获取动态数组v中下标为index的元素。

2.  v.push_back(item) 向v后面添加一个元素item。

3.  v.pop_back() 删除v最后一个元素。

4.  v.size() 返回动态数组v中的元素个数。

5.  v.resize(n) 把动态数组v的大小重新设置为n。

6.  v.empty() 判断动态数组v是否为空,若是则返回1,否则返回0。

7.  v.clear() 清空动态数组v中的元素。

8.  v.insert(it,x) 其中it为v中原本某一元素的迭代器,类型为iterator,将x这个新元素插入到it所指向的元素前的那个位置。

9.  v.erase(it) 删除迭代器it所指向的那个元素。

10. v.front() 返回首元素的引用。

11. v.back() 返回尾元素的引用。

12. v.begin() 返回首迭代器,指向第一个元素(感觉和指针咩啥区别?)

13. v.end() 返回尾迭代器,指向最后一个元素的下一个位置,注意,是最后一个元素的后一个位置,不是最后一个元素。

下面是一些实例代码展示:

#include <bits/stdc++.h>
using  namespace  std;

int main() {

	vector<int>v{1,5,8,4,3};
	v.erase(v.begin()+1);//erase与insert函数都需要传入对应元素的迭代器
	v.insert(v.begin()+1,10);
    v.front()=20;
    v.back()=80;//front和back函数返回的均为对应元素的引用,直接改变值即可改变对应元素的值
   
}

下面介绍一种c++11新出的新类型  auto:

for(auto i=v.begin();i!=v.end();i++)

cout<<*i<<" ";

 auto是一种全自动的数据类型,它可以根据等号后面的数据类型来自动决定前面变量的实际类型,像这里i的类型就被自动判定为迭代器。这里浅提一嘴,迭代器的数据类型为vector<int>::iterator   所以这里相当于vector<int>::iterator it=v.begin()。

还有一种更牛逼的写法

for(auto i:v)

cout<<i<<" ";

这种写法也可以遍历v,同时直接用i即可输出出来。同时这种方式还可以用于除了vector之外的其他容器遍历上,非常方便。

3.队列(queue)

queue队列只能在容器末尾添加新元素,只能从头部移除元素。

创建方式:

queue<type>q 建立一个存放数据类型为type的队列q。

queue的相关函数:

1. q.push(item):在 q 的最后添加⼀个type类型元素item。

2. q.pop():使 q 最前⾯的元素出队。这里可以发现和vector的pop_back()正好相反。

3. q.front():获取 q 最前⾯的元素。

4. q.size():获取 q 中元素个数。

5. q.empty():判断 q 是否为空,空返回1,不空返回0。

4.优先队列(priority_queue)

和队列不同,优先队列的出队顺序与插入顺序无关,仅与数据的优先级有关,本质是一个堆。

创建方法:

priority_queue<Type,Container,Function> pq;

Type:数据类型;

Container:存放数据的容器,默认用的是vector;

Functional:元素之间的比较方法,当type可以比较时可以省略;

优先队列相关函数:

1.  pq.push(item):在pq中添加一个元素。

2.  pq.top()获取pq最大的元素。

3.  pq.pop()使pq中最大元素出队。

4.  pq.size() 获取pq中元素个数。

5.  pq. empty() 判断pq是否为空。

优先队列注意事项:

1.  顺序打印(默认是从大到小)可采用:

while (!pq.empty())

    {

        cout << pq.top() << endl;

        pq.pop();

    }

即每打印一个最大,就删去一个最大,这样依次顺序打印。

2.  如果priority_queue中存储的是结构体元素,那就需要事先重载运算符<(bool operator<),相当于使该队列中的元素可以进行比较之后再存,不然会报错。

代码如下

#include <bits/stdc++.h>
using namespace std;

struct cnm {
	int a;
	int b;


};

bool operator<(cnm a, cnm b) {
	return a.a < b.a;
}




int main() {
	priority_queue<cnm> pq;
	pq.push({1, 3});
	pq.push({4, 5});
	pq.push({3, 7});
	pq.push({2, 4});

	while (!pq.empty()) {
		cout << pq.top().a << " " << pq.top().b << endl;
		pq.pop();
	}


}

3.正常情况下,优先队列从小到大排列,如果想要逆序排列,需要在定义时priority_queue<int,vector<int>,greater<int> >pq,其中vector<int>不能省略,greater<int>是系统自带的逆序函数。

 5.集合(set)

集合是一种包含对象的容器,能够快速查找对象是否包含在集合中。

set中的元素都是有序的(结构体记得重载<),且只能出现一次。

multiset元素有序且可以出现多次;

unordered_set元素无序且只能出现一次;

unordered_multiset元素无序且可以出现多次;

创建方式:

set<type>s;(type无法比较记得重载运算符<)
遍历方法:

for(auto i:s)

cout<<i<<" ";

 set的相关函数:

1.  s.insert(item) 向集合中插入一个元素。

2.  s.size() 获取s中的元素个数。

3.  s.empty() 判断s是否为空.

4.  s.clear() 清空s。

5.  s.find(item) 在s中查找一个元素item,返回其迭代器,若找不到则返回则返回end()。 

6.  s.begin()  返回s中最小元素的迭代器,注意set中的迭代器与vector中的不同,不能直接加数字,因此需要经常用到++和--。

7.  s.end() 返回s中最大元素的后一个迭代器。

8.  s.count(item) 返回待查找item的个数,但由于set中元素只能出现一次,故只能是0或1;但在两个multiset中可以返回大于1的数。

9.  s.erase(position或item) 其中position为迭代器,item为具体元素,二者皆可,前者是删除s中这个迭代器对应元素,后者就是直接删除相关元素。

10. s.erase(pos1,pos2) 删除[pos1,pos2)这个区间内的元素。 

11. s.lower_bound(item)  返回s中第一个大于等于item的元素的迭代器,找不到就返回s.end()。

12. s.upper_bound(item) 返回s中第一个大于item的元素的迭代器,找不到就返回s.end()。

 6.映射(map)

map是按照特定顺序存储key和value的特定容器,其中按照key的顺序进行排列,key值是唯一的,每个key对应一个value,一个map中value可以重复,但key不能重复。 

map底层实现与set一样均为红黑树。与map类似的还有unordered_map,这样的key就不是按顺序排列的了。

创建方式:

map<key(类型),value(类型)>mp;

unordered_map<key(类型),value(类型)>mp;

遍历方式:

for(auto i:mp)

cout<<i.first<<' '<<i.second<<endl;

map的相关函数:(注:以下函数均为对mp整个容器的操作,而非对容器内单一元素的操作)

1.  mp.size()  获取mp中元素个数。

2.  mp.empty()  判断mp是否为空。

3.  mp.clear()  清空mp。

4.  mp.begin()  返回mp中最小key的迭代器。和set一样,只能++与--。

5.  mp.end()  返回mp中最大key的后一个迭代器。

6.  mp.find(key)  在mp中查找一个key并返回其迭代器,找不到的话返回mp.end()。

7.  mp.count(key)  在mp中统计key的数量,由于两种map中key值均唯一,故只可能返回0或1。

8.  mp.erase(key)  在mp中删除key所对应的项,也就是key和对应的value均会被删除。

9.  mp.lower_bound(item)  返回mp中第一个key值大于等于item的迭代器,找不到就返回mp.end()。

10. mp.upper_bound(item)  返回mp中第一个key值大于item的迭代器,找不到就返回mp.end()。

11.  mp[key] 返回在该mp容器中,键值为key的项所对应的value,若该key不存在会自动创建一个该key的项,返回value类型的默认构造器所构造的值。同时也可进行mp[key]=xxx这样的赋值操作。

注意:unordered可以使set 和map的底层原理从红黑树变为哈希表,使得查、改、删操作都变为O(1)复杂度。

对于既定义过的map容器——mp,中的某一特定的元素i而言,其key值调用为i.first,value值调用为i.second。

 7.二进制有序集(bitset)

bitset是一种类似数组的结构,它的每个元素只能是0或1,每个元素只占用1bit空间。

创建方式:

bitset<n>b; 创建一个可以存n位的二进制有序集。

bitset<n>b(k); 其中,k的类型为unsigned long;相当于在创建时存进去了一个十进制数,然后bitset会自动将其转化为二进制数。例如,bitset<10>b(5),易知5的二进制为000···0101,记住一句在bitset中低位(坐标位)存低位(二进制位),高位(坐标位)存高位(二进制位),且在容器bitset中按照从高到低排列。所以将会是b[0]为1,b[1]为0,b[2]为1,余下7位由低到高(3到9)补0。

 bitset<n>b(s); 其中,s的类型为string,且只能由字符‘0’和‘1’组成,存到b中。具体存取也是字符s从右到左,位置坐标从小到大,余位补0。

bitset相关函数:

1. b.count()  求该二进制有序集b中1的个数。

2. b.any()  检查b中是否有1,是1否0,下同。

3. b.none() 检查b中是否没有1。

4. b.all() 检查b中是否全是1。

5. b.flip() flip函数不指定参数时,将b中每一位全部取反。

6. b.set() set函数不指定参数时,将b中的每一位全部置为1。

7. b.reset() reset函数不指定参数时,将b中的每一位全部置为0.

8. string s=b.to_string()  将bitset转换为string类型并赋值给字符串变量s。类似的函数有:

  1. unsigned long n = b.to_ulong()
  2. unsigned long long n =b.to_ullong()

9. b[index] 可直接调用某二进制位,注意存取时是高位在前低位在后。

8.常用函数

1. max(n1,n2)返回较大值。

2. min(n1,n2)返回较小值。

3. swap(n1,n2)交换两者的值,可以是两个值也可以是两个容器。

4. sort(first,last,compare)first排序起始位置,last排序终止位置,排序范围为[first,last),compare比较方式,可省略,省略时默认按升序排序,如果排序元素没有定义比较运算符(如结构体),则必须要compare。

以下代码是对一个数组进行逆序(即降序)排序的多种操作方式

#include<bits/stdc++.h>
using namespace std;
bool cmp(int a,int b){return a>b;}
int main()
{
    int arr[]={3,2,5,1,4};
    sort(arr,arr+5);
    sort(arr,arr+5,greater<int>());
    sort(arr,arr+5,cmp);
    sort(arr,arr+5,[](int a,int b){return a>b;});
    return 0;
}

可以看到,greater<int>()是 系统自带的逆序排列,cmp函数则是自己写的,compare这个部分似乎是要你写一个函数,然后该函数是bool类型,以返回值为1的结果作为排序方式。例如a是数组中较小下标的值,b是较大下标的值,该cmp函数就要求较小下标要恒大于较大下标,即导致降序排序。

5.unique(first,last)去重函数,功能是将相邻的重复元素去除,然而其本质是将重复元素移动到数组末尾,最后将迭代器末尾指向第一个重复元素下标 。需要注意一下三点:

一、 [first, last)范围内的值必须是一开始就提前排好序的(巧用sort)

二、 移除 [first, last) 内连续重复项,前闭后开。

三、去重之后的返回最后一个元素的下一个地址(迭代器)

 在想要遍历被unique之后的数组(假如包含五个元素),需要通过  n=unique(arr,arr+5)-arr

 来获取遍历所需步数,再用这个步数n来循环逐个遍历。

6.lower_bound(first,last,value)  二分函数,返回序列中第一个大于等于value的元素的位置。还有一个为upper_bound(first,last,value),返回的是大于value的元素的位置,同时在使用前必须先排序,因为这是个二分查找。

first:  查找起始位置,指针或迭代器。

last:查找的终止位置,也是指针或迭代器,需要注意的是由于查找范围是[first,last),last是开的,所以last一般指实际所需查找范围中,最后一个元素的后一个位置的迭代器 。

样例:

int main()
{
    int arr[]={3,2,5,1,4};
    sort(arr,arr+5);//需要先排序
    cout << *lower_bound(arr,arr+5,3);//输出数组中第一个大于等于3的值
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值