c++ STL 学习指南

c++ STL 学习指南

 在写c++代码的过程中,stl就像工具一样,工欲善其事,必先利其器,熟练掌握各种stl数据结构的使用,才可以在写代码的过程中得心应手,尽快的实现自己想要的功能。
 初学者看到stl的各种数据结构,可能会感到陌生,甚至有时候觉得,这些数据结构的用途到底是啥呢,之前编程语言学习到数组,结构体,变量,指针,字符串之类的东西,还不够用吗。其实stl可以看做是c++编程语言的智慧结晶,也是一个很好用的数据存储以及操作的工具。很多时候遇到的问题都可以归结为输入,计算,输出的过程。其中输入一般都是一串特定格式的字符或者数字,如果只是使用数组来增删这些数据处理的话,会有许多不方便的地方,而使用STL则方便许多。
 初学者如何学习STL呢,其实这也是有规律可循的,可以这么理解: 既然是操作的数据,无外乎增, 删, 查, 改这几个。除此之外,还要额外关注一下,这些数据是如何初始化的,又是如何遍历的。了解了STL数据结构的这些方面,算是基本入门了。
 这篇文章将会按照这样的结构来讲:以STL中的不同数据结构为一小节,每一小节会按照简介,初始化,遍历,增删查改的顺序进行详细的介绍。

STL数据结构简介

  • 在stl 中, c++有几种常用的数据结构,首先需要熟练他们的使用方式,如果要是需要深入研究的话,可以继续探讨stl实现的底层原理。

数据结构:

<vector>  <deque> <list> <queue>
<stack>   <set>   <map>  <unordered_map>  
<unordered_set>

容器的分类:

  • 序列容器:vector list deque queue stack
  • 排序容器: set, map
  • 哈希容器: unordered_map unordered_set

array容器的使用

1. 简介

和c++的数组一致,比数组多一些成员函数,效率一样

2. 初始化

std::array<double, 10> values;//没有初始化
std::array<double, 10> values {
    
    };//初始化为0
std::array<double, 10> values {
    
    1,1};//只是初始化前几个

查找

get<3>(values);// 输出指定位置的元素
values[3]

vector容器的使用

1. 简介

  vector是一种常用的数据结构,使用它进行数据的处理操作非常方便,而且效率也比较高。一般来讲,在做算法题的时候,如果输入数据是数组或者矩阵的话,都会优先采用vector容器,便于操作数据。

2. 初始化


// 创建vector
std::vector<double> values;

---------------------------------------------
/* "思考: reserve存在的价值是啥: 减少内存重新
分配的次数,原来内存每次分配都是以2的倍数增加的"*/
---------------------------------------------
values.reserve(20);


std::vector<double> values(20);//20个初始值为0的元素
std::vector<double> values(20,1.0);
std::vector<double> values(int num, double value);//变量表示

//可以通过容器来创建容器 
std::vector<double> value2(value);

//可以使用容器中的部分元素来创建容器
int array[]={
    
    1,2,3};
vector<int>values(array, array+2);
vector<int>value1{
    
    1,2,3,4,5};
vector<int>value2(begin(value1), begin(value1)+3);

3. 遍历元素

//方法一
for(int i = 0; i < values.size(); i++){
    
    
    cout <<values[i] << "  ";
}

//方法二
for(auto && value : values)
cout << value;

//方法三
for(auto first = values.begin();first < values.end(); ++first){
    
    
    cout << *first << " ";
}

4. 增删查改

//增加元素使用
values.push_back(1);
values.emplace_back(1);
//使用insert添加元素
std::vector<int> demo{
    
    1,2};
demo.insert(demo.begin()+1, 3);// {1,3,2}
demo.insert(demo.end,2,5);//{1,3,2,5,5}

std::array<int,3>test{
    
    7,8,9};
demo.inserts(demo.end(), test.begin(), test.end());//{1,3,2,5,5,7,8,9}

demo.insert(demo.end(), {
    
    10, 11});
//使用emplace添加元素,每次只能插入一个元素
demo1.emplace(demo1.begin(),3);

