序列容器


C++ Standard Library

一、特点

并没怎么用面向对象的特性。

For efficiency reasons, STL is not object-oriented:

    • Makes little use of inheritance, and
    • Makes no use of virtual functions

二、分类

STL是c++的精华,开始前先在此提一下。

有助于总体理解的链接:[C++ STL] 各容器简单介绍

The Standard Template Library (STL) is a part of the C++ standard library, Some others are:

分类一

    • The C standard library
    • Language support: <exception>, <ctime>, ...
    • Strings: <string>, <cstring>, ...
    • Numerics: <cmath>, <complex>, ...
    • I/O: <iostream>, <fstream>, <cstdio>, ...
    • Exception classes
    • Smart pointers
    • Classes for internationalisation support

分类二

    • Containers: vector, list, stack, ...
    • Algorithms: find, sort, copy, ...
    • Iterators are the glue between the two!

Containers 蓝图

一、c++11新特性

四大板块

In reality, not all operations are supported in all containers,

More details: http://www.cplusplus.com/reference/stl/

适配器 Adaptors

适配器?Adaptors和序列容器的关系是什么?

本质上,适配器是使一事物的行为类似于另一类事物的行为的一种机制。

容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。

例如,stack适配器可使任何一种顺序容器以栈的方式工作。

二、效率对比

需要逐个实践下,为下一节做准备

容器解密


Vector

一、插入操作

resize()

Ref: 关于vector的resize()的理解

resize()的作用是改变vector中元素的数目。

如果n比当前的vector元素数目要小,vector的容量要缩减到resize的第一个参数大小,既n。并移除那些超出n的元素同时销毁他们。

如果n比当前vector元素数目要大,在vector的末尾扩展需要的元素数目,如果第二个参数val指定了,扩展的新元素初始化为val的副本,否则按类型默认初始化。

注意:如果n大于当前的vector的容量(是容量,并非vector的size),将会引起自动内存分配。所以现有的pointer, references, iterators将会失效。

综合操作示范

int func_vector_insert(void)
{
cout << "== vector insert() show ==" << endl; std::vector<int> myvector (,);
std::vector<int>::iterator it; it = myvector.begin();
it = myvector.insert ( it , ); //某位置插入一个数字
for (auto p = myvector.begin(); p != myvector.end(); p++)
{
std::cout << (*p) << ", ";
}
std::cout << std::endl; myvector.insert (it,,);  //某位置插入若干数字 for (auto p = myvector.begin(); p != myvector.end(); p++)
{
std::cout << (*p) << ", ";
}
std::cout << std::endl; // "it" no longer valid, get a new one:
it = myvector.begin(); std::vector<int> anothervector (,);
myvector.insert (it+, anothervector.begin(), anothervector.end());  //某位置插入一段动态同类的数字 for (auto p = myvector.begin(); p != myvector.end(); p++)
{
std::cout << (*p) << ", ";
}
std::cout << std::endl; int myarray [] = { ,, };
myvector.insert (myvector.begin(), myarray, myarray+);  //某位置插入一段静态同类的数字 std::cout << "myvector contains:";
for (it=myvector.begin(); it<myvector.end(); it++)
std::cout << ' ' << *it;
std::cout << '\n'; return ;
} //****************************************************************************** void func_vector(void)
{
// vector<int> vec= new vector();
vector<int> vec= {, , , , };
cout << vec.at() << endl;
cout << "size = " << vec.size() << endl;
cout << "capacity = " << vec.capacity() << endl; cout << "resize(10)\n";
vec.resize(); /* Jeff --> I don't like unsigned here. */
// for (unsigned int i = 0; i < vec.size(); i++)
// {
// cout << vec.at(i) << "\n";
// } for (auto p = vec.begin(); p != vec.end(); p++)
{
// Jeff --> endl == \n + flush
cout << (*p) << ' ';
}
cout << endl; cout << "size = " << vec.size() << endl;
cout << "capacity = " << vec.capacity() << endl; cout << "vec.clear()" << endl;
vec.clear(); cout << "size = " << vec.size() << endl;
cout << "capacity = " << vec.capacity() << endl; //-----------------------------------------------------------------------------
for (int i = ; i <= ; i++)
{
vec.push_back(i);
} // Jeff --> vector<int>::iterator iter = vec.begin();
// 可使用通用的iterator方式,毕竟list不能直接l.begin()+1这么用!
cout << "erase(0-2)" << endl;
vec.erase(vec.begin(), vec.begin() + ); for (auto p = vec.begin(); p != vec.end(); p++)
{
// Jeff --> endl == \n + flush
cout << (*p) << ' ';
}
cout << endl; cout << "size = " << vec.size() << endl;
cout << "capacity = " << vec.capacity() << endl; //----------------------------------------------------------------------------- func_vector_insert(); //----------------------------------------------------------------------------- cout << "vec.front() = " << vec.front() << endl; cout << "push_back(111)" << endl;
vec.push_back(); for (auto p = vec.begin(); p != vec.end(); p++)
{
// Jeff --> endl == \n + flush
cout << (*p) << ' ';
}
cout << endl; cout << "size = " << vec.size() << endl;
cout << "capacity = " << vec.capacity() << endl;
}

