序列容器


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. kubernetes资源预留---转发

    下面内容还处于测试阶段,生产上是否能保证集群稳定暂时还不清楚.

  2. spring cloud (五) 熔断面板 聚合熔断 turbine

    改造feign 项目 1 启动类添加注解  @EnableHystrix 2 添加如下配置文件 #ji eureka.instance.metadata-map.cluster=MAIN#actuat ...

  3. HDU1395 2^x mod n = 1——积与余数的性质

    对于数论的学习比较的碎片化,所以开了一篇随笔来记录一下学习中遇到的一些坑,主要通过题目来讲解 本题围绕:积与余数 HDU1395 2^x mod n = 1 题目描述 输入一个数n,如果存在2的x次方 ...

  4. [Reprint] Difference Between Job, Work, And Career

    https://www.espressoenglish.net/difference-between-job-work-and-career/ A lot of English learners co ...

  5. dijkstra,belllman-ford,spfa最短路算法

    参考博客 时间复杂度对比: Dijkstra:  O(n2) Dijkstra + 优先队列(堆优化):  O(E+V∗logV) SPFA:  O(k∗E) ,k为每个节点进入队列的次数,一般小于等 ...

  6. 《BUG创造队》第八次团队作业:Alpha冲刺

    项目 内容 这个作业属于哪个课程 2016级软件工程 这个作业的要求在哪里 实验十二 团队作业8:软件测试与ALPHA冲刺 团队名称 BUG创造队 作业学习目标 (1)掌握软件测试基础技术.(2)学习 ...

  7. 格式化字符串——初级% 和format

    print '{a},{b}'.format(a='hello',b='word') st='a %s %s x y z' st1=('b','c') print st%st1 print '%s % ...

  8. po模式

    一条测试用例可能需要多个步骤操作元素,将每一个步骤单独封装成一个方法,在执行测试用例时调用封装好的方法进行操作.PO模式可以把一个页面分为三个层级,对象库层.操作层.业务层. 对象库层:封装定位元素的 ...

  9. 非旋转 treap

    其实之前学过一次非旋转 treap,但是全忘光了,今天复习一下. 洛谷 P3369 [模板]普通平衡树 code: #include <bits/stdc++.h> #define N 1 ...

  10. 过滤器的API

    1.API a.生命周期(和servletcontext相似): (1)创建:服务器启动的时候创建(执行init方法). (2)销毁:服务器关闭的时候销毁(执行destory方法). b.filter ...