C++基础(八)——STL六大组件

01 vector容器

vector:向量容器

底层数据结构:动态开辟的数组

扩容方式:每次以原来空间大小的2倍进行扩容

vector<int> vec; //声明方式

增加:

vec.push_back(20); 末尾添加元素 O(1) 导致容器扩容

vec.insert(it, 20); it迭代器指向的位置添加一个元素20 O(n) 导致容器扩容

删除:

vec.pop_back(); 末尾删除元素 O(1)

vec.erase(it); 删除it迭代器指向的元素 O(n)

查询:

operator[ ] 通过下标进行随机访问vec[5] O(1)

iterator迭代器进行遍历

find,for_each

注意:对容器进行连续插入或者删除操作(insert/erase),一定要更新迭代器,否则第一次insert或者erase完成,迭代器就失效了

常用方法介绍:

size()

empty()

reserve(20):vector预留空间的 只给容器底层开辟指定大小的内存空间,并不会添加新的元素

resize(20):容器扩容用的 不仅给容器底层开辟指定大小的内存空间,还会添加新的元素

swap : 两个容器进行元素交换

#include <vector>
#include <iostream>

using namespace std;

int main()
{
	vector<int> vec; // vector<string> vec; 0 1 2 4 8 16 32 64
	//vec.reserve(20); // 叫做给vector容器预留空间
	vec.resize(20);

	cout << vec.empty() << endl;
	cout << vec.size() << endl; // int()

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

	cout << vec.empty() << endl;
	cout << vec.size() << endl;



	// vector的operator[]运算符重载函数
	int size = vec.size();
	for (int i = 0; i < size; ++i)
	{
		cout << vec[i] << " ";
	}
	cout << endl;

	// 把vec容器中所有的偶数全部删除
	auto it2 = vec.begin();
	while (it2 != vec.end())
	{
		if (*it2 % 2 == 0)
		{
			it2 = vec.erase(it2);
		}
		else
		{
			++it2;
		}
	}

	// 通过迭代器遍历vector容器
	auto it1 = vec.begin();
	for (; it1 != vec.end(); ++it1)
	{
		cout << *it1 << " ";
	}
	cout << endl;

	// 给vector容器中所有的奇数前面都添加一个小于奇数1的偶数   44 45    56 57
	for (it1 = vec.begin(); it1 != vec.end(); ++it1)
	{
		if (*it1 % 2 != 0)
		{
			it1 = vec.insert(it1, *it1-1);
			++it1;
		}
	}

	for (it1 = vec.begin(); it1 != vec.end(); ++it1)
	{
		cout << *it1 << " ";
	}
	cout << endl;

    system("pause");
	return 0;
}

运行结果:

image-20220411091727623

02 deque容器和List容器

deque:双端队列容器

底层数据结构:动态开辟的二维数组

扩容方式:一维数组从2开始,以2倍的方式进行扩容,每次扩容后,原来第二维的数组,从新的第一维数组的下标oldsize/2开始存放,上下都预留相同的空行,方便支持deque的首尾元素添加

deque deq;

增加:

deq.push_back(20); 从末尾添加元素 O(1)

deq.push_front(20); 从首部添加元素 O(1) // vec.insert(vec.begin(), 20) O(n)

deq.insert(it, 20); it指向的位置添加元素 O(n)

删除:

deq.pop_back(); 从末尾删除元素 O(1)

deq.pop_front(); 从首部删除元素 O(1)

deq.erase(it); 从it指向的位置删除元素 O(n)

查询搜索:

iterator(连续的insert和erase一定要考虑迭代器失效的问题)

list:链表容器

底层数据结构:双向的循环链表 pre data next

list mylist;

增加:

mylist.push_back(20); 从末尾添加元素 O(1)

mylist.push_front(20); 从首部添加元素 O(1) // vec.insert(vec.begin(), 20) O(n)

mylist.insert(it, 20); it指向的位置添加元素 O(1) // 链表中进行insert的时候,先要进行一个query查询操作

对于链表来说,查询操作效率就比较慢了

删除:

mylist.pop_back(); 从末尾删除元素 O(1)

mylist.pop_front(); 从首部删除元素 O(1)

mylist.erase(it); 从it指向的位置删除元素 O(1)

查询搜索:

iterator(连续的insert和erase一定要考虑迭代器失效的问题)

deque和list,比vector容器多出来的增加删除函数接口:

push_front和pop_front

03 vector、deque、List容器对比

vector特点:动态数组,内存是连续的,2倍的方式进行扩容,

vector vec; 0-1-2-4-8… reserve(20)/resize

deque特点:动态开辟的二维数组空间,第二维是固定长度的数组空间,扩容的时候(第一维的数组进行2倍扩容)

deque底层内存是否是连续的?

并不是 每一个第二维是连续的,

容器的纵向考察:容器掌握的深度

容器的横向考察:各个相似容器之间的对比

vector和deque之间的区别?

1.底层数据结构

2.前中后插入删除元素的时间复杂度: 中间和末尾 O(1) 前 deque O(1) vector O(n)

3.对于内存的使用效率: vector 需要的内存空间必须是连续的 deque 可以分块进行数据存储,不需要内存空间必须是一片连续的

4.在中间进行insert或者erase,vector和deque它们的效率谁能好一点(vector)?谁能差一点(deque)? O(n)

vector和list之间的区别?

数组:增加删除O(n) 查询O(n) 随机访问O(1) 链表:(考虑搜索的时间)增加删除O(1) 查询O(n)

1.底层数据结构:数组 双向循环链表

04 容器适配器

标准容器 - 容器适配器 => 设计模式,就叫做适配器模式

怎么理解这个适配器?

1.适配器底层没有自己的数据结构,它是另外一个容器的封装,它的方法全部由底层依赖的容器进行实现的

2.没有实现自己的迭代器

template<typename T, typename Container=deque<T>>

class Stack

{

public:

  void push(const T &val) { con.push_back(val); }

  void pop() { con.pop_back(); }

  T top()const { return con.back(); }

private:

  Container con;

};

stack: push入栈 pop出栈 top查看栈顶元素 empty判断栈空 size返回元素个数

queue: push入队 pop出队 front查看队头元素 back查看队尾元素 empty判断队空 size返回元素个数

queue => deque 为什么不依赖vector呢???

stack => deque 为什么不依赖vector呢???

1.vector的初始内存使用效率太低了!没有deque好 queue stack vector 0-1-2-4-8 deque 4096/sizeof(int) = 1024

2.对于queue来说,需要支持尾部插入,头部删除,O(1) 如果queue依赖vector,其出队效率很低

3.vector需要大片的连续内存,而deque只需要分段的内存,当存储大量数据时,显然deque对于内存的利用率更好一些

priority_queue: push入队 pop出队 top查看队顶元素 empty判断队空 size返回元素个数 默认:大根堆

priority_queue => vector 为什么依赖vector???

底层默认把数据组成一个大根堆结构 在一个内存连续的数组上构建一个大根堆或者小根堆的

#include <iostream>
#include <stack>
#include <queue>

using namespace std;

int main()
{
	priority_queue<int> pque;
	for (int i = 0; i < 20; ++i)
	{
		pque.push(rand() % 100 + 1);
	}
	cout << pque.size() << endl;
	while (!pque.empty())
	{
		cout << pque.top() << " ";
		pque.pop();
	}


	cout << endl;
	cout << "---------------------------" << endl;

	queue<int> que;
	for (int i = 0; i < 20; ++i)
	{
		que.push(rand() % 100 + 1);
	}
	cout << que.size() << endl;
	while (!que.empty())
	{
		cout << que.front() << " ";
		que.pop();
	}

	cout << endl;
	cout << "---------------------------" << endl;
	
	stack<int> s1;
	for (int i = 0; i < 20; ++i)
	{
		s1.push(rand() % 100 + 1);
	}

	cout << s1.size() << endl;

	while (!s1.empty())
	{
		cout << s1.top() << " ";
		s1.pop();
	}
    cout << endl;

	system("pause");
	return 0;
}

运行结果:

stack 后进先出 栈顶出

queue 先进先出 队头进

image-20220411095048047

05 无序关联容器

unordered_set

#include <unordered_map>
#include <unordered_set>
#include <iostream>
using namespace std;

