序列容器


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++] Sequence Containers的更多相关文章

  1. C++ std::list

    std::list template < class T, class Alloc = allocator > class list; List Lists are sequence co ...

  2. C++ std::forward_list

    std::forward_list template < class T, class Alloc = allocator > class forward_list; Forward li ...

  3. C++ std::deque

    std::deque template < class T, class Alloc = allocator > class deque; Double ended queue deque ...

  4. C++ std::array

    std::array template < class T, size_t N > class array; Code Example #include <iostream> ...

  5. C++的STL

    今天,看一段代码的时候发现只一句话就做了个排序,是这样的: sort(rotateArray.begin(),rotateArray.end()); 很震惊,后来查了一下sort的用法, sort函数 ...

  6. STL---总结

    文章转自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/22/2603525.html 一.STL的六大组件 容器(Container),是一种 ...

  7. 词频统计_输入到文件_update

    /* 输入文件见337.in.txt 输出文件见338.out.txt */ #include <iostream> #include <cctype> #include &l ...

  8. STL--向量(vector)

    STL的组成 标准模板库STL关注的重点是泛型数据结构和算法,其关键组成部分是容器(containers).算法(algorithms).迭代器(iterators).函数对象(Function Ob ...

  9. STL学习一:标准模板库理论基础

    STL(Standard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称.现然主要出现在C++中,但在被引入C++之前该技术就已经存在了很长的一段时间. STL的从广 ...

随机推荐

  1. 使用Arduino开发板实现与MPU6050陀螺仪传感器连接的方法

    MPU6050陀螺仪传感器具有许多强大的功能,采用单芯片封装.它是由一个MEMS加速度计.一个MEMS陀螺仪和温度传感器组成.该模块在将模拟量转换为数字量时非常准确,因为每个通道都有一个16位的模数转 ...

  2. centos 7 修改密码

    linux管理员忘记root密码,需要进行找回操作. 注意事项:本文基于centos7环境进行操作,由于centos的版本是有差异的,继续之前请确定好版本. 操作步骤 一.重启系统,在开机过程中,快速 ...

  3. 在windows系统和kali中通过sqlmap注入

    第1章 在windows系统中通过sqlmap注入 1.1 环境搭建 Sqlmap是目前功能最强大,使用最为广泛的注入类工具,是一个开源软件,被集成于kaliLinux, 由于sqlmap是基于Pyt ...

  4. Xms Xmx PermSize MaxPermSize的含义

    参数的含义 -vmargs -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M -vmargs 说明后面是VM的参数,所以后面的其实都是JV ...

  5. C++中优先队列的使用

    既然是队列那么先要包含头文件#include <queue> 优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的 .定义:priority_ ...

  6. angular和datatables一起使用的时候,出现TypeError: Cannot Read Property "childNodes" Of Undefined In AngularJS

    在anguglar中注入'$timeout' 然后: $timeout(function{},0,false);

  7. [Dart] Mixin

    Docs Mixins are a way of reusing a class’s code in multiple class hierarchies. void main() { Animal ...

  8. THUWC2020 划船记

    PS:THUWC2020在2019年 Day 1 考场外的太懒了不写了. 三题题目大意: T1: T2: 给定一个\(n(\leq 10^5)\)个结点的有向图,每条边有个limit,表示经过这条边l ...

  9. [golang]Go常见问题:# command-line-arguments: ***: undefined: ***

    今天遇见一个很蛋疼的问题,不知道是不是我配置的问题,IDE直接run就报错. 问题描述 在开发代码过程中,经常会因为逻辑处理而对代码进行分类,放进不同的文件里面:像这样,同一个包下的两个文件,点击id ...

  10. JavaScript对象及面向对象

    1.创建对象(1)自定义对象   语法:var 对象名称=new Object();(2)内置对象   String(字符串)对象.   Date(对象)对象   Array(数组)对象   Boll ...