c++—STL (six major components)

1. Overview of STL

 

        1. Overview of STL

        (1) STL definition: STL (standard template library), the standard template library, is an efficient C++ program library that focuses on improving the reusability of code; it mainly includes commonly used data structures and basic algorithms, and is used for the majority of C++ programs. Provides an extensible application framework;

        It embodies the idea of ​​generic (templated) programming. In terms of implementation, it is implemented in a type program parameterized manner; it is implemented using template classes, so the type of storage element (basic type or custom type) needs to be executed when defining. type);

      2. Six major components of STL

        (1) Container: various basic data structures

        (2) Iterator: often used to traverse containers, and can also be used to connect containers and algorithms;

        (3) Algorithm: Provides various basic algorithms such as sort, search, etc.;

        (4) Adapter: a component that can change the interface of containers, iterators or function objects;

        (5) Function object: wrap the function with a type and type the function;

        (6) Allocator: internally calls malloc to allocate space;

 2. Container

        1. Classification: It can be divided into three categories:

        (1) Sequence containers, mainly including array, vector, deque, list, forward_list;

        (2) Associative containers (associative containers), the internal mechanism is a tree, a hash table, and the elements are key/value pairs. They are particularly suitable for searches because their time complexity is low; and most of them have automatic sorting functions; they mainly include set, multiset, map, multimap;

        (3) Unordered (hash) containers (UNordered containers), the internal mechanism is a hash table, introduced in C++11: the position of the element is not important, what is important is whether the element is in the container, mainly suitable for search ; Mainly include: underde_set, unordered_multiset, unordered_map, unordered_multimap;

       2. Vector container header file #include<vector>

        (1) Essence and characteristics

        ①Vector has a built-in dynamic array that can randomly access elements, and the [ ] and at() methods can be used;

        ②It is very fast to add or remove elements from the tail of vector;

        ③Because inserting vector in the middle will cause all subsequent elements to be re-destructed and copy-constructed, which is very inefficient;

        ④Vector is implemented using template classes, so the type of storage element (basic type or custom type) needs to be executed when defining.

        (2) Main operations

Default construction of vector objects vector<int>v1;
Parameterized construction of vector objects vector<int>v2(v1.begin()+1, v1.begin()+4);
Insertion of vector push_back、insert
vector data access v1.at(), v1[ ], v1.front(),
Deletion of vector pop_back(), clear(), erase(),
vector iterator begin(), end(), rbegin(), rend()
vector size size(), empty(), capacity(), resize(), shrink_to_fit
Assignment of vector assign(), operator=, swap

vector example:

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

class Test
{
public:
    Test()
    {
        // cout << "Test" << endl;        
    }

    Test(int t) : m_t(t)
    {
        // cout << "Test int" << endl;
    }

    Test(const Test &other)
    {
        m_t = other.m_t;
        // cout << "Test copy" << endl;
    }

    ~Test()
    {
        // cout << "~Test" << endl;
    }

    friend ostream& operator<<(ostream&out, Test &t);
    friend bool operator!=(const Test&t, int num);
    friend int operator%(const Test&t, int num);

    int m_t;
};
ostream& operator<<(ostream&out, Test &t)
{
    out<<t.m_t;
    return out;
}
bool operator!=(const Test&t, int num)
{
    return (t.m_t != num);
}
int operator%(const Test&t, int num)
{
    return (t.m_t % num);
}

template <typename T>
void print(vector<T> &vt)
{
    for(int i = 0; i < vt.size(); i++)
    {
        cout<<vt.at(i)<<" ";
    }
    cout<<endl;
}

template <typename T>
void delete_node(vector<T> &vt)
{
    for(auto it = vt.begin(); it != vt.end(); )
    {
        
        if(*it % 2 != 0)
        {
            // it = vt.erase(it);
            vt.erase(it);
        }
        else
        {
            ++it;
        }
    }
}

int main(int argc, char **argv)
{
    vector<Test> vt;
    vt.reserve(20);  //提前预留20个元素值,避免了空间不够时,频繁的拷贝构造
    for(int i = 0; i < 9; i++)
    {
        vt.push_back(Test(i+1));  //连续构造1-10
    }

    print(vt);
    delete_node(vt);  //删除其中是基数的元素值(test的m_t是奇数的)
    print(vt);

    cout<<"vt.capacity() = "<<vt.capacity()<<endl;  //输出vt容器的可容纳元素的大小,20
    vt.shrink_to_fit();  //回收未被利用的空间
    cout<<"vt.capacity() = "<<vt.capacity()<<endl;  //输出vt容器的可容纳元素的大小,4

    vt.assign(7,7);  //将7个7,赋给vt容器,注意是系统默认将vt原有的内容先清空
    print(vt);

    return 0;
}

        3. list container header file #include<list>

        (1) Essence and characteristics

        ①List is a doubly linked list container, suitable for efficiently inserting or deleting elements at the head and tail;

        ②List does not support random access elements, so it does not support the at() method and the [ ] operator; please note that it++ is available for iterators, but it+2 is not supported; it can only be traversed, not jumped;

        (2) Main operations

