C++专题--标准模板库STL

c++专题-标准模板库STL

  • 1   标准模板库概述
  • 2   序列式容器
    •   2.1 vector 容器
    •   2.2 deque 容器
    •   2.3 list 容器
  • 3   关联式容器
  • 4   无序关联容器
  • 5   容器适配器
    •   5.1 STL容器适配器的种类
    •   5.2 stack容器适配器
    •   5.3 queue容器适配器
    •   5.3 priority_queue容器适配器

1   标准模板库概述

STL,英文全称 standard template library,中文可译为标准模板库或者泛型库,其包含有大量的模板类和模板函数,是 C++ 提供的一个基础模板的集合,用于完成诸如输入/输出、数学计算等功能

容器的分类

C++专题--标准模板库STL_第1张图片

2   序列式容器

  2.1 vector 容器

  1. vector容器的创建及常用的方法
int32 AdapterTest::vectorCreTest()
{
    // 空的vector容器,因为容器中没有元素,所以没有为其分配空间
    // 当添加第一个元素(比如使用 push_back() 函数)时,vector 会自动分配内存
    std::vector<int32> testVec;
    // 增加容器的容量
    testVec.reserve(10);

    // 除了创建空的向量容器外,也可以在创建的同时指定元素的初始值及元素的个数
    std::vector<int32> primes{2, 3, 5, 7, 11, 13, 17, 19};

    // 在创建vector容器的同时指定元素的个数
    std::vector<int32> values(20);

    // 指定元素的个数及初始默认值
    std::vector<int32> values1(10,3);
    return 0;
}
  1. 常用的成员函数
int32 AdapterTest::vectorFuncTest()
{
    std::vector<int32> nums;
    for (int i = 0; i < 30;++i)
    {
        if((i & 1) == 0)
        {
            nums.push_back(i);
        }
    }

    /*
     *  vector容器的遍历
     *  - begin()
     *      返回指向容器中第一个元素的迭代器
     *  - end()
     *      返回指向容器最后一个元素所在位置后一个位置的迭代器
     */
    cout << "vector iterator traversal begin...." << endl;
    std::vector<int32>::iterator it = nums.begin();
    for(;it != nums.end();++it)
    {
        cout << *it << " ";
    }
    cout << endl;
    cout << "vector iterator traversal end...." << endl;
    cout << endl;
    /*
     *
     *  -rbegin()
     *     返回指向最后一个元素的迭代器
     *  -rend()
     *     返回指向第一个元素所在位置前一个位置的迭代器
     *
     */
    cout << "vector reverse_iterator traversal begin......" << endl;
    std::vector<int32>::reverse_iterator rit = nums.rbegin();
    for (; rit != nums.rend();++ rit)
    {
        cout << *rit << " ";
    }
    cout << endl;
    cout << "vector reverse_iterator traversal end......" << endl;

    /*
     *   - cbegin()
     *      和begin()功能相同,只不过在其基础上,增加了const属性,不能用于修改元素
     *   - cend()
     *      和end()功能相同,只不过在其基础上,增加了const属性,不能用于修改元素
     *      const_iterator 可以接收 begin() 返回的数据 也可以接收cbegin()返回的数据
     */
    cout << "vector reverse_iterator traversal begin......" << endl;
    std::vector<int32>::const_iterator cit = nums.begin();
    for(;cit != nums.end();++cit)
    {
        cout << *cit << " ";
    }
    cout << "vector reverse_iterator traversal end......" << endl;
    cout << endl;

    /*
     *  -crbegin()
     *      和rbegin()功能相同,只不过在其基础上,增加了const属性,不能用于修改元素。
     *  -crend()
     *      和rend()功能相同,只不过在其基础上,增加了const属性,不能用于修改元素。
     *  在使用反向迭代器进行++或--运算时,++指的是迭代器向左移动一位,--指的是迭代器向右移动一位,即这两个运算符的功能也“互换”了
     */
    // const_reverse_iterator 可以接收 rbegin() 也可以接收crbegin()的返回值
    cout << "vector const_reverse_iterator traversal begin......" << endl;
    std::vector<int32>::const_reverse_iterator crit = nums.rbegin();
    for (;crit != nums.rend();++crit)
    {
        cout << *crit << " ";
    }
    cout << endl;
    cout << "vector const_reverse_iterator traversal end......" << endl;

    /*
     *
     *  重载了 []
     *  size() 容器中元素的个数
     *
     */
    for(int i = 0; i < nums.size();++i)
    {
        cout << nums[i] << " ";
    }
    cout << endl;
    /*
     *  vector 增 删 改 查 操作
     *  - front()
     *      返回第一个元素的引用
     *  - back()
     *      返回最后一个元素的引用
     *  - push_back()
     *      在序列的尾部添加一个元素
     *  - pop_back()
     *     弹出序列尾部的元素
     */
    std::vector<int32>  vecTest{10,20,30,40};

    cout << "front() begin" << endl;
    //cout << *vecTest.begin() << endl;
    //vecTest.front();
    //num = -1;
    cout << "front : " << vecTest.front() << " back : " << vecTest.back() << endl;
    //cout << "num : " << num <<  endl;
    cout << "front() end" << endl;

    vecTest.push_back(50);
    vecTest.insert(vecTest.end(),60);
    // 仅仅是弹出尾部的元素  没有返回值
    vecTest.pop_back();

    // 增加容器的容量
    cout << "capacity : " << vecTest.capacity() << endl;
    vecTest.reserve(100);
    cout << "capacity : " << vecTest.capacity() << endl;
    /*
     *
     * 使用反向迭代器进行遍历
     *
     *
     */
    std::vector<std::string> vecStr{"BOSS","EMPLOYEE","MANAGER"};
    auto first = vecStr.rbegin();
    auto end = vecStr.rend();
    while (first != end)
    {
        cout << *first << " ";
        // 前移一个
        ++first;
    }

    return 0;
}

 

  1. 访问单个元素
