C++标准模板库(STL)与常用泛型算法

目录:

        一、C++标准模板库(STL)

                1、头文件模板

                2、vector

                3、set

                4、string

                5、map

                6、queue

                7、priority_queue

                8、stack

                9、pair

                    10、algorithm头文件下常用函数

                    11、链表

        二、常用泛型算法

                1、find

                2、find_if

                3、count

                4、accumulate

                5、find_first_of

                6、fill

                7、fill_n

                8、sort

                9、unique

                10、count_if

                11、find_if

一、C++标准模板库(STL)

1、头文件模板

/*   头文件   */
#include <vector>
#include <set>
#include <string>
#include <map>
#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iterator>
#include <list>
#include <bitset>
#include <iostream>
using namespace std;

 2、vector

2.1、vector:本意为向量,这里作为“变长数组”,即“长度根据需要而自动改变的数组”。

2.2、使用前提:添加vector头文件

#include <vector>
using namespace std;

2.3、定义

vector<typename> name;
vector<typename> Arrayname[arraySize];
//举例
vector<int> name;
vector<node> name;
vector<vector<int> > name;
vector<int> Arrayname[100];

2.4、访问(两种访问方式:下标和迭代器=指针)

        2.4.1、通过下标访问:和访问普通数组一样

vi[3]

        2.4.2、通过迭代器访问:迭代器可以理解为一种类似指针的东西

vector<typename>::iterator it = vi.begin();//定义迭代器it
通过*it来访问vector里的元素

vi.begin()+3//迭代器=指针
*(vi.begin()+3)//和v[3]是等价的

        Note:在常用STL容器中,只有在vector和string中,才允许使用vi.begin()+3这种迭代器加上整数的写法

        2.4.3、遍历

#include<cstdio>
#include<vector>
using namespace std;
int main()
{
	vector<int> vi;
	for(int i = 0; i <= 5; i++)
    {
		vi.push_back(i);
	}
	vi.insert(vi.begin()+2,-1);//将-1插在vi[2]的位置
	for(int i = 0; i < vi.size(); i++)
    {
        //下标方式
		printf("%d ",vi[i]);
	}
	for(vector<int>::iterator it = vi.begin(); it != vi.end(); it++)
    {
        //迭代器方式
		printf("%d ",*it);
	}
	return 0;
}

 2.5、常用函数

函数 功能
begin() 首元素地址
end() 尾元素地址的下一个地址(美国人思维比较习惯左闭右开)
size() 元素个数
clear() 清空元素
push_back(x) 在尾部添加元素
pop_back() 删除尾部元素
insert(it,x) 向指定位置插入一个元素
erase(it) 删除指定位置元素
erase(first,last) 删除 [ first , last ) 内的所有元素,如果要删除全部元素,写法是vi.erase(vi.begin(),vi.end()),此时等价于clear()

3、set

3.1、set:翻译为集合,是一个内部自动有序且不含重复元素的容器。

          作用:自动去重并按升序排序

3.2、使用前提:添加set头文件

#include<set>
using namespace std;

3.3、定义

set<typename> name;
set<typename> Arrayname[arraySize];

 3.4、访问(只能通过迭代器=指针访问)

set<typename>::iterator it;//定义迭代器it

由于除开vector和string之外的STL容器都不支持*(it+i)的访问方式,因此只能按如下方式枚举:

#include<cstdio>
#include<set>
using namespace std;
int main()
{
	set<int> st;
	st.insert(3);
	st.insert(5);
	st.insert(2);
	st.insert(3);
	for(set<int>::iterator it = st.begin(); it != st.emd(); it++)
    {
	    printf("%d",*it);//输出结果:2 3 5
	}
	return 0;
}

 3.5、常用函数

函数 功能
begin() 首元素地址
end() 尾元素地址的下一个地址(美国人思维比较习惯左闭右开)
size() 元素个数
clear() 清空元素
insert(x) 插入元素
find(x) 查找元素,返回对应元素的迭代器(指针)
erase(it) 删除指定位置元素,可结合find函数使用。st.erase(st.find(x));
erase(x) 删除元素x,等价于st.erase(st.find(x));
erase(first,last) 删除 [ first , last ) 内的所有元素

4、string

4.1、string:string类型对字符串常用的需求功能进行了封装,使得操作起来更方便,且不易出错。

4.2、使用前提:添加string头文件

#include<string>
using namespace std;

4.3、定义

string str = "abcd";

4.4、输入输出

        4.4.1、如果要读入和输出整个字符串,则只能用cin和cout:

#include<iostream>//cin和cout在iostream头文件中
#include<string>
using namespace std;
int main()
{
	string str;
	cin>>str;
	cout<<str;
	return 0;
}

        4.4.2、用c_str()将string类型转换为字符数组进行输出

#include<iostream>//cin和cout在iostream头文件中
#include<string>
using namespace std;
int main()
{
	string str = "abcd";
	printf("%s\n",str.c_str());//将string型str使用c_str()变成字符数组
	return 0;
}

4.5、访问(两种访问方式:下标和迭代器=指针)

        4.5.1、通过下标访问:和访问字符数组一样

str[3]

        4.5.2、通过迭代器访问:迭代器可以理解为一种类似指针的东西

string::iterator it;//定义迭代器it
通过*it来访问string里的元素

str.begin()+3//迭代器=指针
*(str.begin()+3)//和str[3]是等价的

        Note:在常用STL容器中,只有在vector和string中,才允许使用vi.begin()+3这种迭代器加上整数的写法

4.6、常用函数

函数 功能
begin() 首元素地址
end() 尾元素地址的下一个地址(美国人思维比较习惯左闭右开)
clear() 清空元素
size() 元素个数
length() 元素个数,等价于size()
+ 拼接字符串。例如:str3 = str1 + str2; 或str1 += str1;
比较运算符 按字典序比较两字符串大小
insert(pos,string) 在pos号位置插入字符串string

insert(it,it2,it3)

串[it2,it3)将被插在it的位置上
erase(it) 删除指定位置元素
erase(first,last) 删除 [ first , last ) 内的所有元素
erase(pos,length) 从pos位置开始删除length位
substr(pos,length) 从pos位置开始,截取长度为length的子串
str.find(str2) 当str2是str的子串时,返回其在str中第一次出现的位置;如果str2不是str的子串,那么返回string::npos
str.find(str2,pos) 从str的pos号位开始匹配str2
string::npos 一个常数,用以作为find函数失配时的返回值

str.replace(pos,len,str2)

把str从pos号位开始、长度为len的子串替换为str2
str.replace(it1,it2,str2) 把str的迭代器[it1,it2)范围的子串替换为str2

5、map

5.1、map:翻译为映射。map可以将任何基本类型(包括STL容器)映射到任何基本类型(包括STL容器)。map会以键从小到大的顺序自动排序,这是由于map内部是使用红黑树实现的(set也是),在建立映射的过程中会自动实现从小到大的排序功能。

5.2、使用前提:添加map头文件

#include<map>
using namespace std;

5.3、定义

map<typename1,typename2> mp;
举例:
map<string,int> mp;//如果字符串作为key,必须使用string而不能char数组
map<set<int>,string> mp;

5.4、访问(两种访问方式:下标和迭代器=指针)

        5.4.1、通过下标访问:和访问普通数组一样

mp[key]

        5.4.2、通过迭代器访问:迭代器可以理解为一种类似指针的东西

map<typename1,typename2>::iterator it;//定义迭代器it
通过it->first来访问键,通过it->second来访问值
#include<cstdio>
#include<map>
using namespace std;
int main()
{
	map<char,int> mp;
	mp['m'] = 20;
	mp['r'] = 30;
	mp['a'] = 40;
	for(map<char,int>::iterator it = mp.begin(); it != mp.end(); it++)
    {
		printf("%c %d\n",it->first,it->second);
	}
	return 0;
}

5.5、常用函数

函数 功能
begin() 首元素地址
end() 尾元素地址的下一个地址(美国人思维比较习惯左闭右开)
size() 元素个数
clear() 清空元素

find(key)

查找key,返回迭代器
erase(it) 删除指定位置元素
erase(key) 删除key
erase(first,last) 删除 [ first , last ) 内的所有元素

6、queue

6.1、queue:翻译为队列,先进先出的容器

6.2、使用前提:添加queue头文件

#include<queue>
using namespace std;

6.3、定义

queue<typename> name;

6.4、访问

        只能通过front()来访问队首元素,通过back()访问队尾元素

6.5、常用函数

函数 功能
size() 元素个数
empty() 检测是否为空,返回bool
push(x) 入队列
pop() 出队列
front() 获得队首元素
back() 获得队尾元素

        Note:使用front()和pop()函数时,必须用empty()判断队列是否为空,否则可能因为队空而出现错误。

7、priority_queue

7.1、priority_queue:翻译为优先队列,其底层是用堆来进行实现的,队首元素一定是当前队列中优先级最高的那一个。

7.2、使用前提:添加queue头文件

#include<queue>
using namespace std;