Default construction of list objects list<int> l1; can also be a custom type;
Parameterized construction of list objects list<int> l2 (l1.begin()+1, l1.end());
Insertion into list

l2.push_back();     l2.pop_back();

l2.push_front();     l2.pop_front();

l2.emplace_back(); l2.emplace_front();

delete list element

l2.clear(); 

l2.erase(beg,end);

l2.erase(pos);

l2.remove(elem);

l2.remove_if();

list.unique();

iterator to list

l2.begin();

l2.end();

l2.rbegin();

l2.rend();

List element access

l2.front();

l2.back();

Assignment of list

l2.assign();

l2.swap(l1);

list size

l2.size();

l2.empty();

l2.resiae();

l2.resize();

Arrangement of list

l2.sort();

l2.reverse();

list operations on containers

splice();

list example:

#include <list>
#include <iostream>
#include <vector>

using namespace std;

template <typename T>
class Deleteor
{
public:
    bool operator()(const T &t)
    {
        return (t % 2) == 0;
    }
};

template <typename T>
void print(list<T> & other)
{
    for(auto it = other.begin(); it != other.end(); ++it)
    {
        cout<<*it<<" ";
    }
        cout<<endl;
}

int main(int argc,char **argv)
{
    list<int> l1;
    l1.emplace_back(1);
    l1.emplace_back(2);
    l1.emplace_back(3);
    l1.emplace_back(4);
    l1.emplace_back(5);
    l1.emplace_back(6);
    l1.emplace_back(7);
    l1.emplace_front(8);

    print(l1);  

    l1.insert(std::next(l1.begin(),3),9);  //std::next(l1.begin(),3)是制定插入的位置
    print(l1); 

    l1.remove(4);  //删除值等于4的元素
    print(l1);

    l1.remove_if(Deleteor<int>());  //Deleteor<int>()是删除的规则,返回的是bool(true,false)
    print(l1);                      //这里是一个函数对象,重载了函数运算符()

    l1.sort();  //对list排序,默认是从小到大
    print(l1);

    l1.sort(greater<int>());  //内建函数(对象),实现从大到小排序
    print(l1);

    l1.reverse();  //对list逆序
    print(l1);

    list<int>l2 = {1,3,5,7,9};
    list<int>l3 = {2,4,6,8,10};
    l2.merge(l3);  //将l3合并到l2上,并按照默认的从小到大方式排好序
    cout<<"l2 : ";
    print(l2);
    cout<<"l3 : ";
    print(l3);  //l3会被清空

    list<int>l4 = {7,7,7,7};

    auto it = l2.begin();
    std::advance(it,2);  //将it迭代器后退2个位置,-2就是前进2个位置
    l2.splice(it,l4);
    cout<<"l2 : ";
    print(l2);

    return 0;
}

        4. forward_list container

        Essence and characteristics: Generated by optimizing list, forward_list is a one-way linked list, which saves one pointer and saves system overhead;

        ①Only forward iterators are provided, reverse iterators are not supported;

        ②The size() member function is not provided;

        ③There is no anchor point pointing to the last element, so back(), push_back(), and pop_back() are not provided;

        ④Does not provide random access, same as list;

        ⑤插入和删除元素不会造成“指向其他元素的”指针、引用、迭代器失效;

        5. deque 容器,本质及特点:

        ①deque是双端数组,而vector是单端的;

        ②与vector一样,可以随机存取元素;且在头部和尾部移除元素都非常快速,但是在中间插入元素则比较费时;

        ③其他内容以vector一样;

        6. set/multiset 容器  头文件:#include<set>

        (1)本质及特点

        ①set是一个集合容器,其中包含的元素是唯一的,集合中的元素按一定的顺序排列;元素插入过程是按排序规则插入,所以不能指定插入位置;

        ②set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树,在插入操作和删除操作上比vector快;时间复杂度是O(log n);

        ③set不可以直接存取元素(不可以使用at.(pos)与[ ] 操作符);

        ④multiset与set的区别是:set支持唯一键值,即每一个元素只能出现一次;而multiset中的元素可以出现多次;

        ⑤不可以直接修改set或multiset容器中的元素值,因为这类容器是自动排序的,如果要修改其中的某个元素值,必须先删除原有的元素,再插入新的元素;

程序示例:

#include <iostream>
#include <set>
#include <vector>

using namespace std;

template <typename T>
void print(const set<T> &other)
{
    for(auto it = other.begin(); it != other.end(); ++it)
    {
        cout<<*it<<" ";
    }
    cout<<endl;
}