/**
 *
 * 访问单个元素
 * @brief AdapterTest::vectorFuncTest1
 * @return
 */
int32 AdapterTest::vectorFuncTest1()
{
    std::vector<int32> vecNums{10,20,30,40};
    // vector 的索引从0开始,这和普通数组一样。通过使用索引,总是可以访问到vector容器中现有的元素
    cout << "vecNums[0]  : " << vecNums[0] << endl;
    for (int i = 0; i < vecNums.size();++i)
    {
        cout << vecNums[i] << " ";
    }
    cout << endl;
}

 

  1. 往向量容器中插入元素 insert
int32 AdapterTest::vecInsertEleTest()
{
    int arr[]{10,20,4,6,16};
    std::vector<int32> vecNum;
    // 插入数组中的元素 在指定位置的前面插入元素
    vecNum.insert(vecNum.end(),arr,arr+5);

    std::vector<int32> vecOther{9,10,20,30};
    // 在vecNum的末尾插入另外一个元素
    vecNum.insert(vecNum.end(),vecOther.begin(),vecOther.end());

    //排序
    std::sort(vecNum.begin(),vecNum.end());
    std::vector<int32>::const_iterator iter = vecNum.begin();
    for(;iter != vecNum.end();++iter)
    {
        cout << *iter << " ";
    }
}
  1. 往向量容器中插入元素 emplace
/**
 * emplace()是 C++11 标准新增加的成员函数,用于在vector容器指定位置之前插入一个新的元素
 * emplace() 每次只能插入一个元素,而不是多个
 * @brief AdapterTest::vecEmplaceTest
 * @return
 */
int32 AdapterTest::vecEmplaceTest()
{
   std::vector<int32> vecTest;
   for(int i = 0; i <= 10; ++i)
   {
        vecTest.emplace(vecTest.end(),i);
   }

   std::vector<int32>:: const_iterator iter =  vecTest.begin();
   for(; iter != vecTest.end();++iter)
   {
       cout << *iter << " ";
   }
   cout << endl;
   return 0;
}

  2.2 deque 容器

  2.3 list 容器

