【C/C++】STL学习笔记

本文介绍了C++ STL(Standard Template Library),包括其组成部分如迭代器、容器(如array、vector、list、stack、queue、priority_queue、set、map等)和算法(如for_each、remove_if等)。讲解了迭代器的本质和不同类型的迭代器,以及红黑树在set和map中的应用。STL是C++标准库的重要部分,提供了一种高效的方式来处理数据和实现泛型编程。
摘要由CSDN通过智能技术生成

STL

什么是STL

STL(Standard Template Library)是C++标准库的一部分(80%),是用C++ Template机制来表达泛型的库。

面向过程——基于对象——面向对象——泛型

STL其实就是一个模板库,这个模板库主要由以下几个组件组成:
Iterator(迭代器):正向迭代器、反向迭代器、文件流迭代器。
Container(容器):数组、链表、栈、队列、set、map等。
Algorithm(算法):对容器进行查找、排序、遍历等操作。
Adaptors(适配器):适配器是标准库中通用的概念,包括容器适配器、迭代器适配器和函数适配器。

本质上,适配器是使一事物的行为类似于另一类事物的行为的一种机制
容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。例如,stack适配器可使任何一种顺序容器以栈的方式工作。
http://blog.sina.com.cn/s/blog_9946f55601016qwk.html

迭代器

迭代器实际是也是一个指针。是经过封装的指针类。

迭代器对象名称就是迭代器的地址。
iterator._Ptr是迭代器对象指向数据的地址。当使用++、–、或*操作符时实际上就是对_Ptr进行操作。

迭代器主要有以下类型:
正向迭代器
反向迭代器
文件流迭代器

环境说明:windows7(64bit)、QT5.4.1

// 迭代器
#include<stdio.h>
void iteratorTest(){

    vector<int> arr = {1,2,3,4,5,6,7,8};
    vector<int>::const_iterator ib = arr.begin();
    vector<int>::const_iterator ie = arr.end();

    //printf("%p %p \n\n", ib._Ptr, ib);
    for(;ib != ie; ++ib){
        //printf("%p %p \n", ib, ib._Ptr);
        printf("%p %p \n", ib._Ptr, ib);
    }
}

打印结果如下:
这里写图片描述
在64位系统地址是16位的,可以每次打印ib看到的都是同一个地址,但ib._Ptr在++ib后每次都有变化。

容器

array(静态数组)

//静态数组:可随机访问,内存分配在栈上,长度不可变
#include<array>
void main(){

    // 定义一个大小为5的数组
    std::array<int,5> arr = {1,2,3,4,5};
    arr[0] = 1;
    arr[1] = 3;
    arr[2] = 2;

    cout<<"size="<<arr.size()<<endl;

    // 打印每个元素
    for_each(arr.begin(), arr.end(), [](int i){ //lambda表达式
        cout<<i<<endl;
    });
}

vector(动态数组)

// 动态数组:可随机访问,插入和删除效率较低,内存分配在堆上,长度可动态变化
#include<vector>
void vectorTest()
{
    vector<int> arr = {1,2,3,4,5};

    // 添加元素
    arr.push_back(6);
    // 随机访问
    arr[5] = 7;
    //删除索引为0—1的元素
    arr.erase(arr.begin(),arr.begin()+2);

    // 迭代,打印
    for_each(arr.begin(), arr.end(), [](int val){
        cout<<val<<endl;
    });
}

list(链表)

// 链表:插入和删除时间复杂度为O(1),相对效率较高,随机访问效率为O(n),相对效率较低。
#include<list>
void listTest(){
    list<int> l;

    //在结尾添加元素
    l.push_back(1);
    l.push_back(2);
    //在开头添加元素
    l.push_front(3);

    list<int>::iterator ib = l.begin();
    ++ib;//链式结构容器的迭代器不能使用+操作符...只能这样了...
    ++ib;
    // 第3个位置插入一个元素
    l.insert(ib, 100);

    // 迭代打印
    for_each(l.begin(), l.end(), [](int val){
        cout<<val<<endl;
    });
}

注意:merge和unique函数依赖于sort,也就是合并list和去除重复元素这两个操作是在list有序的前提下才可执行。

stack(栈)