常见的修改操作示范

二、二维 vector

auto类型的好处,没以及使用的“指针”在遍历。

void func_vector2(void)
{
vector<vector<int>> vector2;
vector<int> v1 = {, , , , };
vector<int> v2 = {, , , , }; vector2.push_back(v1);
vector2.push_back(v2); for (auto it_v = vector2.begin(); it_v != vector2.end(); it_v++)
{
for (auto it_sub_v = (*it_v).begin(); it_sub_v != (*it_v).end(); it_sub_v++)
{
cout << (*it_sub_v) << ' ';
}
cout << endl;
}
}

三、vector.at() 更安全

如果你不能确信程序是否能做到不越界,C++VECTOR提供边界检查(at函数)。

因为,提供处理越界的异常的 “机会”。

void func_catch(void)
{
vector<string> words = { "Hello", "World" };
for (unsigned int i = ; i < words.size(); ++i) {
cout << words[i] << endl;
}
try {
cout << words.at() << endl;
} catch(const std::out_of_range &e) {
cout << e.what() << endl;
}
}

四、assign()

区别在于处理类时,如果有malloc就有有不同,会涉及到引用计数的问题。详见:assign、retain和copy的区别

assign是浅拷贝。retain的意思是保持引用,也就是说,如果想保持某个对象的引用,避免它被正统传人释放。

#include <iostream>
#include <vector> int main ()
{
// 三个列表
std::vector<int> first;
std::vector<int> second;
std::vector<int> third; first.assign (, );  // 分配 七个100
for (auto p = first.begin(); p != first.end(); p++)
{
std::cout << (*p) << std::endl;
} std::cout << "============================" << std::endl; std::vector<int>::iterator it;
it=first.begin()+;
second.assign (it, first.end()-); // first上的一段数据 (动态数组)
for (auto p = second.begin(); p != second.end(); p++)
{
std::cout << (*p) << std::endl;
} int myints[] = {, , };
third.assign (myints, myints+); // myints上的一段数据 (静态数组)
std::cout << "Size of first: " << int (first.size()) << '\n';
std::cout << "Size of second: " << int (second.size()) << '\n';
std::cout << "Size of third: " << int (third.size()) << '\n';
return ;
} void func_assign()
{
vector<int> v1(10);
// vector<float> v2 = v1;
vector<float> v2; v2.assign(v1.begin(), v1.end()); // 可能的优势:v1转化为了v2类型
}

五、copy()

读文件

copy() 作为输入的技巧,以及map的使用。


#include <string.h>
  #include <vector>
  #include <map>
  #include <iostream>
  #include <fstream>
  #include <iterator>

int test02(void)
{
using namespace std;
std::vector<string> v;
std::map<string, int> m; // (1) 定义该文件的输入流 in
std::ifstream in("words.txt");

std::copy(std::istream_iterator<string>(in), std::istream_iterator<string>(), std::back_inserter(v)); // The number of times it occurs in a file.
for (auto vi = v.begin(); vi != v.end(); ++vi)
    ++m[*vi]; for (auto mi = m.begin(); mi != m.end(); ++mi)
std::cout << mi->first << ": " << mi->second << std::endl; return ;
}

打印出

copy() 作为输出的技巧:拷贝到ostream。

int test03(void)
{
std::vector<int> v = {, , , , , };
std::string s("string"); std::sort(v.begin(), v.end());
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << std::endl; std::sort(s.begin(), s.end());
std::copy(s.begin(), s.end(), std::ostream_iterator<char>(std::cout, " "));
std::cout << std::endl; return ;
}

List

一、链表上的递增 advance()