STL list 容器,又称双向链表容器,即该容器的底层是以双向链表的形式实现的。这意味着,list 容器中的元素可以分散存储在内存空间里,而不是必须存储在一整块连续的内存空间中 st 容器中各个元素的前后顺序是靠指针来维系的,每个元素都配备了 2 个指针,分别指向它的前一个元素和后一个元素。其中第一个元素的前向指针总为 null,因为它前面没有元素;同样,尾部元素的后向指针也总为 null。

基于这样的存储结构,list 容器具有一些其它容器(array、vector 和 deque)所不具备的优势,即它可以在序列已知的任何位置快速插入或删除元素(时间复杂度为O(1))。并且在 list 容器中移动元素,也比其它容器的效率高

  1. list 容器的创建
/**
 *
 * list 相关的方法测试
 * @brief AdapterTest::listTest
 * @return
 */
int32 AdapterTest::listTest()
{
    // 创建一个没有任何元素的空list容器
    std::list<int32> li1;

    // 创建一个包含n个元素的list容器
    std::list<int32> li2(10);

    // 创建一个包含n个元素的list容器,并为每个元素指定初始值
    std::list<int32> li3(10,8);

    // 在已有 list 容器的情况下,通过拷贝该容器可以创建新的list容器
    std::list<int32> li4(li3);// 采用此方式,必须保证新旧容器存储的元素类型一致


    std::list<int32> testList;
    for(int i = 0; i < 100;i+=10){
        if ((i & 1) == 0){
            testList.push_back(i);
        }
    }

    cout << "size : " << testList.size() << endl;
    // begin()  返回指向容器中第一个元素的双向迭代器
    // end()    返回指向容器中最后一个元素所在位置的下一个位置的双向迭代器
    std::list<int32>::iterator iter = testList.begin();
    // 1.遍历
    cout << "------using iterator to travelsal list begin------" << endl;
    for(;iter != testList.end();++iter)
    {
        cout << *iter << " ";
    }
    cout << endl;
    cout << "------using iterator to travelsal list end------" << endl;

    // const_iterator cbegin() 也可以换成 begin()  cend() 也可以换成 end()
    std::list<int32>::const_iterator citer = testList.cbegin();
    cout << "------using const_iterator to travelsal list begin------" << endl;
    for(;citer != testList.cend();++citer)
    {
        cout << *citer << " ";
    }
    cout << endl;
    cout << "------using const_iterator to travelsal list end------" << endl;

    // 反向迭代器 rbegin() rend()
    cout << "------using reverse_iterator to travelsal list begin------" << endl;
    //  std::list::const_reverse_iterator 使用这个也可以
    std::list<int32>::reverse_iterator riter = testList.rbegin();

    for(;riter != testList.rend();++ riter)
    {
        cout << *riter << " ";
    }
    cout << endl;
    cout << "------using reverse_iterator to travelsal list end------" << endl;

    cout << "empty : " << testList.empty() << endl;

    cout << "size : " << testList.size() << endl;

    // 返回第一个元素的引用
    cout << "front : " << testList.front() << endl;

    // 返回最后一个元素的引用
    cout << "back : " << testList.back() << endl;

    // 在容器尾部插入一个元素
    testList.push_back(766);
    cout << "back : " << testList.back() << endl;
    // 删除容器尾部的一个元素 无返回值

    testList.pop_back();

    // 在容器中的指定位置插入元素。该函数和insert()功能相同,但效率更高
    testList.emplace(testList.end(),800);
    cout << "back : " << testList.back() << endl;

    // insert() 在容器中的指定位置插入元素
    testList.insert(testList.begin(),900);
    cout << "front : " << testList.front() << endl;

    // 删除指定区间的元素 返回指向下一个元素的迭代器
    iter = testList.erase(testList.begin());
    cout << *iter << endl;
}

  1. list 遍历删除元素