int main(int argc, char **argv)
{
    set<int>si;
    si.insert(1);
    si.insert(3);
    si.insert(7);
    si.insert(5);
    si.insert(2);

    print(si);

    // auto node = si.extract(3);  //修改成员值,c++17,本系统不支持
    // node.value = 9;
    // si.insert(move(node));

    // set<int>s2 = {2,4,6,8};
    // si.merge(s2);  //将s2合并到si,c++17,本系统不支持

    cout<<"si.count(5) : "<<si.count(5)<<endl;  //数值是5的元素有几个?

    auto it = si.find(5);
    if(it != si.end())
    {
        cout<<"找到了"<< *it <<endl;
    }

    auto it1 = si.lower_bound(5); //返回首个不小于5的元素的迭代器
    auto it2 = si.upper_bound(5); //返回收个大于5的元素迭代器
    if(it1 != si.end())
    {
        cout<<"it1 = "<<*it1<<endl;
    }
    if(it2 != si.end())
    {
        cout<<"it2 = "<<*it2<<endl;
    }

    return 0;
}

        7. map/multimap 容器  #include<map>

        (1)本质及特点

        ①map是标准的关联式容器,一个map是一个键值对(一个序列号,一个值,相当于数组的索引和对应的值),它提供基于key(键值)的快速检索能力;键值对中第一个元素是first,第二个元素是second,支持迭代器访问输出;

        ②map中的key值是唯一的;集合中的元素按一定的顺序排列,元素插入过程是按排序规则插入,所以不能指定插入位置;

        ③map的具体实现采用红黑树变体的平衡二叉树数据结构,在插入和删除操作上比vector块;

        ④map可以直接存取key所对应的value,支持[ ] 操作符,如map[key] = value;

        ⑤multimap 与 map 的区别:map支持唯一键值,每个键只能出现一次,而multimap中相同的键可以出现多次。但是multimap不支持[ ]操作符;

        (2)示例

#include <iostream>
#include <map>
#include <string>

using namespace std;

template<typename key, typename value>
void print(const map<key, value> &other)
{
    for(auto it = other.begin(); it != other.end(); ++it)
    {
        cout<<"key : "<<it->first<<" value : "<<it->second<<endl;
    } 
}

int main(int argc, char **argv)
{
    map<int, string> m1;  //和数组很像,索引+对应的值
    m1[1] = "hello1";
    m1[2] = "hello2";
    m1[3] = "hello3";

    print(m1);

    return 0;
}

        (3)一个面试题示例:给定一个数组,计算每个数值出现的次数,然后按字符出现次数的升序输出数值及其出现次数;并且单独输出个数大于数组个数一半的元素;

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

using namespace std;

template<typename key, typename value>
void print(const multimap<key, value> &other)
{
    for(auto it = other.rbegin(); it != other.rend(); ++it)
    {
        cout<<"降序遍历 : 值"<<it->second<<"   次数:"<<it->first<<endl;
    } 
}

int main(int argc, char **argv)
{
    int num[]={1,3,3,3,5,3};
    int len  = sizeof(num)/(sizeof(num[0]));
    cout<<"len = "<<len<<endl;  //输出元素的个数
    map<int,int>mc;

    for(auto &temp : num)
    {
        mc[temp]++;
    }
    // cout<<"mc[3] = "<<mc[3]<<endl;  //输出数值3对应的次数

    multimap<int,int>mi;  //注意这里又新建了一个容器,在插入元素时,与mc相反,以次数为键值,这样就会自动根据次数排序,默认是升序,若要降序,则需要采用逆向迭代器
    for(auto it = mc.begin(); it != mc.end(); ++it)
    {
        mi.insert(pair<int,int>(it->second,it->first));
    }

    for(auto &temp : mi)
    {
        cout<<"升序遍历 : 值"<<temp.second<<"   次数:"<<temp.first<<endl;  //注意前后的输出顺序对照上
    }

    print(mi);
    
    for(auto &temp : mc)
    {
        if(temp.second > (len/2))
        {
            cout<<"符合要求:"<<temp.first<<" 出现次数:"<<temp.second<<endl;
        }
    }

    return 0;
}

结果:

        8. 容器的选择原则总结

        ①vector是内置有动态数组,当容量满时会自动扩充,在末尾有定位指针,所以末尾插入十分快速,但中间插入则较慢,因为涉及到原有数组的拷贝;最大的特点是适合高效的随机访问;而deque则优化为可以既可以头插也可以末尾插;

        ②list是一个双向链表容器,访问指定元素的效率较低,不支持随机访问,最大的特点是中间插入的效率较快;

        ③set是一个集合容器,元素具有唯一性,因为排序功能,所以开销较大;若需要支持重复数据则需要使用multiset;

        ④map是关联式容器,内置键值对,且内部按照顺序排序,内部采用红黑树的数据结构,时间复杂度是O(log n ),查找与删除的效率很高;适合字典式存储数据;

Guess you like

Origin blog.csdn.net/m0_72814368/article/details/130994549