STL简单序列式容器的使用(vector、list、stack、queue)

更多知识点这里C++学习 - 目录索引

1:STL的起源

为了让大量程序员不再重复前人所做好的工作,力求提高代码的复用性、独立性、弹性、相互合作性,
所以C++社群里诞生了STL,STL所实现的是依据泛型思维搭建的一个概念结构,这个以抽象概念为主体而非实际类为主体的结构,形成了一个严谨的接口标准。
在这个接口下,任何组件都有最大的独立性,并以迭代器(iterator)粘合起来,或者以配接器(adapter)互相配接,或者以所谓的仿函数(function)动态选择某种策略。

2:STL历史

  • STL系由Alexander Stepanov创造于1979年前后,这也正是创造C++的年代。
  • Alexander在AT&T实验室以及惠普公司的实验室分别实验了多种架构和计算公式,先以C完成,而后再以C++完成。
  • 在1994年夏天在滑铁卢会议开幕前,Alexander完成正式提案,并以压倒性多数一举让这个巨大的计划成为C++的标准规格的一部分。

3:vector容器的使用

3.1:创建容器

vector() 创建一个空vector
vector(int nSize) 创建一个vector,元素个数为nSize
vector(int nSize,const t& t) 创建一个vector,元素个数为nSize,且值均为t
vector(begin,end) 复制[begin,end)区间内另一个数组的元素到vector中

3.2:定义二维数组

eg1:
void test()
{
    vector<vector<int>> v;
    vector<int> a;
    a.push_back(1);
    a.push_back(2);
    vector<int> b;
    b.push_back(3);
    b.push_back(4);

    v.push_back(a);//将一个vector插入到另一个vector中,构成二维数值
    v.push_back(b);
}
eg2:
//定义一个5行4列的数组
void test()
{
    int i = 0;
    int j = 0;
    vector<vector<int>> array(5);//定义一个有5行的二维数组
    for (i = 0; i < array.size(); i++)
    {
        array[i].resize(4);//给每行开辟4列
    }    
}

3.3:接口的使用

3.3.1: resize()

void resize (size_type n, value_type val = value_type());

调整数组的容量,接受两个参数或者第一个参数
如果 n < v.size(),则将其元素个数减少到n个,销毁后面那些超出的元素。
如果 v.size() < n < v.capacity(),扩大size(),新增的元素值为val。
没写val时默认新增元素为0
如果 n > v.capacity(),对数组进行扩容,新增的元素值为val,
没写val时默认新增元素为0

3.3.2:reserve()

void reserve (size_type n);

扩大数组容量,如果n > v.capacity(),给数组新开辟一块容量为n的空间,把原数据拷贝至新空间;如果n<=v.capacity(),则不进行任何操作。

3.3.3:clear()

清除元素个数,即size变为0

3.3.4:assign()

void assign(size_type n,const T& x = T());

相当于v.clear()和v.resize(n,T())一起用,先将size清空,然后开辟空间n,并初始化为val

3.3.5:operator[]

reference operator[] (size_type n);
const_reference operator[] (size_type n) const;

返回数组下标为n的元素的引用,该函数有两种形式,一个为const形式,一个为非const形式,
非const形式可以通过返回值修改,const形式不能通过返回值被修改

3.3.6:insert()

(1) iterator insert (iterator position, const value_type& val);

(2) void insert (iterator position, size_type n, const value_type& val);

(3) template <class InputIterator>
    void insert (iterator position, InputIterator first, \
    InputIterator last);

(1) 在指定位置position前插入值为val的元素,返回指向这个元素的迭代器
(2) 在指定位置position前插入n个值为val的元素
(3)在指定位置position前插入区间[first, last)的所有元素

int main ()
{
  std::vector<int> myvector (3,100);
  std::vector<int>::iterator it;

  it = myvector.begin();
  it = myvector.insert ( it , 200 );

  myvector.insert (it,2,300);

  // "it" no longer valid, get a new one:
  it = myvector.begin();

  std::vector<int> anothervector (2,400);
  myvector.insert (it+2,anothervector.begin(),anothervector.end());

  int myarray [] = { 501,502,503 };
  myvector.insert (myvector.begin(), myarray, myarray+3);

  std::cout << "myvector contains:";
  for (it=myvector.begin(); it<myvector.end(); it++)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}

结果:myvector contains: 501 502 503 300 300 400 400 200 100 100 100

3.3.7:erase()

(1) iterator erase (iterator position);
(2) iterator erase (iterator first, iterator last);

(1) 删除position处的元素。迭代器position指向不变,仍然指向被删除元素的位置,而被删除元素之后的所有元素都向前移动一位,也就是该迭代器实际上是指向了原来被删除元素的下一个元素。
(2) 删除区间[first, last)内的所有元素。删除一段段元素之后,后面的元素(从last开始到vector.end())会被复制到被删除元素段开始的地方(first开始),而vector.end()也根据删除的元素个数往前移动。

注意:即使容器内容是指针对象,erase()也会清空指针对象指向的内容。

3.3.8:其它接口

    v.push_back();   //尾插

    v.size();        //元素个数

    v.begin();       //首元素地址

    v.end();         //尾元素地址

    v.capacity();    //容量大小

    v.empty();  //判空,空返回ture,非空返回false

    v.max_size();    //没有意义的接口,返回“42亿/数据类型大小”

    v.swap(v1);      //交换 v 和 v1 中的first、finish、endofstorage指针