7.3、定义(设置优先级)

        如何定义优先队列内元素的优先级是运用好优先队列的关键

priority_queue<int> name;//默认大的优先级越大,等价于priority_queue<int,vector<int>,less<int> > q;
//基本数据类型的优先级设置
//第二个参数是来承载底层数据结构堆的容器,第三个参数是对第一个参数的比较类
priority_queue<int,vector<int>,less<int> > q;//less表示大的优先级越大
priority_queue<int,vector<int>,greater<int> > q;//greater表示小的优先级越大
//结构体的优先级设置
struct fruit{
	string name;
	int price;
};
struct cmp{
	//优先队列的这个函数与sort中的cmp函数的效果是相反的
	bool operator () (fruit f1,fruit f2)
    {
		return f1.price > f2.price;
	}
};
priority_queue<fruit,vector<fruit>,cmp> q;

7.4、访问

        只能通过top()来访问队首元素(也可以称为堆顶元素),也就是优先级最高的元素。

7.5、常用函数

函数 功能
size() 元素个数
empty() 检测是否为空,返回bool
push(x) 入队列
pop() 出队列
top() 获得队首元素

        Note:使用top()函数时,必须用empty()判断优先队列是否为空,否则可能因为队空而出现错误。

8、stack

8.1、stack:翻译为栈,后进先出的容器。

         用来模拟实现一些递归,防止程序对栈内存的限制而导致程序运行出错。

8.2、使用前提:添加stack头文件

#include<stack>
using namespace std;

8.3、定义

stack<typename> name;

8.4、访问

        只能通过top()来访问栈顶元素

8.5、常用函数

函数 功能
size() 元素个数
empty() 检测是否为空,返回bool
push(x) 入栈
top() 获得栈顶元素
pop() 弹出栈顶元素

9、pair

9.1、pair:实际上可以看作一个内部有两个元素的结构体

常见用途一:代替二元结构体,节省编码时间
常见用途二:作为map的键值对来进行插入

#include<string>
#include<map>
int main()
{
	map<string,int> mp;
	mp.insert(make_pair("hehe",5));
	mp.insert(pair<string,int>("hehe",5));
	for(map<string,int>::iterator it = mp.begin(); it != mp.end(); it++)
    {
		printf("%c %d\n",it->first,it->second);
	}
}

9.2、使用前提:添加utility头文件或map头文件

        map内部实现涉及pair,因此添加map头文件时会自动添加utility头文件

#include<map>
//或 #include<utility> 
using namespace std;

9.3、定义

//定义
pair<typename1,typename2> name;
//定义并初始化
pair<string,int> p("haha",5);
//临时变量(只用一次的)
pair<string,int>("haha",5)
或 
make_pair("haha",5)

9.4、访问

        pair中只有两个元素,分别是first和second,只需要按正常结构体的方式去访问即可。

9.5、常用函数

函数 功能
比较运算符 比较规则是先以first的大小作为标准,相等时才去判别second的大小

10、algorithm头文件下常用函数

函数 功能
max(x,y) 最大值 ( int和double均可)
max(x,max(y,z)) 三个数的最大值
min(x,y) 最小值
abs(int x) 绝对值( Note:浮点型的绝对值请用math里的fabs)
swap(x,y) 交换
reverse(it,it2) 将数组指针或容器迭代器在[it,it2)范围内的元素反转

next_permutation(it,it2)

给出一个序列在全排列中的下一个序列
fill(it,it2,value) 把数组或容器某区间赋为某个相同的值
sort(it,it2) 排序
sort(it,it2,cmp) 排序

lower_bound(it,it2,val)

寻找数组或容器的[it,it2)范围内第一个值大于等于val的元素的位置,返回指针或迭代器,如果不存在,则返回可插入该元素的指针或迭代器
upper_bound(it,it2,val) 寻找数组或容器的[it,it2)范围内第一个值大于val的元素的位置,返回指针或迭代器,如果不存在,则返回可插入该元素的指针或迭代器

11、链表

11.1、链表结点

struct node{
	typename data;//数据域
	node* next;//指针域
}

11.2、为链表结点分配内存空间和释放内存空间

node* p = new node;//分配内存空间
delete(p);//释放内存空间

        Note:new运算符和delete运算符必须成对出现,否则会容易产生内存泄露,从编程习惯上,应养成即时释放空间的习惯。不过一般考试中,分配的空间在程序结束时即被释放,不会有太大影响,以下代码没有释放空间。

11.3、链表基本操作

        11.3.1、创建链表