在链表上的指针位移操作,弥补 ++ 功能。

void func_list(void)
{
list<int> l = {, , , , };
list<int> l2(, ); list<int>::iterator iter = l.begin();
list<int>::iterator iter2 = l.begin(); // Jeff --> 不能直接++, 就设计了这个函数
advance (iter2, );
l.erase(iter, iter2); for(auto p = l.begin(); p != l.end(); p++)
{
cout << (*p) << ' ';
}
cout << endl;

/////////////////////////////////////////////////////////
// Jeff --> notice: 在erase之后,iter will be a 野指针!
// l2.insert(l2.begin(), iter, iter2);
/////////////////////////////////////////////////////////
l2.insert(l2.begin(), l.begin(), l.end()); for(auto p = l2.begin(); p != l2.end(); p++)
{
cout << (*p) << ' ';
}
cout << endl;
}

二、链表上的插入 splice()

list::splice实现list拼接的功能。将源list的内容 部分或全部元素删除,拼插入到目的list。

#include <iostream>
#include <list> int main ()
{
std::list<int> mylist1, mylist2;
std::list<int>::iterator it; // set some initial values:
for (int i=; i<=; ++i)
mylist1.push_back(i); // mylist1: 1 2 3 4 for (int i=; i<=; ++i)
mylist2.push_back(i*); // mylist2: 10 20 30 it = mylist1.begin();
++it; // points to 2 --> 这里要改用 advance()
mylist1.splice (it, mylist2); // mylist1: 1 10 20 30 2 3 4
// mylist2 (empty)
// "it" still points to 2 (the 5th element)
mylist2.splice (mylist2.begin(), mylist1, it);
// mylist1: 1 10 20 30 3 4
// mylist2: 2
// "it" is now invalid.
it = mylist1.begin();
std::advance(it,); // "it" points now to 30
mylist1.splice ( mylist1.begin(), mylist1, it, mylist1.end());
// mylist1: 30 3 4 1 10 20
std::cout << "mylist1 contains:";
for (it=mylist1.begin(); it!=mylist1.end(); ++it)
std::cout << '' << *it;
std::cout << '\n'; std::cout << "mylist2 contains:";
for (it=mylist2.begin(); it!=mylist2.end(); ++it)
std::cout << '' << *it;
std::cout << '\n'; return ;
} void func_splice(void)
{
list<string> words = {"hello", "world"};
list<string> words2 = {"lovely"}; words.splice(++words.begin(), words2);
for (auto p = words.begin(); p != words.end(); p++)
{
cout << *p << endl;
}
cout << "=============================" << endl;
//-------------------------------------------------
// 单向链表
//-------------------------------------------------
forward_list<int> myfl1 = {1, 2, 3};
forward_list<int> myfl2 = {4, 5, 6}; // Jeff --> iterator's next position.
myfl1.splice_after(myfl1.begin(), myfl2); for (auto p = myfl1.begin(); p != myfl1.end(); p++)
{
cout << *p << endl;
}
}

Adaptor Class

一、必要性

在顺序容器的基础上再包装一层,形成常见的数据结构。

More details: 容器适配器

我们已有的容器(比如vectorlistdeque)支持的操作很多,比如插入,删除,迭代器访问等等。而我们希望这个容器表现出来的是栈的样子:先进后出,入栈出栈等等,此时,我们没有必要重新动手写一个新的数据结构,而是把原来的容器重新封装一下,改变它的接口,就能把它当做栈使用了。
C++中定义了三种容器适配器,它们让容器提供的接口变成了我们常用的的3种数据结构:栈(先进后出),  队列(先进先出),  优先级队列

二、实现原理

至于具体是怎么变的,我们可以先了解一个大概:

      • 默认情况下,栈和队列都是基于deque实现的,
      • 而优先级队列则是基于vector实现的。

当然,我们也可以指定自己的实现方式。但是由于数据结构的关系,我们也不能胡乱指定:

      • 栈 的特点是后进先出,所以它关联的基本容器可以是任意一种顺序容器,因为这些容器类型结构都可以提供栈的操作有求,它们都提供了push_back、pop_back和back操作。
      • 队列 的特点是先进先出,适配器要求其关联的基础容器必须提供pop_front操作,因此其不能建立在vector容器上 (内存的是固定的);
      • 优先级队列,由于它要求支持 随机访问 的功能,所以可以建立在vector或者deque上,不能建立在list上。

    