#include <stack>
// 栈(stack):先进后出,后进先出。
void main()
{
    // 把一个数字转换为二进制
    int a = 100;
    stack<int> s;

    // 一个数字不断除以2的余数,由下往上的数字序列就是其二进制数
    while(a){
        s.push(a%2);
        a/=2;
    }

    // 打印该数字的二进制数
    while(!s.empty()){
        cout<< s.top();
        s.pop();
    }
    cout<<endl;
}

queue(队列)


#include<queue>
//队列(queue):先进先出。
void main(){

    queue<int> q;
    // 入队
    q.push(1);
    q.push(1);
    q.push(0);
    q.push(1);
    q.push(0);

    // 打印队列元素个数
    cout<<"queue size : "<<q.size()<<endl;

    while(!q.empty()){
        cout<<q.front();
        // 出队
        q.pop();
    }
    cout<<endl;
}

deque(双向队列)

// 打印函数
void print(int n){
    cout<<n;
}

#include<deque>
//双端队列(deque):两端都可进可出,可随意位置插入和删除。  可反向迭代。可整体交换值(deque.swap(deque))。
void main(){

    deque<int> q;
    q.push_back(1);
    q.push_back(2);
    q.push_back(3);
    q.push_back(4);
    q.push_back(5);
    q.push_back(6);
    q.push_front(0);

    deque<int> q2;
    q2.push_back(11);
    q2.push_back(12);
    q2.push_back(13);

    //使用swap函数实现整体交换
    q.swap(q2);

    // 正向迭代
    /*
    deque<int>::const_iterator cib =  q.begin();
    while( cib != q.end())
    {
        cout<<*cib;
        cib++;
    }
    */
    for_each(q.begin(),q.end(),print);
    cout<<endl;

    for_each(q2.begin(),q2.end(),print);
    cout<<endl;

    // 反向迭代
    /*
    deque<int>::reverse_iterator rib = q.rbegin();
    while( rib != q.rend())
    {
        cout<<*rib;
        rib++;
    }
    */
    for_each(q.rbegin(),q.rend(),print);
    cout<<endl;

    // 随机访问
    q[1] = 2;

    for_each(q.begin(),q.end(),print);
    cout<<endl;
}

priority_queue(优先级队列)

#include<vector>
#include<queue>
//优先级队列(priority_queue)是一个可自动排序的队列。
void main(){

    priority_queue<int> q;
    q.push(1);
    q.push(2);
    q.push(3);
    q.push(4);
    q.push(5);

    // 自动排序,默认按从大到小的顺序
    while(!q.empty()){
        cout<<q.top();
        q.pop();
    }
    cout<<endl;

     自定义类的大小比较 ///
    // 类
    class student{
    public:
        int age;
        std::string name;

    };
    // 比较器
    class studentComparator{
    public:
        // age值较大的排在前面
        bool operator()(const student& s1,const student& s2)
        {
            return s2.age > s1.age;
        }
    };

    //priority_queue<类型,容器,比较器>
    priority_queue<student,vector<student>,studentComparator> students;
    student s1;
    s1.age = 10;
    s1.name = "xiaoming1";

    student s2;
    s2.age = 15;
    s2.name = "xiaoming2";

    student s3;
    s3.age = 9;
    s3.name = "xiaoming3";

    students.push(s1);
    students.push(s2);
    students.push(s3);

    while(!students.empty()){
        student s = students.top();
        cout << "name = " << s.name.c_str() << " , age = " << s.age << endl;
        students.pop();
    }
}

红黑树

红黑树实际上是可自平衡的二叉树。如果二叉树不平衡的话,结构就可能类似链表,而二叉树最大的特点就是可以实现二分查找,不平衡的二叉树查找效率就不高了,为了解决这个问题就出现了可自平衡的二叉树,即红黑树。红黑树可以在O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目。>>维基百科
红黑树的综合效率是数组与链表的折中。
这里写图片描述

set(集合)

set