int main()
{
	unordered_set<int> set1; // 不会存储key值重复的元素
	for (int i = 0; i < 50; ++i)
	{
		set1.insert(rand()%20+1); // vector/deque/list  insert(it, val)
	}

	cout << set1.size() << endl;
	cout << set1.count(15) << endl;

	auto it1 = set1.begin();
	for (; it1 != set1.end(); ++it1)
	{
		cout << *it1 << " ";
	}
	cout << endl;

	// set1.erase(20); // 按key值删除元素

	// for (it1 = set1.begin(); it1 != set1.end(); )
	// {
	// 	if (*it1 == 20)
	// 	{
	// 		it1 = set1.erase(it1); // 调用erase,it1迭代器就失效了
	// 	}
	// 	else
	// 	{
	// 		++it1;
	// 	}
	// }
	
	it1 = set1.find(20);
	if (it1 != set1.end())
	{
		set1.erase(it1);
	}

	for (int v : set1)
	{
		cout << v << " ";
	}
	cout << endl;

   system("pause");
   return 0;
}

运行结果:

image-20220411101010096

unordered_map

  //[key, value]

  struct pair

  {

   first; => key

   second; => value

  }

map的operator[]

1.查询

2.如果key不存在,它会插入一对数据[key, string()]

  V& operator[](const K &key)

  {insert({key, V()});

  }
#include <unordered_map>
#include <unordered_set>
#include <iostream>
using namespace std;

int main()
{
	unordered_map<int, string> map1;
	map1.insert(make_pair(1000, "zhang san"));
	map1.insert({1010, "li si"}); // map表增加元素
	map1.insert({1020, "wang wu" });
	map1.insert({1030, "wang kai" });
	map1.erase(1020); // {1020, "wang wu" }删除了

	auto it1 = map1.find(1030);
	if (it1 != map1.end())
	{
		// it1 -> pair对象
		cout << "key:" << it1->first << " value:" << it1->second << endl;
	}

	map1[2000]; // key:2000 value:""
	map1[3000] = "liu shuo"; // map1.insert({2000, "刘硕"});
	map1[1000] = "zhang san2"; // 
	cout << map1.size() << endl;
	// map operator[](key) => value  查询
	cout << map1[1000] << endl;

   system("pause");
   return 0;
}

运行结果:

image-20220411102111865

海量数据去重复

#include <unordered_map>
#include <unordered_set>
#include <iostream>
using namespace std;

int main()
{
	// 处理海量数据查重复;去重复的时候
	const int ARR_LEN = 100;
	int arr[ARR_LEN] = { 0 };
	for (int i = 0; i < ARR_LEN; ++i)
	{
		arr[i] = rand() % 20 + 1;
	}

	// 上面的10万个整数中,把数字进行去重打印  set  map
	unordered_set<int> set;
	for (int v : arr) // O(n)
	{
		set.insert(v); // O(1)
	}

	for (int v : set)
	{
		cout << v << " ";
	}
	cout << endl;

    system("pause");
    return 0;
}

运行结果:

image-20220411151239784

统计重复次数

#include <unordered_map>
#include <unordered_set>
#include <iostream>
using namespace std;

int main()
{
	// 处理海量数据查重复;去重复的时候
	const int ARR_LEN = 100;
	int arr[ARR_LEN] = { 0 };
	for (int i = 0; i < ARR_LEN; ++i)
	{
		arr[i] = rand() % 20 + 1;
	}

	// 上面的10万个整数中,统计哪些数字重复了,并且统计数字重复的次数
	unordered_map<int, int> map1;
	for (int k : arr)
	{
		/*
		auto it = map1.find(k);
		if (it == map1.end())
		{
			map1.insert({k, 1});
		}
		else
		{
			it->second++;
		}
		*/
		map1[k]++; // map1[k]  [k, 1]
	}


	auto it = map1.begin();
	for (; it != map1.end(); ++it)
	{
		if (it->second > 1)
		{
			cout << "key:" << it->first <<
				" count:" << it->second << endl;
		}
	}

    system("pause");
    return 0;
}

运行结果:

image-20220411151523409

06 有序关联容器

set:

#include <set>
#include <map>
#include <iostream>

using namespace std;

class Student
{
public:
	Student(int id, string name)
		:_id(id), _name(name) {}
	bool operator<(const Student &stu)const
	{
		return _id < stu._id;
	}
private:
	int _id;
	string _name;
	friend ostream& operator<<(ostream &out, const Student &stu);
};

ostream& operator<<(ostream &out, const Student &stu)
{
	out << "id:" << stu._id << " name:" << stu._name << endl;
	return out;
}