---------------------------------------
//删除元素使用:
//删除最后一个元素
pop_back()
//删除指定位置的元素
erase(pos)
erase(demo.begin()+1);
erase(demo.begin()+1, demo.end() - 2);


//下面删除元素的方法,这是一种替代的方式,swap + pop_back 的方式,如何不在意顺序的话,这么做的话,时间复杂度比较低
swap(begin(demo)+1, end(demo)-1)
pop_back()

//删除所有和指定元素相等的元素
remove()
//删除所有的3
remove(demo.begin(), demo.end(), 3);
//要清楚remove的原理
//其实大小没有变
1 3 3 4 3 5
1 4 5 4 3 5
// 删除容器中的所有元素
demo.clear()

deque容器的使用 (双端队列容器)

1. 简介

  • 与vector相同的地方:

擅长在尾部添加元素,不擅长在中间增删元素O(1)
可以根据需要修改自身的容量和大小

  • 自己的特性
  • 擅长在头部添加或删除元素O(1)
  • 不保证所有元素在连续的空间中
  • 如果需要频繁的在序列头尾操作的话,首选deque容器

2.初始化


// 创建 deque
deque<int> d(10);
deque<int> d(10,5);
//拷贝赋值
deque<int> d1(5);
deque<int> d2(d1);
-------------------
int a[]={
    
    1,2,3,4,5}
deque<int>d(a, a+5);

3. 遍历

for(auto i = d.begin(); i< d.begin(); i++){
    
    
    cout << *i << " ";
}

for(auto i=begin(d); i < end(d); i++){
    
    
    cout << *i << " ";
}

//使用while循环操作
auto first = d.cbegin();//这里无法修改元素的值
auto end = d.cend();
while(first < end){
    
    
    cout << *first << " ";
    ++first;
}
//使用反向迭代器
for(auto i = d.rbegin(); i < d.rend(); i++ ){
    
    
    cout << *i << " ";
}

4. 增删查改

===========================
//添加元素的方法
push_back()
push_front()
pop_back()
pop_front()

===========================
//deque的访问方式
deque<int> d{
    
    1,2,3,4,5}
d[1] = 5

deque<int> d{
    
    1, 2, 3, 4, 5}
cout << d.at(1)
============================

d.at(1)=5
d.front()
d.back()
============================

// deque 容器添加或删除元素
d.push_back(2);

/* 迭代器用法
要清楚begin() end() 与 rbegin() rend()的位置区别
cbegin, 与cend 只能访问,无法修改
注意事项:
不能用来初始化容器
deque 添加元素会导致迭代器失效
*/

list 容器(双向列表容器)

1. 简介

  • 插入或者删除元素的时间复杂度为O(1),移动元素的效率高
  • 不擅长访问元素的,需要遍历访问

2. 初始化

list<int> values(10);
list<int> values(10,5);
int a[] = {
    
    1,2,3,4,5}
list<int> values(a, a+5)

array<int, 5> arr{
    
    11,12,13,14,15}
list<int> values (arr.begin()+2, arr.end());

3. 遍历

for(std::list<char>::iterator it = values.begin();it !=values.end(); ++it){
    
    
    std::cout << *it;
}

for(std::list<char>::reverse_iterator it = values.rbegin(); it != values.rend(); ++it){
    
    
    std::cout << *it;
}

while(begin != end){
    
    
    std::cout << *begin;
    ++begin;
}

4. 增删查改


* 访问元素的方法
int &first = mylist.front()
int &last = mylist.back()

//添加元素的方法
values.push_front(0);
values.push_back(4);

// 直接生成元素,没有拷贝过程
values.emplace_front(-1);
vaules.emplace_back(5);

//插入元素
insert(pos, elem)
insert(pos, n, elem)
insert(pos, first, last)
insert(pos, initlist)



// 删除元素的方法
values.pop_front();
values.pop_back();
values.clear();
values.erase(del);
values.erase(first, last);
## 根据元素的值来执行操作
values.remove('c');

mylist.remove_if([](int value){
    
    return (value < 10);});

关联式容器

简介:

关联式容器的主要有:

pair,  map,  set,  multimap,  multiset

pair容器

1. 简介

需要include <utility>,可以用来存储键值对

2. 初始化