#include<stdio.h>
#include<stdlib.h>
struct node{
	int data;
	node* next;
}
node* create(int Array[],int len)
{
	node *p,*pre,*head;
	head = new node;
	head->next = NULL;
	pre = head;
	for(int i = 0; i < len; i++)
    {
		p = new node;
		p->data = Array[i];
		p->next = NULL;
		pre->next = p;
		pre = p;
	}
	return head;
}
int main()
{
	int Array[5] = {5,3,6,1,2};
	node* L = create(Array,5);
	L = L->next;
	while(L != NULL)
    {
		printf("%d",L->data);
		L = L->next;
	}
}

        11.3.2、查找元素

int search(node* head,int x)
{
	int count= 0;
	node* p = head->next;
	while(p != NULL)
    {
		if(p->data == x)
        {
			count++;
		}
		p = p->next;
	}
	return count;
}

        11.3.3、插入元素

void insert(node* head,int pos,int x)
{
	node* p = head;
	for(int i = 0; i < pos -1; i++)
    {
		p = p->next;
	}
	node* q = new node;
	q->data = x;
	q->next = p->next;
	p->next = q;
}

        11.3.4、删除元素

//删除以head为头结点的链表中所有数据域为x的结点
void del(node* head,int x)
{
	node* p = head->next;
	node* pre = head;
	while(p != NULL)
    {
		if(p->data == x)
        {
			pre->next = p->next;
			delete(p);
			p = pre->next;
		}
        else
        {
			pre = p;
			p = p->next;
		}
	}
}

11.4、静态链表

struct Node{
	typename data;
	int next;//令数组的下标直接表示结点的地址,实现原理是hash
}node[size];

二、常用泛型算法

1、find

vector<int> a;
int val = 10
//find接受两个迭代器和要查找的值,如果找到,则返回该值对应的迭代器
//否则返回a.end()
auto it = find(a.begin(), a.end(), val);

2、find_if

vector<int> a;
int val = 10
//find_if 返回第一个满足条件的迭代器
//否则返回a.end()
auto it = find(a.begin(), a.end(), [](int a)->bool { return a == 1; });

3、count

vector<int> a;
int val = 10
//count 与find 的参数类型相同,统计两个迭代器之间的值val出现的次数
int s = count(a.begin(), a.end(), val);

4、accumulate

accumulate 带有三个形参。
头两个形参指定要累加的元素范围。第三个形参则是累加的初值。accumulate 函
数将它的一个内部变量设置为指定的初值,然后在此初值上累加输入范围 accumulate
用于指定累加起始值的第三个实参是必要的,因
为 accumulate 对将要累加的元素类型一无所知,因此,除此
之外,没有别的办法创建合适的起始值或者关联的类型。
accumulate 对要累加的元素类型一无所知,这个事实有两层含义。首先,调用
该函数时必须传递一个起始值,否则,accumulate 将不知道使用什么起始值。
其次,容器内的元素类型必须与第三个实参的类型匹配,或者可转换为第三个实
参的类型。在 accumulate 内部,第三个实参用作累加的起点;容器内的元素按

顺序连续累加到总和之中。因此,必须能够将元素类型加到总和类型上。

 

int main()
{
	vector<int> vec;

	for(int i = 0; i < 10; i++)
	{
		vec.push_back(i);	
	}
	
	//在100000的基础上进行累加
	int sum = accumulate(vec.begin(),vec.end(),100000);

	cout << sum << endl;
	return 0;
}

还可以进行字符串的累加

int main()
{
	string vec = "qweqwe";
	//可以进行字符串的拼接
	string sum = accumulate(vec.begin(),vec.end(),string("pppppp"));

	cout << sum << endl;
	return 0;
}

5、find_first_of

除了 find 之外,标准库还定义了其他一些更复杂的查找算法。当中的一部
分类似 string 类的 find 操作,其中一个是 find_first_of 函数。这个算法带有两对迭代器参数来标记两段元素范围,在第一段范围内查找与第二段范围中任意元素匹配的元素,然后返回一个迭代器,指向第一个匹配的元素。如果找不到元素,则返回第一个范围的 end 迭代器。

int main()
{
	srand(time(0));
	vector<int> vec1;
	vector<int> vec2;

	for(int i = 0; i < 1000; i++)
	{
		vec1.push_back(rand() % 100);
		vec2.push_back(rand() % 100);
	}

	auto it = find_first_of(vec1.begin(),vec1.end(),vec2.begin(),vec2.end());

	if(it != vec1.end())
	{
		cout << *it << endl;
	}
}

6、fill