3.4:优缺点

优点:像数组一样容易查找O(1)算法
修改直接[]使用 ,支持随机访问
vector的缓存利用率高
尾插,尾删效率高O(1),

缺点:
不易于在其他地方插入和删除O(n)算法
增容代价大

4:list容器的使用

4.1:基本接口使用

list<int> l1;

l1.push_back();//尾插

l1.pop_back();//尾删

l1.push_front();//头插

l1.pop_back();//头删

l1.sort();//排序,(底层归并排序实现)
l1.sort(less<int>());//(使用仿函数)安照升序排列
l1.sort(greater<int>());//(使用仿函数)安照降序排列

l1.unique()//去掉重复数据,但是需要数据连续在一起才会去掉,所以需要先排序数据然后再去重

l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);

l1.resize(2);//此时size变为2,里面内容只有1,2
l1.resize(5,10);//此时size变为5,内容为1 2 10 10 10
l1.resize(8);//size=8,内容 1 2 10 10 10 0 0 0 

l1.reverse()//逆序链表

l1.remove(2)//找到后就删除,找不到不删
for (int i=1; i<=5; ++i) 
list.push_back(i); // 1 2 3 4 5

list<int>::const_iterator it = list.begin();
  ++it;       // 它指向 2的位置           

list.insert(it,10);//it是插入位置的地址,不是下标                
  // 1 10 2 3 4 5

list.insert (it,2,20); // 1  10 20 20  2 3 4 5
    //此时it依然指向2的位置
--it;       
  // 此时it指向10的位置
list.insert (it,1,30); // 1  20  20 30 10 2 3 4 5

list.erase(it1);//// 1  20  20 30  2 3 4 5
 删除it指向位置
for (int i=1; i<=4; ++i)
list1.push_back(i);      // list1: 1 2 3 4

for (int i=1; i<=3; ++i)
list2.push_back(i*10);   // list2: 10 20 30

it =list1.begin();
  ++it;                 // points to 2

list.splice (it, list2); // mylist1: 1 10 20 30 2 3 4

//直接可以链接两个链表

4.2:优缺点

空间不连续 优点:
插入,删除使用方便O(1)算法
缺点:
不支持随机访问

5:stack容器的使用

stack<int> s;
stack<int> m;
s.empty()//判断栈是否为空,为空返回true,不空返回false
s.size()//返回栈中元素的个数
s.top()//取栈顶元素
s.push()//入栈
s.pop()//出栈
s.swap(m);//交换栈中的元素

用法:
int main ()
{
  std::stack<int> mystack;

  for (int i=0; i<5; ++i)
  {
     mystack.push(i);
  }

  std::cout << "Popping out elements...";
  while (!mystack.empty())
  {
     std::cout << ' ' << mystack.top();
     mystack.pop();
  }
  std::cout << '\n';

  return 0;
}

结果:Popping out elements... 4 3 2 1 0

6:queue容器的使用

queue<int> q;
queue<int> p;
q.empty()//判断队列是否为空,为空返回true,不空返回false
q.size()//返回队列中元素的个数
q.front()//返回对头元素
q.back()//返回对尾元素
q.push()//入对列
q.pop()//出队列
q.swap(p);//交换队列中的元素

6:deque框架介绍

6.1:deque简介

deque英文是:double ended queue,即双端队列,它与vector经常拿来比较。

vector是单向开口的连续线性空间,deque是一种双向开口的连续线性空间,所谓双向开口意思是可以在头部和尾部分别进行插入或者删除操作,并且它的效率远高于vecor的在头部插入或者删除操作。

6.2:deque与vector的差异

1:deque允许常数时间内对头端进行插入或者删除操作
2:deuqe没有所谓的容量概念,因为它是动态连续空间组合而成的,随时可以增加一段新的空间并链接起来,而不像vector一样,空间不足时,需要重新开辟空间,然后将旧空间内容拷到新空间,再释放旧空间

6.3:deque底层结构

deque是一段假想的连续空间,与vector不同。deque由一段一段定量的连续空间
构成,一旦有必要再deque的前端或尾端增加新空间,便配置一段定量连续空间,
串接在整个deque的头端和尾端,而deque的任意就是:在分段的定量连续空间上
维护其连续的假象,并提供随机存取的接口。避开了“重新配置、复制、释放”的轮回,代价则是复杂的迭代器结构。

6.4:deque迭代器

因为deque是分段连续空间,维护其“整体连续”假象的任务就落在其迭代器operator++和operator–上。因此deque的迭代器必须要知道分段连续空间在哪里,其次它必须能够识别出自己是否已经处于其所在某段连续空间的边缘,如果是,一旦前进或后退时就必须跳跃至下一个或上一个分段空间

这里写图片描述

6.5:dequ数据结构

deuqe除了要维护指向map的指针外,还要维护start和finish两个迭代器,分别指向第一个缓冲区的第一个元素和最后缓冲区的最后一个元素的下一个位置。此外没还必须知道map的大小,因为一旦map提供的节点不足,就必须重新配置更大的一块map

猜你喜欢

转载自blog.csdn.net/triorwy/article/details/80734849