1.list概述
相比较于vector的连续线性空间,list就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间,不像vector那样当空间不足时将重新配置,数据移动等操作。
2.list的函数列表
1).list 构造函数
list <int > L0 ; // 空链表
list <int > L1 (9); // 建一个含个默认值是的元素的链表
list <int > L2 (5,1); // 建一个含个元素的链表,值都是
list <int > L3 (L2 ); // 建一个L 2 的 copy 链表
list <int > L4 (L0 .begin (), L0 .end());// 建一个含 L0 一个区域的元素
2). assign() 分配值,有两个重载
L1. assign ( 4,3); // L1(3,3,3,3)
L1. assign( ++list1.beging(),list2.end()); // L 1(2,3)
3 ). operator= 赋值重载运算符
L1 = list1; // L1 (1,2,3)
4). front() 返回第一个元素的引用
int nRet = list1.front() // nRet = 1
5). back() 返回最后一元素的引用
int nRet = list1.back() // nRet = 3
6). begin() 返回第一个元素的指针(iterator)
it = list1.begin(); // *it = 1
7). end() 返回最后一个元素的下一位置 的指针(list 为空时end()=begin())
it = list1.end();
--it; // *it = 3
8.rbegin() 返回链表最后一 元素的后向指针(reverse_iteratoror const)
list <int >::reverse_iterator it =list1 .rbegin (); // *it = 3
9). rend() 返回链表第一元素的下一位置 的后向指针
list< int>::reverse_iterator it =list1 .rend(); // *(--riter) = 1
10).push_back() 增加一 元素到链表尾
list1.push_back( 4) // list1(1,2,3, 4 )
11). push_front() 增加一元素到链表头
list1.push_front( 4) // list1( 4 ,1,2,3)
12). pop_back() 删除链表尾的一个元素
list1.pop_back( ) // list1(1,2)
13).pop_front() 删除链表头 的一 元素
list1.pop_front() // list1(2,3)
14) .clear() 删除所有元素(析构并释放每个节点)
list1.clear(); // list1 空了,list1.size() = 0
15).erase() 删除 一个元素 或 一个区域的元素 ( 两个重载函数)
list1.erase( list1.begin()); // list1(2,3)
list1.erase( ++list1.begin(),list1.end());// list1(1)
16). remove() 删除链表中匹配值的元素( 匹配元素全部删除)
list 对象L1(4 ,3,5,1, 4 )
L1.remove(4); // L1(3,5,1);
17).remove_if() 删除条件满足的元素( 遍历一次链表) ,参数为自定义的回调函数
// 小于2 的值删除
bool myFun (const int & value ) {return (value < 2); }
list1.remove_if( myFun ); // list1(3)
18).empty() 判断是否链表为空
bool bRet = L1.empty(); // 若L1 为空,bRet =true ,否则bRet = false 。
19).max_size() 返回链表最大可能长度
list <int >::size_type nMax = list1.max_size ();// nMax = 1073741823
20 ).size() 返回链表中元素个数
list< int>::size_type nRet =list1.size(); // nRet = 3
21).resize() 重新定义链表长度( 两重载函数)
list1.resize(5) // list1 (1,2,3, 0,0 ) 用默认值填补
list1.resize(5,4) // list1 (1,2,3, 4,4 ) 用指定值 填补
22).reverse() 反转链表:
list1.reverse( ); // list1(3,2,1)
23).sort() 对链表排序,默认升序( 可自定义回调函数 )//由于list是Bidrectional Iterators ,不能使用STL算法sort(),因为sort()只接受RandomAccessIterator,必须自己定义成员函数sort():
list L1(4,3,5,1,4)
L1.sort( ); // L1(1,3,4,4,5)
L1.sort( greater <int >() ); //L1(5,4,4,3,1)
24).merge() 合并两个有序链表并使之有序
// 升序
list1.merge(list2); // list1(1,2,3,4,5,6) list2 现为空
// 降序
L1( 3,2,1), L2(6,5,4)
L1.merge(L2, greater <int >() ); //list1(6,5,4,3,2,1) list2 现为空
25).splice() 对两个链表进行结合( 三个重载函数) 结合后第二个链表清空
list1.splice( ++list1.begin(),list2);
// list1(1,4,5,6,2,3) list2 为空
list1.splice( ++list1.begin(),list2,list2.begin());
// list1( 1,4,2,3); list2(5,6)
list1.splice(++list1.begin(),list2,++list2.begin(),list2.end());
//list1( 1, 5,6, 2,3); list2(4)
26).insert() 在指定位置插入一个或多个元素( 三个重载函数)
list1.insert( ++list1.begin(),9); // list1(1,9,2,3)
list1.insert(list1.begin(),2,9); // list1(9,9,1,2,3);
list1.insert(list1.begin(),list2.begin(),--list2.end());//list1(4,5,1,2,3);
27).swap() 交换两个链表( 两个重载)
list1.swap(list2); // list1 (4 ,5 ,6 ) list2 (1 ,2 ,3 )
28). unique() 删除相邻重复元素
L1( 1, 1 ,4,3,5,1)
L1.unique( ); // L1(1,4,3,5,1)
3.list的节点
每一个设计过list的人都知道,list本身和list的节点是不同的结构,需要分开设计。以下是STL list的节点(node)结构:
template<class T>
struct __list_node{
typedef void* void_pointer;
void_pointer prev;
void_pointer next;
T data;
};
由上可以看出,STL list是双向链表。
4.list的迭代器
list不再能够像vector一样以普通指针作为迭代器,因为其节点不保证在储存空间中连续存在。list迭代器必须有能力指向list的节点,并有能力进行正确的递增、递减、取值、成员存取等操作。所谓“list迭代器正确的递增、递减、成员存取”等操作是指,递增时指向下一个节点,递减时指向上一个节点,取值时取出节点的数据值,成员取用时取用的是节点的成员。
list有一个重要性质:插入操作和结合操作都不会造成原有的list迭代器的失效。这在vector中时不可能的,因为vector的插入操作将导致存取空间的重新配置,导致原有的迭代器的全部失效。甚至list的删除也只是导致被删除的迭代器失效,其他的迭代器不受影响。在list的迭代器中实现了operator ==,operator!=,operator -> ,operator ->及递增operator++和递减操作operator--等,迭代器的设计如下:
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 bidirectrional_iterator_tag iterator_category;//由于list链表是双向循环链表,所以迭代器设计为双向迭代器。
typedef __list_node<T> list_node;
typedef list_node* link_type;
};
5.list的数据结构
由于list不仅是双向链表,而且是双向循环链表,所以只需要通过一个指针就可以遍历整个链表,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;//只需要一个指针就可以表示整个环状双向链表
};
如果让指针node指向空白节点,这样node就满足STL的前开后闭的设计准则,成为last迭代器,这样以下几个函数便能轻易设计:
iterator begin(){
return (link_type)((*node).next);
}
iterator end(){
return node;
}
6.list的构造与内存管理
list还是缺省使用Alloc作为空间配置器,list的空间分配不像vector那样复杂,需要经过申请空间,数据拷贝,释放空间,在list中当通过push_back()和push_front压入数据时都是通过调用成员函数insert()来完成的,insert有许多重载版本,以下是最简单的一种,首先配置并构造一个节点,然后再尾端进行适当的操作,将节点插入进去:
iterator insert(iterator position,const T&x){
link_type tmp=create_node(x);
tmp->next=position.node;
position.node->prev=tmp;
((link_type)(position->prev))->next=tmp;
tmp->prev=position->prev;
}
注意,插入完成之后,新节点将位于哨兵迭代器(标识出插入点)所指节点的前方——这是STL对于插入操作的标准规范。由于list不像vector那样有可能在内存不足时做重新配置,数据移动和释放原来空间的操作,所以插入前的所有迭代器在插入操作之后仍然有效。