#include<set>
// set:红黑树,不可有重复元素,自动排序。
void setTest(){

    set<int> s;
    // 插入是乱序的
    s.insert(5);
    s.insert(3);
    s.insert(1);
    s.insert(4);
    s.insert(0);
    s.insert(2);

    // 红黑树会自动对元素进行排序
    // 打印结果为: 0 1 2 3 4 5
    for_each(s.begin(), s.end(), [](int val){
        cout<<val<<" ";
    });
    cout<<endl;


    /// 自定义排序方式 /
    // 类
    struct student{
        std::string name;
        int age;
    };
    // 比较器
    struct studentComparator{
        bool operator()(const student &s1, const student &s2){
            return s1.name.compare(s2.name) < 0; //比较两个字符串,字母较小的排前
        }
    };

    // 使用自定义比较方式,默认是使用less函数,小的排在前面
    set<student,studentComparator> students;

    student s1 = {"ab",11};
    student s2 = {"b",12};
    student s3 = {"a",13};

    // 插入元素
    cout<<endl;
    auto p1 = students.insert(s1);
    cout<<"insert: "<<(*p1.first).name.c_str()<<" "<<p1.second<<endl;

    // insert函数返回一个pair,pair包含插入元素的迭代器和插入结果
    pair<set<student,studentComparator>::iterator, bool> p2 = students.insert(s2);
    cout<<"insert: "<<(*p2.first).name.c_str()<<" "<<p2.second<<endl;

    auto p3 = students.insert(s3);
    cout<<"insert: "<<(*p3.first).name.c_str()<<" "<<p3.second<<endl;
    cout<<endl;

    // 打印所有元素
    for_each(students.begin(), students.end(), [](const student &stu){
        cout<<stu.name.c_str()<<" "<<stu.age<<endl;
    });
}

multiset

// multiset:红黑树,每个节点都是一个链表,可有重复元素,自动排序
void multiSetTest(){

  multiset<int> ms;
  ms.insert(2);
  ms.insert(1); //插入了两个1
  ms.insert(1);
  ms.insert(3);

  // 可插入重复元素
  // 打印结果为:1 1 2 3
  for_each(ms.begin(), ms.end(), [](int val){
    cout<<val<<" ";
  });
  cout<<endl;

  // find与equal_range的区别
  // 1.find:查找目标元素出现的第一个位置,返回该位置的迭代器
  auto it1 = ms.find(1);
  cout<<"find : "<<*it1<<endl;

  // 2.equal_range:查找所有目标元素(multiset可包含重复元素),返回链表(multiset每个节点就是一个链表)的迭代器
  auto it2 = ms.equal_range(1);
  //first是链表的开始结点,second是链表的结束结点
  for_each(it2.first, it2.second, [](int i){
      cout<<"find : "<<i<<endl;
  });
  cout<<endl;


  struct student{
      std::string name;
      int age;
  };

  struct studentComparator{
      bool operator()(const student& s1, const student& s2){
        return s1.name.compare(s2.name) > 0; //字母大的靠前
      }
  };

  student s1 = {"c",11};
  student s2 = {"bd",12};
  student s3 = {"abc",13};

  multiset<student,studentComparator> students;
  students.insert(s2);
  students.insert(s1);
  students.insert(s3); //这里插入了两次s3,都能成功插入
  students.insert(s3);

  // 打印所有元素
  for_each(students.begin(), students.end(), [](const student &stu){
        cout<<stu.name.c_str()<<" "<<stu.age<<endl;
  });
}

map(键值对集合)

map

#include<map>
// map:实际上也是红黑树,每个节点,都是一个键值对,key不允许重复。
void mapTest(){

    map<const char*, int> m;

    // 插入元素
    pair<const char*, int> p1 ("aaaa",2);
    m.insert(p1);
    m["a"] = 1;
    m["bb"] = 3;
    //insert,重复插入的元素将会被忽略
    m.insert(p1);

    // 遍历所有元素
    for_each(m.begin(), m.end(), [](pair<const char*, int> p){
        cout<<p.first<<" - "<<p.second<<endl;
    });

    // 自定义类自定义排序方式 ///
    struct student{
        string name;
        int age;
    };
    struct studentComparator{
        bool operator()(int key1, int key2){
            return key1 > key2; //让大数排在前面
        }
    };

    student s1 = {"c",11};
    student s2 = {"bd",12};
    student s3 = {"abc",13};

    map<int,student,studentComparator> stum;
    stum[1] = s1;
    stum[2] = s2;
    stum[3] = s3;

    // 遍历所有元素
    for_each(stum.begin(), stum.end(), [](pair<int, const student&> p){
        cout<<p.first<<" - [ "<<p.second.name.c_str()<<" , "<<p.second.age<<" ]"<<endl;
    });
}