int32 AdapterTest::listRemoveTest()
{
    std::list<int32> testList;

    for(int i = 0;i<100;++i){
        testList.push_back(i);
    }

    std::list<int32>::const_iterator iter = testList.begin();
    for (;iter != testList.end();++iter)
    {
        cout << *iter << " ";
    }
    cout << endl;

    for(iter = testList.begin();iter != testList.end();)
    {
        if((*iter & 1) == 0)
        {
            // 删除 iter 指向的元素 并返回指向下一个元素的迭代器
            iter = testList.erase(iter);
        } else {
            ++iter;
        }
    }

    cout << "------------------------------" << endl;
    iter = testList.begin();
    for (;iter != testList.end();++iter)
    {
        cout << *iter << " ";
    }
    cout << endl;
}



3   关联式容器


  3.1 pair类模板

    C++ STL 标准库提供了 pair 类模板,其专门用来将 2 个普通元素 first 和 second(可以是 C++ 基本数据类型、结构体、类自定的类型)创建成一个新元素。通过其构成的元素格式不难看出,使用 pair 类模板来创建“键值对”形式的元素,再合适不过。
pair 类模板定义在头文件中,所以在使用该类模板之前,需引入此头文件

	#include 
	using namespace std;

pair对象的创建

int32 RelConTest::pairCreTest()
{
    // 默认构造函数,即创建空的pair对象
    strIntPair pair1;

    // 直接使用两个元素初始化对象
    strIntPair pair2("Mathematics",78);

    // 调用拷贝构造函数
    strIntPair pair3(pair2);

    // 调用移动构造函数
    strIntPair pair4(make_pair("Electric",98));

    // 创建pair 手动为 pair赋值
    strIntPair pair5;
    pair5.first = "Java";
    pair5.second = 90;

    cout << "first : " << pair2.first << ",second : " <<  pair2.second << endl;
}

头文件中除了提供创建 pair 对象的方法之外,还为 pair 对象重载了 <、<=、>、>=、==、!= 这 6 的运算符,其运算规则是:对于进行比较的 2 个 pair 对象,先比较 pair.first 元素的大小,如果相等则继续比较 pair.second 元素的大小


  3.2 关联容器概述及分类

    关联式容器在存储元素值的同时,还会为各元素额外再配备一个值(又称为"键",其本质也是一个 C++ 基础数据类型或自定义类型的元素),它的功能是在使用关联式容器的过程中,如果已知目标元素的键的值,则直接通过该键就可以找到目标元素,而无需再通过遍历整个容器的方式。

关联式容器可以快速查找、读取或者删除所存储的元素,同时该类型容器插入元素的效率也比序列式容器高。

关联式容器所具备的这些特性,归咎于 STL 标准库在实现该类型容器时,底层选用了 「红黑树」这种数据结构来组织和存储各个键值对

C++ STL关联式容器种类

C++专题--标准模板库STL_第2张图片

  3.3 map 容器

  作为关联式容器的一种,map 容器存储的都是 pair 对象,也就是用 pair 类模板创建的键值对。其中,各个键值对的键和值可以是任意数据类型,包括 C++ 基本数据类型(intdouble 等)、使用结构体或类自定义的类型。

  使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。换句话说,map 容器中存储的各个键值对不仅键的值独一无二,键的类型也会用 const 修饰,这意味着只要键值对被存储到 map 容器中,其键的值将不能再做任何修改

map 容器存储的都是 pair 类型的键值对元素,更确切的说,该容器存储的都是 pair 类型(其中 KT 分别表示键和值的数据类型)的键值对元素

map 容器定义在 头文件中,并位于 std 命名空间中。因此,如果想使用 map 容器,代码中应包含如下语句:

#include 
// 没有引入std空间
std::map<int,int> mapTest;
using namespace std;
map<int,int> mapTest;

map类模板定义如下 :

template <typename _Key, // 指定key的类型
		  typename _Tp, // 指定value的类型
		  typename _Compare = std::less<_Key>,//指定比较器的类型
          typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
           class map{
}

C++中创建map的方法

源文件代码