pair <string, double> pair1;
pair <string, string> pair5(("a"), string("b"));
pair5.first = ""
pair5.second = ""
//交换
pair1.swap(pair2)

<map> | <unordered_map> | <unordered_multimap>关联式容器

1. 简介

  • map存储的对象都是pair,底层结构是红黑树,

  • unordered_map 底层的结构是哈希表

  • multimap和unordered_multimap存储的元素可以有相同的键

  • 无序关联式容器种类
    unordered_map, unordered_multimap, unordered_set, unordered_multiset
    --------------------------------------------------------------

  • 容器的选用

大量的遍历操作,用关联容器;
通过键获取对应的值,选用无序容器

  • 什么是无序容器(哈希容器)(<unordered_map>就是无序容器)
  1. 使用哈希表的存储结构
  2. 键值对的存储位置取决于键值对中的键
  3. 擅长通过指定键查找对应的值,而遍历元素的效率则不如关联式容器

由于有序和无序的关联容器用法一样,只是底层的结构不同,这里以map为例进行讲解

2. 初始化

std::map<std::string, int>myMap;

std::map<std::string, int> myMap{
    
    {
    
    "c", 10},{
    
    "s", 20}}

std::map<std::string, int>newMap(myMap);

myMap.emplace("c", 1)

3. 遍历

//map容器的遍历
for(auto iter = mapMap.begin(); iter != myMap.end(); ++iter){
    
    
    count << iter-> first << " " << iter-> second << endl;
}

4. 增删查改

//find函数,返回一个迭代器
auto iter = myMap.find("java")


//如何判断是否存在某个键
if(testMap.count(key) == 0)
    cout << "no this key" << endl;

if (testMap.find(key) == testMap.end())
    cout << "  no this key" << endl;
     

//获取键对应值得方法
string cvalue = myMap["c"]
myMap.at("c"); // 失败的话,会抛出异常

set 容器

1.简介

  • 和map、multimap容器不同, 使用set容器存储的各个键值对,要求key和值value必须相等。

  • 使用set容器存储的这个元素值必须各不相同

  • 对于初学者来说,不要直接修改set元素中已经存储的元素的值,这很有可能破坏set容器中元素的有序性; 应该先删除该元素,再添加一个修改后的元素。

  • multiset可以存储多个相同的元素

2.初始化

std::set<std::string> myset

std::set<std::string> myset{
    
    "a","b","c"};

std::set<std::string> my_copyset(myset);

std::set<std::string> my_copyset(++myset.begin(),myset.end());

3.遍历

//要访问的话,只能用set的迭代器,迭代器不支持 > , < 这样的比较
for(auto iter = myset.begin(); iter != myset.end(); ++iter){
    
    
    cout << *iter << endl;
}

//从某个元素之后才开始遍历
set<string>::iterator iter = myset.find("a");
for(; iter != myset.end(); ++iter){
    
    
    cout << *iter << endl;  
}

4. 增删查改

  • set容器的一些方法
find()
empty()
insert()
erase()
swap()
emplace()
count(val)
  • 插入set容器
myset.insert("a");

//insert 操作的返回值是 pair<iteror, bool>
//---如果添加成功,则返回true; 如果添加失败,说明有相同的元素,表示添加失败----

retpair = myset.insert(str)
cout << *(retpair.first) << retpair.second;

//set的大小
set.size();
//添加多个元素
set.insert({
    
    "a", "b","c"});

//其他插入元素的方法
emplace()
emplace_hint()
  • 删除操作
//删除元素2
//{1,2,3,4,5}
myset.erase(2); //{1,3,4,5}
myset.erase(begin());// {3,4,5}
myset.erase(myset.begin(), --myset.end());//{5}

//删除所有元素
myset.clear();

总结

  看到这里,本以为学习到了全部,殊不知这才只是刚刚开始。本篇文件介绍的内容不是很全,如果想有更深入的了解,还需要大量阅读资料并不断实践。STL的使用只是c++语言编程中的一项基础技能,以后的编程之路还很漫长。路漫漫其修远兮,吾将上下而求索。

猜你喜欢

转载自blog.csdn.net/weixin_37682263/article/details/115256584
今日推荐