multimap

// multimap:红黑树,每个节点都是一个键值对链表,所以key允许重复,自动对元素进行排序。
void multiMapTest(){

    multimap<int,int> m;
    //m[1] = 1; //multimap不可以使用这种方式
    pair<int,int> p (3,3);
    // 这里重复插入了p,但都能插入成功
    m.insert(p);
    m.insert(p);
    m.insert(p);
    m.insert(pair<int,int>(1,1));
    m.insert(pair<int,int>(2,2));

    //遍历每个元素
    for_each(m.begin(), m.end(), [](pair<int,int> p){
        cout<<p.first<<" - "<<p.second<<endl;
    });
}

hash(哈希)

散列表(Hash table,也叫哈希表),是根据关键字(Key value)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。>>维基百科

hash_set

// hash:精确查找,快速,一次找到,比二分查找快,无序,不可重复。
#include<hash_set>
void hashSetTest(){

    hash_set<const char*> hs;
    hs.insert("aa");
    hs.insert("aaa");
    hs.insert("a");
    const char* str = "aaaa";
    hs.insert(str);
    hs.insert(str); // 重复插入的元素将被忽略
    hs.insert(str);

    // 这里的两个字符将被重复插入,因为他们的hashCode不一样
    hs.insert("aaaa");
    hs.insert("aaaa");

    // 无序
    for_each(hs.begin(), hs.end(), [](const char* str){
        cout<<str<<endl;
    });

    //hash_set<const char*,hash<const char*>,comparator> hs;
}

hash_map

#include<hash_map>
void hashMapTest(){

    hash_map<const char*, const char*> m;
    m.insert(pair<const char*, const char*>("1","a"));
    m.insert(pair<const char*, const char*>("2","b"));
    m.insert(pair<const char*, const char*>("3","c"));
    m.insert(pair<const char*, const char*>("4","d"));
    m.insert(pair<const char*, const char*>("4","d"));
    m["5"] =  "e";

    for_each(m.begin(), m.end(), [](pair<const char*, const char*> p){
        cout<<p.first<<" - " <<p.second<<endl;
    });
}

bitset


// 位集合
#include<bitset>
// 使用bitset可以很方便得将一个数字转换为其二进制数
void bitsetTest(){
    //    位数      根据一个数字构造一个二进位集合
    bitset<8> bits (1);
    //bitset<8> bits (256);

    // 是否所以位都没有被设置为1
    bool isNone = bits.none();
    cout<<"isNone : "<< isNone <<endl;

    // 是否有位已经被设置为1
    bool isAny = bits.any();
    cout<<"isAny : "<< isAny <<endl;

    // 测试指定索引位是否为1
    bool testResult = bits.test(1);
    cout<<"testResult : "<< testResult <<endl;

    // 打印所有位:注意存储的顺序是从高位到低位的,并没有做高低位转换,所以顺序是反的
    for(int i=0; i<bits.size(); ++i){
        cout<<bits[i];
    }
    cout<<endl;

    // 转换为字符串,顺序是正的
    cout<<bits.to_string()<<endl;

    // 所有位重置为0
    bits.reset();

    cout<<bits.to_ulong()<<endl;
}

算法

包含算法相关的头文件:#include

for_each

#include<vector>
#include<iostream>
#include<algorithm> //算法相关的头文件

using namespace std;

// 打印函数
void print2(int val){
    cout<<val<<endl;
}

// 模板
template<class T>
class Printor{
public:
    // 重载()操作符
    void operator()(T val){
        cout<<val<<endl;
    }
};

void forEachTest(){

    // 定义一个动态数组
    std::vector<int> arr;
    // 添加元素
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(3);

    //迭代打印容器
    //1.使用lambda表达式
    for_each(arr.begin(), arr.end(), [](int val){
        cout<<val<<endl;
    });


    //2.使用函数指针
    for_each(arr.begin(), arr.end(), print2);

    //3.使用模板
    Printor<int> printInt;
    for_each(arr.begin(), arr.end(), printInt);//当类的()操作符被重载时,可使用类似调用函数的方式去调用该类

    // 实例化模板
    Printor<const char*> printStr;
    printStr("abc"); //调用()操作符
}

remove_if


