list:带头结点的双向链表
//list的底层数据结构:带头结点的双向循环链表
//可以正向反向遍历,有反向迭代器
//无法通过位置直接访问元素,必须从已知位置(如开始或结束)迭代到该位置
//还会消耗一些额外的内存来保持与每个元素关联的链接信息
//forward_list对象是单链表,因此它们只能向前迭代,以换取更小和更有效。
list既然是链表也就没有随机元素访问,所以没有重载[ ]运算符,链表是不能对结点进行随机访问的(区别于vector)
容量相关:
empty,size,max_size,resize
没有像vector总的capacity那些方法,因为链表的增效率是O(1),vector设置capacity是为了减少申请内存,扩容,提高效率,这一点list链表的数据结构是不必考虑的。
修改:
push_back,push_front
//不用给出迭代器位置,这是尾插头插,对应的pop_back,pop_front
iterator insert (iterator position, const value_type& val);
插入操作原本时间复杂度是O(1),因为insert不会造成大量的元素移动
但是pos要插入的位置,总头需要遍历才能获得,所以O(n),但是这样理解也不太合适,因为考虑算法的时间复杂度是针对算法本身,而不应去看参数的获取效率。
比如我要删除一个元素:
//比如要删除一个元素,在第4个位置不能
//L1.erase(L1.begin() + 4);这是做不到的,
//因为这个迭代器想象成指针,给指针+4,访问的地址不是第四个元素,
//因为这是链表,每个元素存储在不同且不相关的存储位置
auto it = L1.begin();
for (int i = 0; i < 3; i++)//必须通过遍历++,而不是it+3
it++;
L1.erase(it);
可以使用find算法库中函数
template<class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val)
{
while (first!=last) {
if (*first==val) return first;
++first;
}
return last;
}
L1.erase(find(L1.begin(), L1.end(), 3));
list的迭代器失效:
list的erase删除操作会造成迭代器失效,不过这是这个位置的迭代器失效,不同于vector,因为它是链表,地址不是连续的。增加结点不会。
当然lis也有自己一些特有的方法:
splice:这个操作效率是蛮高的,不会涉及大量元素的移动和拷贝,只是指针修改
void splice (iterator position, list& x);//完整列表
void splice (iterator position, list& x, iterator i);//单元素
void splice (iterator position, list& x, iterator first, iterator last);//范围
void remove (const value_type& val);
template <class Predicate> void remove_if (Predicate pred);
要给出条件,为真删除
list.unique()
将从容器中每个连续的相等元素组中删除除第一个元素之外的所有元素。
注意unique这个特点,删除的是每个连续的相等元素组中第一个元素之外的,所以要先进行排序,这样才能达到全列表去重,在算法库< algorithm>中也有这个方法
template <class ForwardIterator> ForwardIterator unique (ForwardIterator first, ForwardIterator last) { if (first==last) return last; ForwardIterator result = first; while (++first != last) { if (!(*result == *first)) // or: if (!pred(*result,*first)) for version (2) *(++result)=*first; } return ++result; }
L1.merge(L2);
在操作之前也要保证L1和L2有序,是两个有序链表的合并,合并后L2就成empty了
L.reverse()
反转链表
bool single_digit(const int& val)
{
return val < 10;
}
struct is_odd//是偶数,要保存偶数,是偶数时返回false
{
bool operator()(const int& val)
{
return val % 2 == 1;
}
};
void test01()
{
list<int> L{ 15, 4,4, 736, 7, 17, 20, 39, 20, 17, 4, 1 };
//L.sort();
//L.unique();//去重要先sort,再unique,才能达到效果
L.remove_if(single_digit);
L.remove_if(is_odd());
}
int main()
{
test01();
return 0;
}
list的特性基本就是这些,下面对list进行模拟实现:
先考虑一个问题,再次之前我们对迭代器的理解是:typedef Node* iterator;
那么如果对进行下面的操作,还是对的吗?
模拟实现
对于链表的操作没有太多难点,主要是区别于string和vector,list要封装自己的迭代器,才能完成预想操作
#pragma once
#include<iostream>
namespace gyx
{
template<class T>
struct ListNode
{
ListNode* prev;
ListNode* next;
T val;
ListNode(const T& value = T())
:prev(nullptr)
, next(nullptr)
, val(value)
{}
};
//对Node* iterator进行封装,因为对于list指针++,是不能满足遍历节点,++it,只是指针加+1,访问到的并不是下一个节点
//operator++(),重载++,让++实际是指向node->next;
template<class T,class Ptr,class Ref>
class ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T,Ptr,Ref> Self;
public:
//用于反向迭代器
typedef Ptr Ptr;
typedef Ref Ref;
Node* _pNode;
public:
ListIterator(Node* pNode = nullptr)
:_pNode(pNode)
{}
//支持解引用*
Ref operator*(){ return _pNode->val; }
Ptr operator->(){ return &(operator*()); }
//能够移动,list的移动是访问pnode->next
Self& operator++()
{
_pNode = _pNode->next;
return *this;
}
Self operator++(int)
{
Self temp(*this);
_pNode = _pNode->next;
return temp;
}
Self& operator--()
{
_pNode = _pNode->prev;
return *this;
}
Self operator--(int)
{
Self temp(*this);
_pNode = _pNode->prev;
return temp;
}
//能够比较
bool operator!=(const Self& s)const
{
return _pNode != s._pNode;//迭代器是否相同就判断所指节点是否相同
}
bool operator==(const Self& s)const
{
return _pNode == s._pNode;
}
};
//反向迭代器
template<class Iterator>
struct ListReverseIterator
{
typename typedef Iterator::Ref Ref;
//Iterator::Ref ;
//类名::成员名,这是静态成员变量的访问方式,这里是作为类型所以要加typename声明为类型,再起给类型起别名
typename typedef Iterator::Ptr Ptr;
typedef ListReverseIterator<Iterator> Self;
public:
ListReverseIterator(Iterator it)
: _it(it)
{}
// 解引用
Ref operator*()
{
Iterator temp = _it;
--temp;
return *temp;
}
Ptr operator->()
{
return &(_it->_pNode->_val);
}
///
// 能够移动
Self& operator++()
{
--_it;
return *this;
}
Self operator++(int)
{
Self temp(*this);
_it--;
return temp;
}
Self& operator--()
{
++_it;
return *this;
}
Self operator--(int)
{
Self temp(*this);
_it++;
return temp;
}
///
// 能够比较
bool operator!=(const Self& s)const
{
return _it != s._it;
}
bool operator==(const Self& s)const
{
return _it == s._it;
}
Iterator _it;
};
//list容器
template<class T>
class list
{
typedef ListNode<T> Node;
public:
typedef ListIterator<T,T*,T&> iterator;
typedef ListIterator<T, const T*, const T&> const_iterator;
typedef ListReverseIterator<iterator> reverse_iterator;//用正向迭代器,作为反向迭代器的模版的参数
//用迭代器作为参数,也就不用传递后面两个参数
typedef ListReverseIterator<const_iterator> const_reverse_iterator;
public:
list()
{
CreateHeadNode();
}
list(int n, const T& val = T())
{
CreateHeadNode();
for (int i = 0; i < n; i++)
push_back(val);
}
template<class Iterator>
list(Iterator first, Iterator last)
{
CreateHeadNode();
while (first!=last)
{
push_back(*first);
++first;
}
}
list(const list<T>& L)
{
CreateHeadNode();
for (auto x : L)//范围for是用迭代器来进行的,而拷贝构造要求const&,所以也要实现const_iterator
push_back(x);
}
list<T> operator=(list<T> L)
{
this->swap(L);
return *this;
}
~list()
{
Clear();
delete _head;
_head = nullptr;
}
iterator begin(){ return iterator(_head->next); }
iterator end(){ return iterator(_head); }
iterator begin()const{ return const_iterator(_head->next); }
iterator end()const{ return const_iterator(_head); }
reverse_iterator rbegin(){ return reverse_iterator(_head->next); }
reverse_iterator rend(){ return reverse_iterator(_head); }
const_reverse_iterator rbegin()const{ return const_reverse_iterator(_head->next); }
const_reverse_iterator rend()const{ return const_reverse_iterator(_head); }
size_t size()const
{
size_t count = 0;
Node* cur = _head->next;
while (cur != _head)
{
count++;
cur = cur->next;
}
return count;
}
bool empty()const{ return _head == _head->next; }
void resize(size_t newsize, const T& val = T())//可以看到list的resize方法是没有大量的元素拷贝问题的,list适合于这种经常增删的情况
{
size_t oldsize = size();
if (newsize < oldsize)
{
for (size_t i = newsize; i < oldsize; ++i)
pop_back();
}
else
{
for (size_t i = oldsize; i < newsize; ++i)
push_back(val);
}
}
T& front(){ return _head->next->val; }
T& back(){ return _head->prev->val; }
T& front()const{ return _head->next->val; }
T& back()const{ return _head->prev->val; }
void push_front(const T& val){ insert(beign(), val); }
void pop_front()
{
if (empty())
return;
erase(pos);
}
void push_back(const T& val){ insert(end(), val); }
void pop_back()
{
if (empty())
return;
auto pos = end();
--pos;
erase(pos);
}
iterator insert(iterator Itpos, const T& val)
{
Node* pos = Itpos._pNode;//访问该节点
Node* newnode = new Node(val);
newnode->next = pos;
newnode->prev = pos->prev;
newnode->prev->next = newnode;
pos->prev = newnode;
return newnode;
}
iterator erase(iterator Itpos)
{
Node* pos = Itpos._pNode;
if (pos == _head)
return pos;
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
Node* retNode = pos->next;
delete pos;
return retNode;
}
void Clear()
{
Node* cur = _head->next;
while (cur != _head)
{
_head->next = cur->next;
delete cur;
cur = _head->next;
}
_head->next = _head;
_head->prev = _head;
}
void swap(list<T>& L){ std::swap(_head, L._head); }
private:
Node* _head;
inline void CreateHeadNode()
{
_head = new Node();
_head->prev = _head;
_head->next = _head;
}
};
template<class T>
void showlist(list<T>& L)
{
for (auto x : L)
std::cout << x << " ";
std::cout << '\n';
}
}
测试:
#include"MyList.h"
using namespace gyx;
void test01()
{
list<int> L1(10, 2);
showlist(L1);
}
int main()
{
test01();
_CrtDumpMemoryLeaks();
return 0;
}