【C++ STL】序列式容器之list

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;

link_type node;
Self& operator ++(){
...
}
....

};

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那样有可能在内存不足时做重新配置,数据移动和释放原来空间的操作,所以插入前的所有迭代器在插入操作之后仍然有效。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值