int32 RelConTest::mapCreTest()
{
    //1 通过调用map容器类的默认构造函数,可以创建出一个空的map容器
    std::map<std::string,int32> mapTest;

    //2 在创建map的同时进行初始化
    std::map<std::string,int32> mapInit{{"C++",99},{"algorithm",90}};

    //3 map容器中存储的键值对,其本质都是pair类模板创建的pair对象
    // 上面的代码使用pair改写
    std::map<std::string,int32> mapPair{std::make_pair("C++",99),std::make_pair("algorithm",90)};

    //4 利用先前已创建好的map容器,再创建一个新的map容器
    std::map<std::string,int32> mapNew(mapPair);

    //5 在创建map的同时 指定排序规则
    std::map<int32,std::string,std::less<int32>> mapSort;
    return 0;
}

头文件代码

class Item;
struct ItemPriority;
typedef std::pair<string,int32> strIntPair;
typedef std::map<int32,Item,std::greater<int32>> ItemMap;

class RelConTest
{
public:
    RelConTest();
    int32 pairCreTest();
    int32 mapFuncTest();
};

class Item
{
public:
    Item() {}
    Item(int32 iItemId,int32 iPriority)
        : m_iItemId(iItemId),
          m_iPriority(iPriority){}
    friend std::ostream& operator <<(std::ostream& os,const Item& item);
public:
    int32 m_iItemId;
    int32 m_iPriority;
};

// 函数对象类
struct ItemPriority
{
    ItemPriority();
    // 重载 ()
    bool operator()(const Item& l,const Item& r)
    {
        if(l.m_iPriority != r.m_iPriority)
        {
            return l.m_iPriority < r.m_iPriority;
        }
        return l.m_iItemId < r.m_iItemId;
    }
};

C++ map容器包含的成员方法

int32 RelConTest::mapFuncTest()
{
    ItemMap itemMap;

    itemMap.insert(make_pair(10001,Item(10001,10)));
    itemMap.insert(make_pair(10007,Item(10007,10)));
    itemMap.insert(make_pair(10002,Item(10007,10)));
    itemMap.insert(make_pair(10835,Item(10835,10)));
    itemMap.insert(make_pair(17331,Item(17331,10)));
    itemMap.insert(make_pair(18023,Item(18023,10)));

    // map的遍历
    // begin() end()
    std::map<int32,Item>::iterator iter =  itemMap.begin();
    for(;iter != itemMap.end();++iter)
    {
        cout << "[" << iter->first << "] : " <<  iter->second << endl;
    }
    /**
      *
      * 在map容器中查找键为key的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,
      * 则返回和end()方法一样的迭代器。另外,如果map容器用const 限定,则该方法返回的是const类型的双向迭代器
      *
      */
     std::map<int32,Item>::iterator it =  itemMap.find(10001);
     if (it != itemMap.end())
     {
         cout << "find it" << endl;
     }
     else
     {
         cout << "not find  please check it" << endl;
     }

     /**
       * lower_bound()
       * 返回一个指向当前map容器中第一个大于或等于
       * key的键值对的双向迭代器。如果map容器用const限定,
       * 则该方法返回的是const类型的双向迭代器
       *
       */
      std::map<int32,Item>::iterator  lower_bound_it = itemMap.lower_bound(10007);
      if (lower_bound_it != itemMap.end())
      {
          cout << "upper_bound : " << (lower_bound_it->first) << endl;
      }
      else
      {
          cout << "not find it "<< endl;
      }

      /**
        *
        *  返回一个指向 大于给定key的迭代器 如果找不到返回end()
        */
      std::map<int32,Item>::iterator upper_bound_it =  itemMap.upper_bound(10002);
      cout << (upper_bound_it != itemMap.end()) << endl;

      /*
       *   该方法返回一个pair对象(包含 2 个双向迭代器),其中pair.first和lower_bound()方法的返回值等价,
       *   pair.second和upper_bound()方法的返回值等价。
       *   也就是说,该方法将返回一个范围,该范围中包含的键为key的键值对(map容器键值对唯一,因此该范围最多包含一个键值对)
       */
      cout << "equal_range : " << endl;
      std::pair<std::map<int32,Item>::iterator,std::map<int32,Item>::iterator>  pairResult;
      pairResult = itemMap.equal_range(10002);
      cout << pairResult.first->first << endl;
      cout << pairResult.second->first << endl;
      cout << (pairResult.first == pairResult.second) << endl;

      // 移除map指向的元素 并返回离被删除元素最近的元素的
      std::map<int32,Item>::iterator erIt =  itemMap.erase(itemMap.begin());
      cout << "erIt->first : " << erIt->first << endl;

      // 键值对的个数
      cout << "size : " << itemMap.size() << endl;
      return 0;
}


