STL源码剖析之序列式容器list
2011-06-07 13:48 Aga.J 阅读(917) 评论(1) 编辑 收藏 举报33 List
List是一个紧凑型的容器,每次插入或者删除一个元素,就需要配置或者释放一个元素空间,并且,对于任何位置的元素插入或元素移除,list都是【常数】时间
List的节点和List本身是不同的结构(这和vector一致)
Template<class T>
Struct __list_node
{
Typedef void* void_pointer; // typedef __list_node<T>* pointer;
Void_pointer prev;
Void_pointer next;
T data;
}
可以看出stl的list是基于链表的。
因为list不是一种基于连续空间的容器,所以我们没办法使用普通的指针通过指针上下偏移来作为list的迭代器,因为普通的指针的自增等操作无法正确的寻址到list的下一个元素。我们需要一个新的iterator,可以在自增的时候指向下一个节点,取值的时候取到节点的数据域。
我们设计一个Bidirectional iterator来完成list的迭代子操作
Template<class T, class Ref, class Ptr>
Struct __list_iterator
{
为什么要用这两种形式定义??
Typedef __list_iterator<T, T&, T*> iterator; //兼容其他类型的iterator
Typedef __list_iterator<T, Ref,Ptr> self; //匹配参数类型的iterator
根据前一章vector的学习得知设计一个容器类的迭代器,需要下面的类型信息
Typedef Bidirectional_iterator_tag iterator_category;
Typedef T value_type;
Typedef Ptr pointer; //不指定Ptr的类型,由模板参数指定,增加了灵活性
Typedef Ref reference;
Typedef __list_node<T>* link_type;
Typedef size_t size_type;
Typedef ptrdiff_t difference_type;
Link_type node; //指向list的当前节点 __list_node<T*>* node,也是迭代器内的实际指针
__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;}
Reference operator* () const { return (*node).data;}
Pointer operator->() const{return &(operator*());}
Self& operator++()
{
Node = (link_type) ( (*node).next );
Return *this;
}
Self operator++(int) //前置++,所以在编程过程中使用i++ 会带来额外的局部变量操作的开销
{
Self tmp = *this;
++*this;
Return tmp;
}
Self& operator—(){}
Self operator — (int ){}
}
template<class T, class Ref, class Ptr>
struct __list_iterator
{
__list_node<T>* node;
__list_iterator(_list_node<T>* n): node(n){}
__list_iterator(const __list_iterator<T,T&,T*>& other): node(other.node){}
__list_iterator(){}
__list_iterator<T,Ref,Ptr>& operator*(){return (*node).data;}
//Ref operator*() const {return (*node).data;}
__list_iterator<T,Ref,Ptr>* operator->(){}
//Ptr operator->() const {return &(operator*())}
__list_iterator<T,Ref,Ptr>& operator++()
{
node = (*node).next;
return *this;
}
__list_itertor<T,Ref,Ptr> operator++(int)
{
__list_itertor<T,Ref,Ptr> temp=*this;
(*this)++;
return temp;
}
}
(1)定义好list容器的节点 (2)以及针对节点进行操作的迭代器 (3)接下来是定义list数据结构了。
Template<class T, class Alloc=alloc>
Class List
{
//头节点,基本向前向后操作,删除,插入等
Protected:
Typedef __list_node<T> list_node;
Public:
Typedef list_node* link_type;
Protected:
Link_type node;
Public:
__list_iterator iterator;
//定义iterator是给外部用,内部也可以直接操作node
//…
};
这种node类型定义方式,充分利用了类的保护域,因为想保护list node的实际类型,所以将其非指针的类型在protected中声明,再通过public暴露list node的指针类型,由此我们可以方便的在类外声明一个和该类有相同类型的节点的指针。同时为了隐藏list的实际节点操作,我们也将节点的定义放在了protected中。
//假设让node指向链表尾部的一个空白节点,并且该节点下一个为头,则
Iterator begin(){return (link_type)((*node).next) ;}
Iterator end() {return node;}
Bool empty const{return node->next == node;} //尾部即为头部
Size_type size() cosnt
{
Size_type result=0;
Distance ( begin(), end(), result);
Return result;
}
Reference front(){return *begin();}
Reference back() {return *(—end());}
34 list的重头戏,内部构造和内存管理
(constructor, push_back , insert)
List和vector一样,使用alloc作为自身的空间配置器,但是list还多定义了一个list_node_allocator,让我们可以更加方便的以【节点的大小】作为配置单位(vector使用元素值大小作为基本配置单位)。
Template<class T, class Alloc=alloc>
Class List
{
Protected:
Typedef __list_node<T> list_node;
Typedef simple_alloc<list_node,Alloc> list_node_allocator;
这样一来 list_node_allocator就是该list的基本配置单位了!
Protected:
Link_type get_node() {return list_node_allocator::allocate(); }
//会根据list_node_allocator的模板参数类型来配置所需要的空间
//配置一个节点,并传回
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 destroy_node(link_type p)
{
Destroy( &p->data);
Put_node(p); //释放空间
}
Public:
List() { empty_initialize();}
Protected:
Void empty_initialize()
{
Node = get_node();
Node->next= node; //node的头尾都指向自己,防止边界错误
Node->prev= node;
}
Public:
Void push_back( const T& x) { insert(end(),x); }
{
Iterator insert(iterator position,const T& x) //使用__list_iterator
{
Link_type tmp = create_node(x);
Tmp->next=position.node;
Tmp->prev=position.node->prev;
(position.node->prev)->next=tmp;
position.node->prev=tmp;
return tmp; //__list_iterator(_list_node<T>* n): node(n){}
}
}
}
可以看到list的内存配置灵活的使用了alloc,将alloc通过一个simple_alloc封装起来,并且指定空间分配的大小是list的node节点大小,这样一来就可以很好的为list分配节点空间。
35 list的元素操作 push_front, push_back, erase, pop_front, pop_back, clear,remove….
Void push_front ( const T& x) { insert (begin(),x); } //在begin之前插入
Void push_back (const T& x) {insert(end(),x);} //在end之前插入
Iterator erase(iteator 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;
Destroy_node( position.node);
Return iterator(next_node);
}
Void pop_front() {erase(begin());}
Void pop_back() {iterator tmp = end(); erase(--tmp); }
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;
Destroy_node(tmp);
}
Node->next = node; //初始化的状态
Node->prev = node;
}
Template<class T, class Alloc>
Void list<T,Alloc>::remove( const T& value)
{
Iterator first = begin();
Iterator last = end();
While( first!=last) // for( iterator iter= first;iter!=last;iter++)
{
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是一个双向环状链表,只要我们把边际条件处理好,那么在任何位置插入或移除元素,操作都是一样的。
36 除此以外,list还提供了一个transfer的方法,可以将[first,last) 的所有元素移动到position之前,这适用于两个list也适用于一个list内的transfer。
如何设计一个transfer,来适配多种迁移情况?
Void transfer(iterator position, iterator first, iterator last) // 注意是这个[first,last)区间
{
If( last!=postion) //这种情况下本来就满足了
{
Position->prev->next = first; //将pos原来的前一个节点的next指向first
First->prev->next = last; //将first原来的前一个节点的next指向last
Iterater tmp= last->prev; //保存last原来的前一个节点
Last->prev = First->prev; //将last的prev指向first原来的前一个节点
First->prev = position->prev; //将first的prev执行position的prev
temp->next=position; //将last原来的前一个节点的next指向position
position->prev=temp; //将position的prev指向last原来的前一个节点
}
}
同时正因为有了transfer,使得list在执行splice,reverse,sort等操作时变得十分简洁。
Public:
Void splice( iterator position, list& x)
{
If (!x.empty())
Transfer ( position, x.begin(), x.end());
}
Void splice (iterator position, list&, iterator i)
{
Iterator j=I;
++j;
If(position == I || position ==j) return;
Transfer(position,I,j);
}
Void splice(iterator position, list&, iterator first , iterator last)
{
If (first!=last)
Transfer(position, first,last);
}
Merge. Reverse, sort
Template<class T, class Alloc>
Void list<T,Alloc>::merge(list<T,Alloc>& x) //假设每个list元素都递增
{
Iterator first1 = begin() ;
Iterator last1 = end() ;
Iterator first2 = x.begin();
Iterator last2 = x.end();
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);
}
Template<class T, class Alloc>
Void list<T,Alloc>::reverse()
{
If( node->next == node || link_type(node->next)->next == node )
Return;
// 如果条件不足,即list为空,或者list只有一个头节点
Iterator first = begin();
++first;
While(first !=end())
{
/*
Transfer(begin(),first,first->next);
First=begin()->next;
*/
Iterator old=first;
++first;
Transfer(begin(),old,first);
}
}
List不能使用STL的算法sort,因为sort只接受RamdonAccessIterator
Template<class T, class Alloc>
Void list<T, Alloc>::sort() //只能自定义list的sort函数
{
If( node->next==node || node->next->next==node) // 如果条件不足,即list为空,或者list只有一个头节点
Return;
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); //上一个counter和新的carry元素进行merge
Carry.swap(counter[i++]); //新的carry元素和已经完成merge的count进行交换,并增加i的计数
}
Carry.swap(counter[i]); //counter[i]记录本次循环的carry结果
If(i==fill) ++fill;
}
For (int i=1;i<fill;++i)
Counter[i].merge(counter[i-1]); //每个counter都已经有序的情况下,对counter进行两两merge
Swap(counter(fill-1));
}
书上说该sort函数使用了quick sort的方法,但我看了好久还是看不出这和quick sort有什么关系,求解答!!
这个排序算法主要是利用了merge函数的归并排序,每次都从未排序的队列中取出第一个元素,然后利用已经排序的counter,进行merge,之所以使用counter是因为index越小的counter元素所含的元素越小,可以执行更便捷的merge,然后再将merge结果和下一个counter进行再一次的merge,直到counter元素都完成了一个merge。这种思路好像是类似于利用已经排好序的长度不一的队列之间的merge,从而提高性能,所以我所理解的和quick sort有关系的就在这里:利用部分有序。书上只是说“本函数采用quick sort”..花了我一个多小时研究啊…
作者:Aga.J
出处:http://www.cnblogs.com/aga-j
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
个人学习笔记仅供本人记录知识所用,不属发表性文章。