int main()
{
	set<Student> set1;

	set1.insert(Student(1020, "zhang san"));
	set1.insert(Student(1000, "li si"));
	
	for (auto it = set1.begin();
		it != set1.end(); ++it)
	{
		cout << *it << endl;
	}
	

    system("pause");
	return 0;
}

运行结果:

image-20220411150046282

map:

#include <set>
#include <map>
#include <iostream>

using namespace std;

class Student
{
public:
	Student(int id=0, string name="")
		:_id(id), _name(name) {}
private:
	int _id;
	string _name;
	friend ostream& operator<<(ostream &out, const Student &stu);
};
ostream& operator<<(ostream &out, const Student &stu)
{
	out << "id:" << stu._id << " name:" << stu._name << endl;
	return out;
}
int main()
{
	map<int, Student> stuMap;
	stuMap.insert({ 1000, Student(1000, "zhang san") });
	stuMap.insert({ 1020, Student(1020, "li si") });
	stuMap.insert({ 1030, Student(1030, "wang wu") });

	// stuMap.erase(it) stuMap.erase(1020)  stuMap[2000] [2000, V()]
	// cout << stuMap[1020] << endl;   stuMap.find(key)
	auto it = stuMap.begin();
	for (; it != stuMap.end(); ++it)
	{
		cout << "key:" << it->first << " value:" << it->second << endl;
	}
	cout << endl;

    system("pause");
	return 0;
}

运行结果:

image-20220411150301318

07 迭代器

const_iterator:常量的正向迭代器 只能读,不能进行写操作

iterator:普通的正向迭代器

reverse_iterator:普通的反向迭代器

const_reverse_iterator:常量的反向迭代器

//底层代码
// vector<int>::iterator
// auto it1 = vec.begin(); 
// const_iterator   <=   iterator

class const_iterator
{
    public:
    const T& operator*(){return *_ptr;}
}

class iterator : public const_iterator
{
    T& operator*(){return *_ptr;}
}
    
// rbegin():返回的是最后一个元素的反向迭代器表示
// rend:返回的是首元素前驱位置的迭代器的表示
// vector<int>::reverse_iterator
#include <iostream>
#include <vector>
using namespace std;


int main()
{
	vector<int> vec;
	for (int i = 0; i < 20; ++i)
	{
		vec.push_back(rand() % 100);
	}

	vector<int>::const_iterator it1 = vec.begin();
	for (; it1 != vec.end(); ++it1)
	{
		cout << *it1 << " ";
	}
	cout << endl;


	vector<int>::const_reverse_iterator rit = vec.rbegin();
	for (; rit != vec.rend(); ++rit)
	{
		cout << *rit << " ";
	}
	cout << endl;

	/*for (int v : vec)
	{
		cout << v << " ";
	}
	*/
	cout << endl;

    system("pause");
	return 0;
}

运行结果:

image-20220411142006308

08 函数对象

1.通过函数对象调用operator(),可以省略函数的调用开销,比通过函数指针

调用函数(不能够inline内联调用)效率高

2.因为函数对象是用类生成的,所以可以添加相关的成员变量,用来记录函数对象使用

时更多的信息

#include <iostream>
#include <queue>
#include <vector>
#include <set>
using namespace std;

/*
函数对象  =>  C语言里面的函数指针
*/

// 使用C的函数指针解决方案
/*
template<typename T>
inline bool mygreater(T a, T b)
{
	return a > b;
}
template<typename T>
inline bool myless(T a, T b)
{
	return a < b;
}
*/


// C++函数对象的版本实现
template<typename T>
class mygreater
{
public:
	bool operator()(T a, T b) // 二元函数对象
	{
		return a > b;
	}
};
template<typename T>
class myless
{
public:
	bool operator()(T a, T b) // 二元函数对象
	{
		return a < b;
	}
};

// compare是C++的库函数模板
template<typename T, typename Compare>
bool compare(T a, T b, Compare comp)
{
	// 通过函数指针调用函数,是没有办法内联的,效率很低,因为有函数调用开销
	return comp(a, b);  // operator()(a, b);
}
int main()
{
	cout << compare(10, 20, mygreater<int>()) << endl;
	cout << compare(10, 20, myless<int>()) << endl;
	//cout << compare('b', 'y') << endl;

    system("pause");
	return 0;
}

运行结果:

image-20220411143036952

#include <iostream>
#include <queue>
#include <vector>
#include <set>
using namespace std;