map获取键对应的值的两种方法

  1. map中重载了 []
      这意味着,类似于借助数组下标可以直接访问数组中元素,通过指定的键,我们可以轻松获取 map 容器中该键对应的值

  2.   使用at()成员函数
    除了借助 [ ] 运算符获取 map 容器中指定键对应的值,还可以使用 at() 成员方法。和前一种方法相比,at() 成员方法也需要根据指定的键,才能从容器中找到该键对应的值;不同之处在于,如果在当前容器中查找失败,该方法不会向容器中添加新的键值对,而是直接抛出 out_of_range 异常。

C++ 11 map insert 的四种用法

  1. 无需指定插入位置,直接将键值对添加到 map 容器中
/**
 1、引用传递一个键值对val表示一个键值对 该方法会返回一个pair对象,其中pair.first表示一个迭代器,pair.second为一个bool类型变量:
	1) 如果成功插入val则该迭代器指向新插入的 val,bool值为true;
	2) 如果插入val失败,则表明当前map容器中存有和val的键相同的键值对(用p表示),此时返回的迭代器指向p,bool值为false
*/
pair<iterator,bool> insert (const value_type& val);
//2、以右值引用的方式传递键值对
template <class P>
    pair<iterator,bool> insert (P&& val);
  1. 除此之外,insert() 方法还支持向map容器的指定位置插入新键值对,该方法的语法格式如下
//以普通引用的方式传递 val 参数
iterator insert (const_iterator position, const value_type& val);
//以右值引用的方式传递 val 键值对参数
template <class P> 
iterator insert (const_iterator position, P&& val);


  3.4 multimap 容器

4   无序关联容器

5   容器适配器

  5.1 STL容器适配器的种类


公共的头文件部分commondef.h

#ifndef COMMONDEF_H
#define COMMONDEF_H
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  // pair 类模板定义在头文件中
#include 
#include 
#include 
#include 
#include 
#include 

#define TABLE_NUM 14
#define AISTD std::
#define TABLE_NAME_LEN 128
const char PATH_SEPARATOR = '/';
#define  DATE_CHANGE_NUM 1000000LL
#define BUFFER_SIZE 1024
#define LINE cout << "********************************************************" << endl;
#define LINEFEED cout << endl;
#define FUNCTION_BEGIN  \
    cout << __FILE__ << ":" <<  __LINE__ << ":" << __FUNCTION__ << " : begin >>> " << endl;
#define FUNCTION_END \
    cout << __FILE__ << ":" <<  __LINE__ << ":" << __FUNCTION__ << " : end >>> " << endl;

typedef std::string aistring;
typedef int int32;
typedef long long int64;
typedef short int int16;
using namespace std;

#endif // COMMONDEF_H

简单的理解容器适配器,其就是将不适用的序列式容器(包括 vector、deque 和 list)变得适用。容器适配器的底层实现和模板 A、B 的关系是完全相同的,即通过封装某个序列式容器,并重新组合该容器中包含的成员函数,使其满足某些特定场景的需要。

STL 提供了 3 种容器适配器,分别为 stack 栈适配器、queue 队列适配器以及 priority_queue 优先权队列适配器。其中,各适配器所使用的默认基础容器以及可供用户选择的基础容器

容器适配器 基础容器筛选条件 默认使用的基础容器
stack 基础容器需包含以下成员函数: empty()   size() back() push_back() pop_back() 满足条件的基础容器有 vector、deque、list deque
queue 基础容器需包含以下成员函数: empty() size() front() back() push_back() pop_front() 满足条件的基础容器有 deque、list deque
priority_queue 基础容器需包含以下成员函数: empty() size() front() push_back() pop_back() 满足条件的基础容器有vector、deque vector

  5.2 stack容器适配器