fill 带有一对迭代器形参,用于指定要写入的范围,而所写的值是它的第三个
形参的副本。执行时,将该范围内的每个元素都设为给定的值。如果输入范围有
效,则可安全写入。这个算法只会对输入范围内已存在的元素进行写入操作

int main()
{
	srand(time(0));
	vector<int> vec1;

	for(int i = 0; i < 10; i++)
	{
		vec1.push_back(rand() % 100);
	}

	for(auto val:vec1)
	{
		cout << val << " ";
	}
	cout << endl;

	fill(vec1.begin(),vec1.end(),0);

	for(auto val:vec1)
	{
		cout << val << " ";
	}
	cout << endl;
}

7、fill_n

fill_n 函数带有的参数包括:一个迭代器、一个计数器以及一个值。该函
数从迭代器指向的元素开始,将指定数量的元素设置为给定的值。fill_n 函数
假定对指定数量的元素做写操作是安全的。初学者常犯的错误的是:在没有元素
的空容器上调用 fill_n 函数;

这个 fill_n 函数的调用将带来灾难性的后果。我们指定要写入 10 个元
素,但这些元素却不存在——vec 是空的。其结果未定义,很可能导致严重的运行时错误。 对指定数目的元素做写入运算,或者写到目标迭代器的算法,都不检查目标的大小是否足以存储要写入的元素。

int main()
{
	srand(time(0));
	vector<int> vec1;

	for(int i = 0; i < 10; i++)
	{
		vec1.push_back(rand() % 100);
	}

	for(auto val:vec1)
	{
		cout << val << " ";
	}
	cout << endl;
	
	//fill使用
	fill_n(vec1.begin(),5,0);

	for(auto val:vec1)
	{
		cout << val << " ";
	}
	cout << endl;
}

8、sort

sort 内部采用快排算法。

int main()
{
	srand(time(0));
	vector<int> vec1;

	for(int i = 0; i < 100; i++)
	{
		vec1.push_back(rand() % 100);
	}

	sort(vec1.begin(),vec1.end());

	for(auto val:vec1)
	{
		cout << val << " ";
	}
	cout << endl;
}

9、unique

去除重复数据

如果要删除重复的项,必须使用容器操作,在本例中调用 erase 实现该功
能。这个函数调用从 it 指向的元素开始删除,直到 words 的最后一
个元素也删除掉为止。调用之后,words 存储输入的 8 个不相同的元素。
算法不直接修改容器的大小。如果需要添加或删除元素,则必
须使用容器操作。
值得注意的是,对没有重复元素的 vector 对象,调用 erase 也是安全的。
如果不存在重复的元素,unique 就会返回 words.end(),此时,调用 erase 的
两个实参值相同,都是 words.end()。两个迭代器相等这个事实意味着 erase 函
数要删除的范围是空的。删除一段空的范围没有任何作用,所以即使输入中没有
重复的元素,我们的程序仍然正确。

int main()
{
	srand(time(0));
	vector<int> vec1;

	for(int i = 0; i < 100; i++)
	{
		vec1.push_back(rand() % 100);
	}
	sort(vec1.begin(),vec1.end());

	//去除重复
	auto it = unique(vec1.begin(),vec1.end());

	vec1.erase(it,vec1.end());
}

10、count_if

按条件统计满足条件的元素的个数

int main()
{
	srand(time(0));
	vector<int> vec1;
	for(int i = 0; i < 100; i++)
	{
		vec1.push_back(i);
	}
	sort(vec1.begin(),vec1.end());
	//vector<int>::size_type vectmp = count_if(vec1.begin(),vec1.end(),comp());
	int a = count_if(vec1.begin(),vec1.end(),comp());

	//cout << vectmp << endl;
	cout << a << endl;
	return 0;
}

11、find_if

标准库定义了一个 find_if 函数。与 find 一样,find_if 函数带有一对迭代器形参,指定其操作的范围。与 count_if 一样,该函数还带有第三个形参,表明用于检查范围内每个元素的谓词函数。find_if 返回一个迭代器,指向第一个谓词函数返回非零值的元素。如果
这样的元素不存在,则返回第二个迭代器实参。使用 find_if 函数重写上述例题中统计长度大于 6 的单词个数的程序部分。

class comp
{
public:
	bool operator()(int a)
	{
		return a > 6;
	}
};

int main()
{
	srand(time(0));
	vector<int> vec1;
	for(int i = 0; i < 100; i++)
	{
		vec1.push_back(rand() % 100);
	}
	sort(vec1.begin(),vec1.end());

	auto it = find_if(vec1.begin(),vec1.end(),comp());

	cout << *it << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_72714790/article/details/127398505