#include<list>
#include<functional>
void listTest(){
    list<int> l;

    //在结尾添加元素
    l.push_back(1);
    l.push_back(2);
    //在开头添加元素
    l.push_front(3);

    list<int>::iterator ib = l.begin();
    ++ib;//居然没有重载+操作符...只能这样了...
    ++ib;
    // 第3个位置插入一个元素
    l.insert(ib, 100);

    // 3 1 100 2

    //bind1st:用于绑定一个函数
    //greater:是一个struct模板,重载了()操作符,实现了比较功能,设定值大于目标值则返回true
    //greater_equal:设定值大于等于目标值则返回true
    //equal_to:设定值等于目标值则返回true
    //less:设定值小于目标值则返回true
    //less_equal:设定值小于等于目标值则返回true
    //需要#include<functional>

    //移除小于3的值
    //remove_if(l.begin(), l.end(), bind1st(greater<int>(),3)); //打印结果为3 100 100 2,不对,此处有bug...

    // 保留小于等于3的值
    l.remove_if(bind1st(less_equal<int>(),3));

    // 迭代打印
    for_each(l.begin(), l.end(), [](int val){
        cout<<val<<endl;
    });
}

sort

    vector<int> arr;
    arr.push_back(3);
    arr.push_back(1);
    arr.push_back(4);
    arr.push_back(2);
    arr.push_back(5);

    // 排序(默认从小到大排序)
    //sort(arr.begin(),arr.end());
    // 指定比较使用的函数,greater表示从大到小排序,less表示从小到大排序,如果是自定义类也可以使用自定义的比较方式
    sort(arr.begin(),arr.end(),greater<int>());

    for_each(arr.begin(),arr.end(),[](int val){
        cout<<val<<endl;
    });

find

    vector<int> arr;
    arr.push_back(3);
    arr.push_back(1);
    arr.push_back(4);
    arr.push_back(2);
    arr.push_back(5);

    vector<int>::iterator it = find(arr.begin(),arr.end(),5);
    if(it != arr.end()){
        cout<<"found! "<<*it<<endl;
    }else{
        cout<<"not found!"<<endl;
    }

find_if

#include<vector>
#include<functional>
void findIfTest(){

    vector<int> arr;
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(3);

    //查找等于3的值,如果查找成功返回该值所在位置的迭代器,否则返回结束位置迭代器
    vector<int>::iterator it = find_if(arr.begin(),arr.end(),bind1st(equal_to<int>(),3));
    if(it != arr.end()){
        cout<<"found! "<<*it<<endl;
    }else{
        cout<<"not found!"<<endl;
    }

    // 查找第一个不大于3的元素,返回该元素的迭代器
    vector<int>::iterator it2 = find_if_not(arr.begin(),arr.end(),[](int i)->bool{ //lambda表达式,返回一个bool值
        return i>3;
    });
    if(it2 != arr.end()){
        cout<<"found! val="<<*it2<<endl;
    }else{
        cout<<"not found! it2"<<endl;
    }

}

随机排序

// 打印函数
template<class Type>
struct show{
    void operator()(Type t){
        cout<<t<<"\t";
    }
};

// 对容器中元素进行随机排序
void AlgorithmTest::randomTest(){
    vector<int> arr;
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(3);
    arr.push_back(4);
    arr.push_back(5);

    for_each(arr.begin(),arr.end(),show<const int>());
    cout<<endl;

    // 对指定位置元素进行随机排序
    random_shuffle(arr.begin(),arr.end());

    for_each(arr.begin(),arr.end(),show<const int>());
}

adjacent_find

//adjacent_find,找出一组重复的元素,multiset、multimap每一个节点都是一个链表,可有重复元素。
#include<set>
void AlgorithmTest::adjacent_findTest(){
    multiset<int> s;
    s.insert(1);
    s.insert(2);
    s.insert(2);
    s.insert(2);
    s.insert(2);
    s.insert(3);
    s.insert(3);

    // 返回该节点的迭代器
    auto it = adjacent_find(s.begin(),s.end());
    for_each(it,s.end(),show<int>());
    //cout<<*it<<endl;
}

rotate(begin, end, target); //把begin到end的元素移动到target的后面,后面的元素往前移
fill(begin, end, val); //填充指定元素
count(begin, end, val); //统计指定元素个数

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值