stack容器适配器的创建

/**
 *
 *  stack使用的基础容器有 :
 *      - vector
 *      - list
 *      - deque (默认)
 * @brief AdapterTest::stackTest
 * @return
 *
 */
int32 AdapterTest::stackTest()
{
   // 创建一个不包含任何元素的 stack 适配器,并采用默认的 deque 基础容器
   stack<int32> s1;
   // 使用基础容器list来初始化 stack 适配器,只要该容器的类型和 stack 底层使用的基础容器类型相同即可
   stack<int32,list<int32>> s2;

   for(int i = 0;i < 10;++i)
   {
     // 压栈
     s1.push(i);
   }

   if(!s1.empty())
   {
      // 栈顶元素
      cout << "top : " << s1.top() << endl;
      // 栈中元素的个数
      cout << "size : " << s1.size() << endl;
   }

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

  5.3 queue容器适配器

stack 栈容器适配器不同,queue 容器适配器有 2 个开口,其中一个开口专门用来输入数据,另一个专门用来输出数据

最先进入 queue 的元素,也可以最先从 queue 中出来,即用此容器适配器存储数据具有“先进先出(简称 “FIFO” )”的特点,因此 queue 又称为队列适配器。

queue适配器的创建及常用成员函数

int32 AdapterTest::queueTest()
{
    // 创建一个空的 queue 容器适配器,其底层使用的基础容器选择默认的 deque 容器
    queue<int32> q1;
    // 创建了一个使用 list 容器作为基础容器的空 queue 容器适配器
    queue<int32,list<int32>> q2;

    // 用基础容器来初始化 queue 容器适配器,只要该容器类型和 queue 底层使用的基础容器类型相同即可
    std::deque<int32> values{1,6,7,10,12};
    queue<int32> q3(values);

    /**
      * queue支持的成员函数
      * - empty()
      * - front() 返回queue中第一个元素的引用.如果queue是常量,就返回一个常引用;如果queue为空,返回值是未定义的
      * - back()  返回queue中最后一个元素的引用.如果queue是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的
      * - push()  在queue的尾部添加一个元素的副本.这是通过调用底层容器的成员函数push_back()来完成的.
      * - pop()   删除 queue 中的第一个元素
      */
    cout << "empty : " << q3.empty() << endl;

    int32& val = q3.front();
    cout << "val : " << val << endl;

    val = q3.back();
    cout << "val : " << val << endl;

    q3.push(20);
    val = q3.back();
    cout << "val : " << val << endl;

    while (! q3.empty())
    {
        cout << "front ele " << q3.front() << endl;
        q3.pop();
    }
    return 0;
}

  5.3 priority_queue容器适配器

priority_queue 容器适配器模拟的也是队列这种存储结构,即使用此容器适配器存储元素只能“从一端进(称为队尾),从另一端出(称为队头)”,且每次只能访问 priority_queue 中位于队头的元素

但是,priority_queue 容器适配器中元素的存和取,遵循的并不是 “First in,First out”(先入先出)原则,而是"First in,Largest out"原则。直白的翻译,指的就是先进队列的元素并不一定先出队列,而是优先级最大的元素最先出队列

priority_queue 容器适配器为了保证每次从队头移除的都是当前优先级最高的元素,每当有新元素进入,它都会根据既定的排序规则找到优先级最高的元素,并将其移动到队列的队头;同样,当 priority_queue 从队头移除出一个元素之后,它也会再找到当前优先级最高的元素,并将其移动到队头

基于 priority_queue 的这种特性,因此该容器适配器有被称为优先级队列

template <typename T,
        typename Container=std::vector<T>,
        typename Compare=std::less<T> >
class priority_queue{
    //......
}

typename T:指定存储元素的具体类型;
typename Container:指定 priority_queue底层使用的基础容器,默认使用 vector 容器
typename Compare:指定容器中评定元素优先级所遵循的排序规则,默认使用std::less按照元素值从大到小进行排序,还可以使用std::greater按照元素值从小到大排序,但更多情况下是使用自定义的排序规则

  1.   priority_queue创建的几种方式

由于 priority_queue 容器适配器模板位于头文件中,并定义在 std 命名空间里,因此在试图创建该类型容器之前,程序中需包含以下 2 行代码:

#include 
using namespace std;
/**
 * priority_queue 使用的基础容器
 *  - vector (默认)
 *  - dequeu
 * @brief AdapterTest::priorityQueueTest
 * @return
 */
int32 AdapterTest::priorityQueueTest()
{
    // 创建一个空的priority_queue容器适配器,底层采用默认的vector容器,排序方式也采用默认的std::less方法
    std::priority_queue<int32> pq;

    // 可以使用普通数组或其它容器中指定范围内的数据,对priority_queue容器适配器进行初始化:
    int values[]{20,30,90,17,23,45};//使用普通数组
    std::priority_queue<int32> q1(values,values+5);

    std::vector<int32> v1{100,78,32,87,49,23};//使用向量
    std::priority_queue<int32> q2(v1.begin(),v1.end());

    // 指定优先级队列的底层排序规则 小的排在前面
    std::priority_queue<int32,std::deque<int32>,std::greater<int32>> q3(v1.begin(),v1.end());
    /**
      * priority_queue提供的成员函数
      *     - empty()   如果 priority_queue 为空的话,返回 true;反之,返回 false
      *     - size()    返回 priority_queue 中存储元素的个数
      *     - top()     返回 priority_queue 中第一个元素的引用形式
      *     - push(const T& obj)
      *       根据既定的排序规则,将元素 obj 的副本存储到 priority_queue 中适当的位置。
      *     - push(T&& obj)
      *       根据既定的排序规则,将元素 obj 移动存储到 priority_queue 中适当的位置
      *     - pop()
      *       移除 priority_queue 容器适配器中第一个元素
      *     - swap(priority_queue& other)
      *         将两个priority_queue容器适配器中的元素进行互换,
      *         需要注意的是,进行互换的2个priority_queue容器
      *         适配器中存储的元素类型以及底层采用的基础容器类型
      *         都必须相同
      */
    cout << "size : " << q3.size() << endl;
    while (!q3.empty())
    {
        cout << q3.top() << " ";
        q3.pop();
    }
    cout << endl;

    cout << "---------------------q2---------------------" << endl;
    cout << "size : " << q2.size() << endl;
    while (!q2.empty())
    {
        cout << q2.top() << " ";
        q2.pop();
    }
    cout << endl;

    int32 nums[]{10,34,56,89,78,20};
    std::priority_queue<int32> q4(nums,nums+5);
    q2.swap(q4);
    cout << "---------------------q2---------------------" << endl;
    while (!q2.empty())
    {
        cout << q2.top() << " ";
        q2.pop();
    }
    cout << endl;
    return 0;
}

  1. priority_queue容器适配器实现自定义排序
      无论 priority_queue 中存储的是基础数据类型(int、double 等),还是 string 类对象或者自定义的类对象,都可以使用函数对象的方式自定义排序规则
template <typename T>
// 函数对象类
class IntegerComp
{
public:
    IntegerComp() {}
    //重载()
    bool operator()(const T& l,const T& r)
    {
        return l < r;
    }
};

上面的 class关键字也可以使用 struct关键字替代

// 函数对象类
template <typename T>
struct IntegerComp
{
public:
    IntegerComp() {}
    //重载()
    bool operator()(const T& l,const T& r)
    {
        return l > r;
    }
};

自定义优先级 coding

/**
 *
 *
 * 优先级队列自定义排序规则
 * @brief AdapterTest::priorityQueueCustomerTest
 * @return
 */
int32 AdapterTest::priorityQueueCustomerTest()
{
   int values[]{10,6,23,78,87,90};
   // l < r 时升序
   // l > r 时降序
   std::priority_queue<int32,std::deque<int32>,IntegerComp<int32>> prioQueue(values,values + 6);

   cout << "------------prioQueue: ------------" << endl;

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

你可能感兴趣的:(STL,c++,开发语言)