以下是list的node结点设计
template <class T>
struct __list_node{
typedef void* void_pointer;//型别是void *,但是实际上可以设为__list_node<T>*
void_pointer prev;
void_pointer next;
T data;};
list不再向vector一样拿普通指针作为迭代器,因为其结点不保证在存储空间中连续存在,list迭代器必须有能力指向list的结点,并且有能力进行正确的递增/递减/取值/成员存取等操作.递增指的是指向下一个结点,递减指的是指向上一个结点
list有一个重要性质:插入操作(insert)和接合操作(splice)都不会造成原有的list迭代器失效.erase也只是造成指向的那个元素失效,不会对其他迭代器产生影响.
template <class T,class Ref,class Ptr>
struct __list_iterator{
typedef __list_iterator<T,T&,T*> iterator;
typedef __list_iterator<T,Ref,Ptr> self;
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T> *link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
link_type node;//迭代器内部当然要有一个普通指针,指向list 结点
//constructor
__list_iterator(link_type x):node(x){}
__list_iterator(){}
__list_iterator(const iterator& x):node(x.node){}
bool operator==(const self &x) const {return node==x.node;}
bool operator!=(const self& x) const{return node!=x.node;}
//以下对迭代器取值(dereference),取的是节点的数据值
reference operator*()const{return (*node).data;}
//以下是迭代器的成员存取,运算子的标准做法
pointer operator ->()const{return &(operator*());}
//对迭代器累加1,就是前进一个节点
self & operator++(){
node=(link_type)((*node).next);
return *this;
}
self operator++(int){//后向加
self tmp= *this;
++*this;
return tump;
}
//对迭代器减一,就是后退一个结点
self &operator--(){
node=(link_type)((*node).prev);//指向前一个结点
return *this;
}
self operator--(int){
self tmp=*this;
--*this;
return tmp;
}
};
SGI list不仅是一个双向链表,而且还是一个环装双向链表 所以它只需要一个指针,便可以完整表现整个链表
让node可以指向放在尾部的一个空白结点
下面是几个函数
iterator begin(){return (link_type)((*node).next);}//因为是环状,那么最后一个空白结点的下一个结点就是头结点
iterator end(){return node;}//那么node结点自然而然就是尾部结点
下面是一个list实例:
#include<list>
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int i;
list <int> ilist;
cout<<"size="<<ilist.size()<<endl;//size=o
ilist.push_back(0);
ilist.push_back(1);
ilist.push_back(2);
ilist.push_back(3);
ilist.push_back(4);
cout<<"size="<<ilist.size()<<endl;//size=5
list<int>::iterator ite;
for(ite=ilist.begin();ite!=ilist.end();++ite)
cout<<*ite<<' ';
cout<<endl;
ite=find(ilist.begin(),ilist.end(),3);
//if(ite != 0)//这个地方暂时有点问题 后期再来处理
ilist.insert(ite,99);
cout<<"size="<<ilist.size()<<endl;//size=6
cout<<*ite<<endl;//3
for(ite=ilist.begin();ite!=ilist.end();++ite)
cout<<*ite<<' ';
cout<<endl;
ite=find(ilist.begin(),ilist.end(),1);
//if(ite!=0)
cout<<*(ilist.erase(ite))<<endl;//2
for(ite=ilist.begin();ite!=ilist.end();++ite)
cout<<*ite<<' ';
cout<<endl;
}
list缺省使用alloc作为空间配置器,并据此另外定义了一个list_node_allocator为的是更方便的以结点大小为配置单位
//专属空间配置器,每次配置一个大小
typedef simple_alloc<list_node,Alloc>list_node_allocator;
于是list_node_allocatior(n)表示配置n个结点的空间
下面四个函数用来配置,释放,构造,销毁一个结点
protected:
//配置一个结点并返回
link_type get_node(){return list_node_allocator::allocate();}
//释放一个结点
void put_node(link_type p){list_node_allocator::deallocate(p);}
//产生(配置并构造)一个结点,带有元素值
link_type create_node(const T&x){
link_type p=get_node();
construct(&p->data,x);//全局函数 构造析构的基本工具
return p;
//销毁(析构并释放)一个结点
void destory_node(link_type p){
destory(&p->data);//全局函数
put_node(p);
}
}
list提供许多constructors,其中一个是default constructor,允许我们不指定任何参数做出一个空的list出来
pulic :
list(){empty_initialize();}
protected:
void empty_initialize(){
node=get_node();//配置一个结点空间,令node指向它
node->next=node;//令node头尾都指向自己 ,不设元素值
node_prev=node;
}
当我们调用push_back()的时候,是将新元素插入到list的尾端,此函数内部调用insert():
void push_back(const T& x){insert(end(),x);}
insert()函数的目的是:在迭代器position位置插入一个新结点,它的内容是x
iterator insert(iterator position,const T& x){
link_type tmp=create_node(x);//差生一个结点(设置内容为x)
//调整双向指针,是tmp能够插入进去
tmp->next=position.node;
tmp->prev=position.node->prev;
(link_type(position.node->prev))->next=tmp;
position.node->prev=tmp;
return tmp;}
由于list不像vector那样有可能在空间不足时重新配置,数据移动操作,所以插入前的迭代器在插入操作之后都仍然有效.
//插入一个结作为头结点
void push_front(const T& x){insert(begin(),x);}
//插入一个结点作为尾结点
void push_back(const T &x){insert(end(),x);}
//移除迭代器position所指结点
iterator erase(iterator position){
link_type next_node=link_type(position.node->next);
link_type prev_node=link_type(position.node->prev);
prev_node->next=next_node;
next_node->prev=prev_node;
destorty_node(position.node);
return iterator(next_node);
}
//移除头结点
void pop_front(){erase(begin());}
//移除尾结点
void pop_back(){
iterator tmp=end();
erase(--tmp);//因为end()位置是一个空节点 所以需要前移一个结点
}
//清楚所有节点,整个链表
template <class T,class Alloc>
void list<T,Alloc>::clear(){
link_type cur=(link_type)node->next;//这是头结点
while(cur!=node){//遍历每一个结点
link_type tmp=cur;
cur=(link_type)cur->next;
destory_node(tmp);
}
//回复node的原始状态 都指向尾结点
node->next=node;
node->prev=node;
}
//将数值为valued的全部移除
template<class T,class Alloc>
void list<T,Alloc>::remove(const T& value){
iterator first=begin();
iterator last=end();
while(first!=last){//遍历每一个结点
iterator next=first;
++next;
if(*first==value) erase(first);
first=next;
}
}
//移除数值相同的连续元素 注意:只有"连续而相同的元素"才会被移除一个
template <class T,class Alloc>
void list<T,Alloc>::unique(){
iterator first=begin();
iterator last=end();
if(first==last) return;//空链表 什么都不必做
iterator next=first;
while(++next!=last){//遍历每一个结点
if(*first==*next)
erase(next);
else
first=next;//调整指针
next=first;//修正区段范围
}
}
list 内部提供有一个所谓迁徙操作
protected:
//将[first,last)内的所有元素移动到position之前
void transfer(iterator position,iterator first,iterator last){
if(position!=last){
(*(link_type((*last.node).prev))).next=position.node;//1
(*(link_type((*first.node).prev))).next=last;//2
(*(link_type((*position.node).prev))).next=first.node;//3
link_type tmp=link_type((*position.node).prev);//4
(*position.node).prev=(*last.node).prev;//5
(*last.node).prev=(*first.node).prev;//6
(*first.node).prev=tmp;//7
}
}
这个过程有点复杂
上述的transfer并非是公开的接口,list提供的公开接口是(splice):将某个连续范围的元素从一个list移动到另一个(或同一个)list的某个定点
下面是一些函数的具体实现,
//为了提供各种接口弹性,list<T>::splice有许多版本
public:
//将x接合于position位置之前,x必须不同于*this
void splice(iterator position,list &x){
if(!x.empty())
transfer(position,x.begin(),x.ned());
}
//将i所指元素接合于position位置所指元素之前,position和list可可指向同一个list
void splice(iterator position,list&,iterator i){
iterator j=i;
++j;
if(position==i||position==j) return ;
transfer(position,i,j);
}
//将[first,last)内的所有元素接合于position所指位置之前
//positionhe[first,last)可指向同一个list
//但position不能位于[firslast)之内
void splice(iterator position,list &,iterator first,iterator last){
if(first!=last)
transfer(position,first,last);
}
//一下是merge(),reverse(),sort()的源代码,有了transfer()在手,这些操作就会变得简单
//merge()将x合并到*this上 两个list都必须经过递增排序
template <class T,class Alloc>
void list<T,Alloc>::merge(list<T,Alloc>&x){
iterator first1=begin();
iterator last1=end();
iterator first2=x.begin();
iterator last2=x.end();
//注意前提是两个list都经过递增排序
while(first1!=last1&&first2!=last2)
if(*first2<*first1){
iterator next=first2;
transfer(first1,first2,++next);
first2=next;
}
else
++first1;
if(first2!=last2) transfer(last1,first2,last2);
}
//reverse()将*this内容逆向重置
template<class T,class Alloc>
void list<T,Alloc>::reverse(){
//以下判断,如果是空列表或者仅有一个元素,那么就不进行任何操作
//使用size()==0||size()==1来判断,虽然也可以,但是比较缓慢
if(node->next==node||link_type(node->next)->next==node)
return ;//此时不进行任何操作
iterator first=begin();
++first;
while(first!=end()){
iterator old=first;
++first;
transfer(begin(),old,first);
}
}
//list 不能使用STL的sort(),必须使用自己的sort()成员函数
//因为STL算法sort()只接受RandomAccessIterator
template<class T,class Alloc>
void list<T,Alloc>::sort(){
//以下判断如果是空列表或者仅有一个元素,就不进行任何操作
//与上面同理
if(node->next==node||link_type(node->next)->next==node)
return;
//一些新的lists作为中介数据存放区
list<T,Alloc>carry;
list<T,Alloc>counter[64];
int fill=0;
while(!empty()){
carry.splice(carry.begin(),*this,begin());
int i=0;
while(i<fill&&!counter[i].empty()){
counter[i].merge(carry);
carry.swap(counter[i++]);
}
carry.swap(counter[i]);
if(i==fill) ++fill;
}
for(int i=1;i<fill;++i)
counter[i].merge(counter[i-1]);
swap(counter[fill-1]);
}
最后这个sort()函数使用的快速排序,但是没太看明白,后期回来整理一下