三、操作示范

栈 Stack

一些常用的方法,例如:push, pop。

void func_stack(void)
{
std::stack<int> foo, bar;
foo.push();
foo.push();
foo.push(); bar.push ();
bar.push(); // 交换了两个container的内容
foo.swap(bar); std::cout << foo.top() << std::endl;
foo.pop();
std::cout << foo.top() << std::endl;
foo.pop(); std::cout << "size of foo: " << foo.size() << '\n';
std::cout << "size of bar: " << bar.size() << '\n';
}

队列 Queue

void func_queue(void)
{
std::queue<int> que;
for (int i = ; i <= ; i++)
{
que.push(i);
} cout << que.size() << endl; while(!que.empty())
{
// print & pop 是两码事!
cout << que.front() << ' ';
que.pop();
}
cout << endl;
}

优先级队列 priority_queue

底层原理是通过 [Max/Min-heap] 来实现的。

STL中的优先队列-priorit_queue,包含在头文件”queue”中,

 - 可以使用具有默认优先级的已有数据结构;

 - 也可以再定义优先队列的时候传入自定义的优先级比较对象;

 - 或者使用自定义对象(数据结构),但是必须重载好< 操作符。

模板申明带3个参数:priority_queue<Type, Container, Functional>,其中Type 为数据类型,Container为保存数据的容器,Functional 为元素比较方式。

  Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector。

#include <string.h>
#include <vector>
#include <map>
#include <queue>
#include <list>
#include <iostream>
#include <fstream>
#include <iterator> using namespace std; void func_priority_queue(void)
{
//--------------------------------------------------------------------------
// (1) default way
//--------------------------------------------------------------------------

std
::priority_queue<int> mypq;
// this uses default way to do it.
// actually the details shows as following.
//
// std::priority_queue<int, vector<int>, greater_equal<int>> mypq; mypq.push(rand());
mypq.push(rand());
mypq.push(rand());
mypq.push(rand());
mypq.push(rand()); while(!mypq.empty())
{
cout << mypq.top() << endl;
mypq.pop();
} //--------------------------------------------------------------------------
// (2) custom way
//-------------------------------------------------------------------------- // custom type
typedef struct Node_t
{
int a;
int b;
} Node; // custom sequence container
Node *myNode = new Node[]; // custom compare method.
struct cmp
{
bool operator()(const Node &t1, const Node &t2)
{
return t1.b < t2.b;
}
}; // prepare data in this custom type.
for (int i = ; i < ; i++)
{
myNode[i].a = i;
myNode[i].b = i+;
} // init the priority queue.
std::priority_queue<Node, vector<Node>, cmp> Q(myNode, myNode+);  // <---- 之前使用的push添加数据,这里用了如此的初始化方式 // print the elements one by one in priority queue.
while(!Q.empty())
{
cout << Q.top().b << endl;
Q.pop();
}
}

四、常用操作接口

    • Stack

.empty() 堆栈为空则返回真
.pop() 移除栈顶元素
.push() 在栈顶增加元素
.size() 返回栈中元素数目
.top() 返回栈顶元素
    • Queue

.back()  返回一个引用,指向最后一个元素
.empty() 如果队列空则返回真
.front() 返回第一个元素
.pop() 删除第一个元素
.push() 在末尾加入一个元素
.size() 返回队列中元素的个数
    • Priority_queue

.empty() 如果优先队列为空,则返回真
.pop() 删除第一个元素
.push() 加入一个元素
.size() 返回优先队列中拥有的元素的个数
.top() 返回优先队列中有最高优先级的元素

End.