int main()
{
	set<int, greater<int>> set1;
	for (int i = 0; i < 10; ++i)
	{
		set1.insert(rand() % 100);
	}

	for (int v : set1)
	{
		cout << v << " ";
	}
	cout << endl;

    system("pause");
	return 0;
}

运行结果:

image-20220411143600648

#include <iostream>
#include <queue>
#include <vector>
#include <set>
using namespace std;

int main()
{
	priority_queue<int> que1; // vector
	for (int i = 0; i < 10; ++i)
	{
		que1.push(rand() % 10 + 1);
	}

	while (!que1.empty())
	{
		cout << que1.top() << " ";
		que1.pop();
	}
	cout << endl;

	using MinHeap = priority_queue<int, vector<int>, greater<int>>;
	MinHeap que2; // vector
	for (int i = 0; i < 10; ++i)
	{
		que2.push(rand() % 10 + 1);
	}

	while (!que2.empty())
	{
		cout << que2.top() << " ";
		que2.pop();
	}
	cout << endl;

    system("pause");
	return 0;
}

运行结果

image-20220411144019868

09 泛型算法与绑定器

泛型算法 = template + 迭代器 + 函数对象

特点一:泛型算法的参数接收的都是迭代器

特点二:泛型算法的参数还可以接收函数对象(C函数指针)

sort,find,find_if,binary_search,for_each

绑定器 + 二元函数对象 =》 一元函数对象

bind1st:把二元函数对象的operator()(a, b)的第一个形参绑定起来

bind2nd:把二元函数对象的operator()(a, b)的第二个形参绑定起来

#include <iostream>
#include <vector>
#include <algorithm> // 包含了C++ STL里面的泛型算法
#include <functional> // 包含了函数对象和绑定器
using namespace std;


int main()
{
	int arr[] = { 12,4,78,9,21,43,56,52,42,31};
	vector<int> vec(arr, arr+sizeof(arr)/sizeof(arr[0]));

	for (int v : vec)
	{
		cout << v << " ";
	}
	cout << endl;

	// 默认小到大的排序
	sort(vec.begin(), vec.end());

	for (int v : vec)
	{
		cout << v << " ";
	}
	cout << endl;

	// 有序容器中进行二分查找
	if (binary_search(vec.begin(), vec.end(), 21)) 
	{
		cout << "binary_search 21" << endl;
	}

	auto it1 = find(vec.begin(), vec.end(), 21);
	if (it1 != vec.end())
	{
		cout << "find 21" << endl;
	}

	// 传入函数对象greater,改变容器元素排序时的比较方式
	sort(vec.begin(), vec.end(), greater<int>());
	for (int v : vec)
	{
		cout << v << " ";
	}
	cout << endl;

	// 78 56 52 43 42 31 21 12 9 4
	// 把48按序插入到vector容器当中  找第一个小于48的数字
	// find_if需要的是一个一元函数对象
	// greater a(48) > b    less a < b(48)
	auto it2 = find_if(vec.begin(), vec.end(),
		//bind1st(greater<int>(), 48));
		//bind2nd(less<int>(), 48));
		[](int val)->bool {return val < 48; });
	vec.insert(it2, 48);
	for (int v : vec)
	{
		cout << v << " ";
	}
	cout << endl;

	// for_each可以遍历容器的所有元素,可以自行添加合适的函数对象对容器
	// 的元素进行过滤
	for_each(vec.begin(), vec.end(), 
		[](int val)->void
	{
		if (val % 2 == 0)
		{
			cout << val << " ";
		}
	});
	cout << endl;

    system("pause");
	return 0;
}

运行结果:

image-20220411144747534

10 STL总结

C++ STL standard template libaray 标准模板库

1.顺序容器

vector

deque

list

2.容器适配器

stack

queue

priority_queue

3.关联容器

无序关联容器 => 链式哈希表 增删查O(1)

set:集合 key map:映射表 [key,value]

unordered_set 单重集合

unordered_multiset 多重集合

unordered_map 单重映射表

unordered_multimap 多重映射表

有序关联容器 => 红黑树 增删查O(log2n) 2是底数(树的层数,树的高度)

set

multiset

map

multimap

二、近容器

数组,string,bitset(位容器)

三、迭代器

iterator和const_iterator

reverse_iterator和const_reverse_iterator

四、函数对象(类似C的函数指针)

greater,less

五、泛型算法

sort,find,find_if,binary_search,for_each

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-特立独行的猪-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值