C语言中有数组,有各种内置类型的数组,例如int、double、char。但是C++是一种面向对象的思想,编写C++代码时有许多的类(自定义类型)。需要像内置类型一样把他们像数组一样,C语言是不支持的。所以c++引入了容器的概念。
vector
vector介绍
- vector是表示可变大小数组的序列容器。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
- 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
vector的使用
构造函数
vector() //无参构造
vector(size_type n, const value_type& val = value_type()) //初始化n个val值
vector (const vector& x); //拷贝构造
vector (InputIterator first, InputIterator last); //使用迭代器
增删查改接口
//尾插
void push_back (const value_type& val);
//尾删
void pop_back();
//插入
iterator insert (iterator position, const value_type& val);//在position插入val
void insert (iterator position, size_type n, const value_type& val);//在position位置插入n个val值
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);//使用迭代器区间在position位置插入若干个值
iterator erase (iterator position);//删除position位置的值
//和x进行值交换
void swap (vector& x);
void reserve (size_type n); //为vector预开辟n个空间,若是n小于capacity则什么都不做
void resize (size_type n, value_type val = value_type()); //为vector预开辟空间并初始化。
迭代器的使用
begin();//获取容器的第一个位置
end();//获取容器的最后一个元素的下一个位置
rbegin();//获取容器的最后一个元素的下一个位置
rend();//获取容器的第一个位置
//正向迭代器的使用
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int>::iterator it = v1.begin();
while (it != v1.end()) {
cout << *it << " ";
it++;
}
cout << endl;
}
//输出1 2 3 4
//反向迭代器的使用
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int>::reverse_iterator rit = v1.rbegin();
while (rit != v1.rend()) {
cout << *rit << " ";
rit++;
}
cout << endl;
}
//输出4 3 2 1
vector模拟实现
#pragma once
#include <iostream>
#include <stdlib.h>
#include <assert.h>
#include <algorithm>
using namespace std;
namespace JRG {
//反向迭代器
template<class Iterator, class Ref, class Ptr>
struct reverseIterator
{
typedef reverseIterator<Iterator, Ref, Ptr> self;
Iterator it;
reverseIterator(Iterator val)
:it(val)
{}
Ref operator*() {
Iterator tem = it;
tem--;
return *tem;
}
Ptr operator->() {
return it;
}
self& operator++() {
--it;
return *this;
}
self operator++(int) {
self tem(it);
--it;
return tem;
}
self& operator--() {
++it;
return *this;
}
bool operator!=(const self& ls) {
return it != ls.it;
}
bool operator==(const self& ls) {
return it == ls.it;
}
};
template<class T>
class vector {
public:
//正向迭代器
typedef T* iterator;
typedef const T* const_iterator;
//使用正向迭代器定义反向迭代器
typedef reverseIterator<iterator, T&, T*> reverse_iterator;
typedef reverseIterator<iterator, const T&, const T*> const_reverse_iterator;
//构造函数
vector()
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{}
vector(int n, const T& value = T())
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
resize(n,value);
}
//使用迭代器初始化
template<class Inputlterator>
vector(Inputlterator first, Inputlterator last)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(last - first);
Inputlterator next = first;
while (next != last) {
push_back(*next);
next++;
}
}
void swap(vector<T>& v) {
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
//拷贝构造
vector(const vector<T>& v)
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
/*reserve(v.capacity());
for (size_t i = 0; i < v.size(); ++i) {
push_back(v._start[i]);
}*/
vector<T> tem(v.begin(), v.end());
swap(tem);
}
vector<T>& operator=(vector<T> v)
{
/*reserve(v.capacity());
for (size_t i = 0; i < v.size(); ++i) {
push_back(v._start[i]);
}*/
swap(v);
return *this;
}
~vector() {
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
reverse_iterator rbegin() {
return reverse_iterator(end());
}
reverse_iterator rend() {
return reverse_iterator(begin());
}
iterator begin() {
return _start;
}
iterator end() {
return _finish;
}
const_iterator begin() const{
return _start;
}
const_iterator end() const{
return _finish;
}
size_t capacity() const{
return _end_of_storage - _start;
}
size_t size() const{
return _finish - _start;
}
void resize(size_t n, T val = T()) {
if (n < size()) {
_finish = _start + n;
}
else {
if (n > capacity()) {
reserve(n);
}
while (_finish != _start + n) {
*_finish = val;
_finish++;
}
}
}
void reserve(size_t n) {
if (n > capacity()) {
size_t sz = size();
T* tem = new T[n];
if (_start) {
//memcpy(tem, _start, sizeof(T)*size()); 对于自定义类型是浅拷贝
for (size_t i = 0; i < size(); ++i) {
tem[i] = _start[i];
}
delete[] _start;
}
_start = tem;
_finish = tem + sz;
_end_of_storage = tem + n;
}
}
void push_back(const T& val) {
if (size() <= capacity()) {
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = val;
_finish++;
}
void pop_back() {
assert(!empty());
_finish--;
}
iterator insert(iterator pos, const T& val) {
assert(pos >= _start && pos <= _finish);
if (size() <= capacity()) {
size_t sz = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
//扩容之后pos指向的位置不存在,因为扩容是异地扩容,不是在原来的空间上扩容,所以需要给pos重新赋值,以免迭代器失效
pos = _start + sz;
}
iterator tem = _finish-1;
while (tem >= pos) {
*(tem+1) = *tem;
tem--;
}
*pos = val;
_finish++;
return pos;
}
void erase(iterator pos) {
assert(!empty());
assert(pos >= _start && pos < _finish);
while (pos<_finish-1) {
*pos = *(pos + 1);
pos++;
}
_finish--;
}
T& operator[](size_t pos) {
assert(pos >= 0 && pos < size());
return *(_start + pos);
}
const T& operator[](size_t pos) const{
assert(pos >= 0 && pos < size());
return *(_start + pos);
}
bool empty() {
return _finish == _start;
}
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
}
list
list介绍
在C语言中学习的list链表其实就是模仿STL库中的list链表,不过STL中的链表是用类封装起来了。可以看看C语言实现的链表:链接: link
- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
- list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
- 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)
list的使用
构造函数
list (size_type n, const value_type& val = value_type()) //构造n个值为val的链表
list() //空构造
list (const list& x) //拷贝构造
list (InputIterator first, InputIterator last) //使用迭代器构造
常用函数
void push_front (const value_type& val); //链表的头插
void pop_front(); //链表的头删
void push_back (const value_type& val); //链表的尾插
void pop_back(); //链表的尾删
iterator insert (iterator position, const value_type& val); //在position位置插入
iterator erase (iterator position); //删除position位置元素
void clear(); //删除链表所有值
void swap (list& x); //交换链表值
list模拟实现
namespace JRG {
//一个node节点
template<class T>
struct list_node
{
list_node<T>* _next;
list_node<T>* _prev;
T _val;
list_node(const T& x = T())
:_next(nullptr)
, _prev(nullptr)
, _val(x)
{}
};
//反向迭代器
template<class Iterator, class Ref, class Ptr>
struct reverseIterator
{
typedef reverseIterator<Iterator, Ref, Ptr> self;
Iterator it;
reverseIterator(Iterator val)
:it(val)
{}
Ref operator*() {
Iterator tem = it;
tem--;
return *tem;
}
Iterator operator->() {
Iterator tem = it;
tem--;
return tem;
}
self& operator++() {
--it;
return *this;
}
self operator++(int) {
self tem(it);
--it;
return tem;
}
self& operator--() {
++it;
return *this;
}
bool operator!=(const self& ls) {
return it != ls.it;
}
bool operator==(const self& ls) {
return it == ls.it;
}
};
//迭代器
template<class T,class Ref,class Ptr>
struct list_iterator {
typedef list_node<T> node;
typedef list_iterator<T,Ref,Ptr> self;
node* _node;
list_iterator(node* n)
:_node(n)
{}
Ref operator*() {
return _node->_val;
}
Ptr operator->() {
return &_node->_val;
}
self& operator++() {
_node = _node->_next;
return *this;
}
self operator++(int) {
self tem(_node);
_node = _node->_next;
return tem;
}
self& operator--() {
_node = _node->_prev;
return *this;
}
self operator--(int) {
self tem(_node);
_node = _node->_prev;
return tem;
}
bool operator!=(const self& ls) {
return _node != ls._node;
}
bool operator==(const self& ls) {
return _node == ls._node;
}
};
template<class T>
class list
{
typedef list_node<T> node;
public:
//迭代器
typedef list_iterator<T, T&,T*> iterator;
//const迭代器
typedef list_iterator<T, const T&,const T*> const_iterator;
//使用正向迭代器构建反向迭代器
typedef reverseIterator<iterator, T&, T*> reverse_iterator;
typedef reverseIterator<iterator, const T&, const T*> const_reverse_iterator;
reverse_iterator rbegin() {
return reverse_iterator(end());
}
reverse_iterator rend() {
return reverse_iterator(begin());
}
iterator begin() {
return iterator(head->_next);
}
iterator end() {
return iterator(head);
}
const_iterator begin()const {
return const_iterator(head->_next);
}
const_iterator end()const {
return const_iterator(head);
}
void EmptyInit() {
head = new node;
head->_next = head;
head->_prev = head;
}
list()
{
/*head = new node;
head->_next = head;
head->_prev = head;*/
EmptyInit();
}
template <class Iterator>
list(Iterator first, Iterator last) {
EmptyInit();
while (first != last) {
push_back(*first);
first++;
}
}
void swap(list<T>& ls) {
std::swap(head, ls.head);
}
list(const list<T>& ls) {
list<T> tem(ls.begin(), ls.end());
swap(tem);
}
list<T>& operator=(list<T> ls) {
swap(ls);
return *this;
}
//删除除头节点以外的所有节点
void clear() {
/*node* next = head->_next;*/
iterator it = begin();
while (it != end()) {
/*node* tem = next;
next = next->_next;
delete tem;*/
erase(it++);
}
}
~list() {
clear();
delete head;
head = nullptr;
}
//尾插
void push_back(const T& x) {
/*node* newnode = new node(x);
node* prev = head->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = head;
head->_prev = newnode;*/
insert(end(), x);
}
//头插
void push_front(const T& x) {
insert(begin(), x);
}
//尾删
void pop_back() {
erase(--end());
}
//头删
void pop_front() {
erase(begin());
}
//pos节点前插入节点
void insert(iterator pos, const T& x) {
node* prev = pos._node->_prev;
node* next = pos._node;
node* newnode = new node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = next;
next->_prev = newnode;
}
//删除pos节点
void erase(iterator pos) {
assert(pos != end());
node* prev = pos._node->_prev;
node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
}
private:
node* head;
};
}
list和vector的区别
区别 | vector | list |
---|---|---|
底层结构 | 动态顺序表,一段连续的空间 | 带头节点的双向循环链表 |
随机访问 | 支持随机访问,访问某个元素效率高 | 不支持随机访问, |
插入和删除 | 随机插入和删除效率低下,扩容效率低 | 随机插入和删除效率高,扩容代价低 |
空间利用率 | 底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高 | 底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低 |
迭代器 | 原生态指针 | 对原生态指针进行封装 |
迭代器失效 | 在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效 | 插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响 |
使用场景 | 需要高效存储,支持随机访问,不关心插入删除效率 | 大量插入和删除操作,不关心随机访问 |
这就是vector和list的详解,希望对您有所帮助。