例说数据结构&STL(二)——list

1 白话list(链表)
  list(链表,准确来说STL中该模板类是双向链表,单向链表是slist(目前STL中还没有单独的容器实现),也是一种非常常用的数据结构,它和vector一样是属于STL中顺序容器的一种。list中各个元素之间也存在一个线性的逻辑次序,但是与vector极其不同的是,各单元的物理地址可以任意,无需是连续的空间分配。
  相比较而言,vector的物理存储次序与其逻辑关系必须严格的对应,所以获取元素和size()统计个数操作均可以在常数级时间复杂度中完成,而插入(删除)元素前不得不移动O(n)次后继元素。而链表是一种动态存储的结构,数据分散为一系列称为节点的个体。节点之间通过指针相互联系与访问。所以插入(删除)元素只需要调整局部少量的相关节点的指针指示。但是代价就是失去通过下标索引访问各个成员的能力,只能通过r次迭代扫描来访问第r个元素。也就是说list中删除和插入元素是常数时间复杂度O(1),而索引与size()是线性时间复杂度O(n)。
2 STL中list实战
 2.1 头文件包含  
  STL容器list使用首先需要在程序开头添加头文件包含,这样才可以直接调用list中各种类方法。

#include<list>

  其次一定要加上C++标准库命名空间std,一种方便形式是在文件头文件后面添加using namespace std;,这样全文可以直接使用std下面定义的类与方法了。还可以每个实体前声明诸如std::list形式,这样稍显复杂,但是如果程序中有自己定义的命名空间就最好这样声明了。
 2.2 类型定义  
  这个算是一个技巧的介绍,有时候为了方便程序维护与以后修改,我们可以在程序头类型定义对应的模板类为一个简化名称(一般大写表示),如下:

typedef list<int> INTLIST;
typerdef vector<long> LONGVECTOR;
typerdef list<int>::iterator INTLISTITER;

INTLIST list_fir; //声明一个list<int>对象

  这样的好处就是一旦后面我们想用list<long>定义我们的变量,只需在此修改list<int>list<long>,从而不需要整个程序中修改了。此外取一个通俗易懂的名称也会增加程序的通读行。
 2.3 变量声明  
  首先是最简单的对象声明,容器对象中为空,如下:

list<int> list_fir; //或者INTLIST list_fir,这里不去简写

  下面我们再看看带初始化的声明方式是什么样子的。

list<int> list_sed(10,2);     // 申请10个空间,全部初始化为2

list<int> list_thd(list_fir); //拷贝构造

vector<int> vec(10,1);
list<int> list_for(vec.begin(),vec.end()); //拷贝vector一段数据初始化

 2.4 赋值操作 
  和vector一样,同时格式和前面初始化其实也是一样,参数分为下面几种:

list_fir.assign(10,10086); //重新赋值10个10086

list_sed.assign(list_fir.begin(),list_fir.end()); 

 2.5 拼接操作 
  std::list中提供了两种拼接的方法merge()和splice(),链表拼接实现如下:

list_fir.merge(list_sed); // 拼接加在list_fir上,而list_sed变为空

list_fir.splice(list_fir.begin(),list_sed); //在list_fir初始位置拼接上list_sed

 2.6 删除操作 

list_fir.erase(++list_fir.begin()); //删除第二个数

list_fir.erase(++list_fir.begin(),list_fir.end()); //删除到只剩开头一个数 

 2.7 插入操作 

list_fir.insert(list_fir.begin(),20); //首部插入一个20数据

list_fir.insert(list_fir.begin(),list_sed.begin(),list_sed.end());  // 插入整个list_sed在首部

 2.8 尾首删除与添加操作 
  我们知道vector只提供了尾部删除和添加的操作,而list不仅提供了尾部还提供了首部添加与删除操作。

list_fir.push_back(110); //尾部增加一个110数
list_fir.push_front(1);  //首部增加一个1数

list_fir.pop_back();     //尾部删除一个数
list_fir.pop_front();    //首部删除一个数

 2.9 打印与遍历操作
  前面介绍过vector可以通过下标索引每个元素,所以vector模板类中提供了operator[]运算符重载。而链表因为物理地址的不连续性,所以不可以通过下标访问每个数据,切记没有诸如list_fir[2]这样的使用!我们只能通过迭代器来遍历访问每个数据。迭代器在vector介绍中已经说过,可以看成就是指针,使用的时候记得包含头文件#include<iterator>但是需要注意一点,list中迭代器不能像vector或者deque中迭代器出现关系或者算数运算,例如list_fir.begin()+5这样的操作,只允许iter++或者iter–这样的迭代操作。这是因为vector和deque支持通过元素的位置实现随机访问,而list并不行。另外还包括反向迭代,更详细介绍请另阅例说数据结构&STL(十二)——iterator
  list的元素访问程序如下:

int data1 = list_fir.front(); // 第一个数

int data2 = list_fir.back();  // 最后一个数

list<int>::iterator iter;
for(iter=list_fir.begin();iter!=list_end();iter++)
{
    cout<<*iter<<endl;        //就像指针一样*间接访问每个数

}

 2.10 其他实用操作 

size_t list_fir.size(); // 统计list中数据个数

list_fir.unique();      // 链表中每个连续出现的数仅保留第一次出现的那个

if(!list_fir.empty())   // 判断链表中是否为空
{
    list_fir.clear();   // 清空链表
}

list_fir.swap(list_sed);// 交换两个链表所有数据

list_fir.sort();        // 增序排序整个链表

  这里提醒一下unique()函数接口使用注意的地方,实际上它是和algorithm头文件中unique实现一样,对于连续相同元素仅保留一个,而对于间断性相同元素,每个连续间断都会保存一个。如果想整个list中仅保存相同的元素一个,那么可以先排序再调用该unique()函数。
3 小结
  至此,我们将STL容器中list大部分方法接口介绍了一遍,相信大家有了一定的认识。前面也介绍了它的优势,第一点可以节约空间,不用大片连续空间保存;第二删除与添加数据的方便,可以在常数级时间复杂度中完成工作。如果我们程序中需要频繁删除与添加数据(更确切的说是首部或者中间位置删除或添加),我们最好是使用链表数据结构,这会大大提高我们程序的性能。
  以上是个人学习记录,由于能力和时间有限,如果有错误望读者纠正,谢谢!
  转载请注明出处:http://blog.csdn.net/FX677588/article/details/76218306

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值