[c++] STL = Standard Template Library的更多相关文章

  1. C++ Standard Template Library STL(undone)

    目录 . C++标准模版库(Standard Template Library STL) . C++ STL容器 . C++ STL 顺序性容器 . C++ STL 关联式容器 . C++ STL 容 ...

  2. C++ Standard Template Library (STL) 高级容器

    更多 STL 数据结构请阅读 NOIp 数据结构专题总结(STL structure 章节) std::map Definition: template < class Key, // map: ...

  3. <Standard Template Library>标准模板库专项复习总结(二)

    4.队列 先进先出(FIFO)表 头文件:#include<queue> 变量的定义:queue<TYPE>queueName 成员函数: bool empty() 空队列返回 ...

  4. <Standard Template Library>标准模板库专项复习总结(一)

    看了看博客园的申请时间也一年多了...想想自己一年多以来一直处于各种划水状态,现在又要面临ACM的冲击... 还是要抓紧时间赶紧复习一下了- -毕竟校园新生赛还是有奖金的.. 1.栈 先进后出(LIF ...

  5. 自定义标签 与 JSTL(JSP Standard Tag Library)

    1.自定义标签 [理解]     [1]简介            > 在JSP2.0以后,在jsp页面中不建议使用脚本片段<% %>和JSP表达式<%= %>     ...

  6. JSTL的全称:JSP Standard Tag Library, jsp 标准标签库

    JSTL的全称:JSP Standard Tag Library, jsp 标准标签库 JSTL的作用     提供给Java web开发人员一个标准通过的标签函数库和EL来取代传统直接在页面上嵌入j ...

  7. JSTL(JSP Standard Tag Library ,JSP标准标签库)

    JSTL标签之核心标签   JSTL(JSP Standard Tag Library ,JSP标准标签库)是一个实现 Web应用程序中常见的通用功能的定制标记库集,这些功能包括迭代和条件判断.数据管 ...

  8. JSTL 标准标签库 (JavaServer Pages Standard Tag library, JSTL)

    JSP标准标签库(JavaServer Pages Standard Tag Library,JSTL)是一个定制标签库的集合,用来解决 像遍历Map或集合.条件测试.XML处理,甚至数据 库访问和数 ...

  9. php spl标准库简介(SPL是Standard PHP Library(PHP标准库)(直接看代码实例,特别方便)

    php spl标准库简介(SPL是Standard PHP Library(PHP标准库)(直接看代码实例,特别方便) 一.总结 直接看代码实例,特别方便易懂 thinkphp控制器利眠宁不支持(说明 ...

随机推荐

  1. js 经典正则判断 一个字符串是否包含另一个字符串

    if (!new RegExp(list_table[i].value.split("—")[0]).test(lhtj)) { }判断在lhtj中是否包含list_table[i ...

  2. listview选中没有效果

    listview选中没有效果了,设置了android:listselector也没有效果,最后发现是listview中的item布局设置了背景颜色导致,把item的背景色去掉就OK了 http://b ...

  3. ZabbixCPU温度监视-Centos

    ZabbixCPU温度监视 1, lm_sensors安装 #yum install lm_sensors … #sensors-detect 出来的选项全部选yes 重启服务器 2, lm_sens ...

  4. 负载均衡的mariadb集群搭建

    集群介绍: Galera是一个MySQL(也支持MariaDB,Percona)的同步多主集群软件,目前只支持InnoDB引擎. 主要功能: 同步复制 真正的multi-master,即所有节点可以同 ...

  5. [Xamarin] 從Xamarin中呼叫 *.jar 的 library - 呼叫篇 (转帖)

    上篇文章我們建立一個很簡單的Library : com.example.blackfactory.UtilFunc 現在我們要在Xamarin 中呼叫囉! 首先我們要先成立一個橋接的專案 JARBri ...

  6. 算法:POJ1007 DNA sorting

    这题比较简单,重点应该在如何减少循环次数. package practice; import java.io.BufferedInputStream; import java.util.Map; im ...

  7. angular ng-repeat+sortable 拖拽demo

    由于项目需求,需要使用angular 实现列表的增.删.改,并且列表支持拖拽. 看了下angular-ui 里面的sortable组件,使用起来也是非常简单,几十行代码就完成了所需功能. 我现在懒得想 ...

  8. MySQL4:存储过程和函数

    什么是存储过程 简单说,存储过程就是一条或多条SQL语句的集合,可视为批文件,但是起作用不仅限于批处理.本文主要讲解如何创建存储过程和存储函数以及变量的使用,如何调用.查看.修改.删除存储过程和存储函 ...

  9. Linux文件系统应用---系统数据备份和迁移(用户角度)

    1   前言 首先承诺:对于从Windows系统迁移过来的用户,困扰大家的  “Linux系统下是否可以把系统文件和用户文件分开到C盘和D盘中” 的问题也可以得到完满解决. 之前的文章对Linux的文 ...

  10. 一个App完成入门篇(二)-搭建主框架

    通过第一课的学习,你已经掌握了如何通过debug调试器来跟PC上的设计器联调来实时查看UI设计效果.调试代码了,接下来通过一系列的demo开发教学你将很快上手学习到如何开发一个真正的App. 要开发A ...