一、链表的基本概念

链表是数据元素的线性集合(linear collection),物理存储不连续。那么,这种设计的优点是什么?缺点又是什么?

链表的基本结构:

链表是由一系列的“节点”组成在一起的集合,节点(node)由数据域(data)和指针域(next)组成。

从功能上看,data负责存储数据,next负责存储下一个节点的位置。当然,用更加严格的语句来讲,next存储的是其直接后继的地址,关于直接后继的定义见:

链表的分类:

常见的链表种类有:单向链表、双向链表、单向循环链表、双向循环链表,将会在后面文章中单独介绍各种链表的结构和代码实现,以及对应的链表操作。

链表的基本操作:

链表的基础操作包含:插入、删除、查找、合并等,此外还有:反转、排序、深度复制等。

链表的优点:

  • 插入和删除快;
  • 存储空间不受限制,可动态申请扩充,不需事先开辟内存;

链表的缺点:

  • 相比于数组,链表的需要更多的存储空间(需存储指针);
  • 存储不连续导致其访问时间长;
  • 从反向访问角度,单向链表难以反向访问,扩展为双向链表则需要额外存储反向指针;
  • 每次节点访问都需要从头部开始;

二、单链表

基本结构:

单链表的结构含有四个概念:头指针、头结点、普通node、尾结点,下面分别介绍:

  • 头指针指向头结点,是单链表的表示,必不可少;
  • 头结点是单链表的第一个节点,其数据域内容一般无效;
  • 普通node即用于数据存储和直接后继指针存储的节点;
  • 尾结点即链表中最后一个节点,其指针域next为空,其后无节点;

单链表的基本操作:

针对单链表常见的操作有:增、改、查、删等,

常用的操作如下:

(1)增

对链表添加元素一般有三种方法:头插法(add)、尾插法(append)、任意位置插入法(insert)。

(2)改

改动链表中某个节点的data

(3)查

查找分为按值查找和按位置查找两种,前者表示按照值查找对应位置,后者表示按位置查找对应值;

(4)删

删除分为按值删除和按位置删除两种;前者表示按照值删除对应节点,后者表示按照位置删除对应节点;

实现说明:

按照自己目前所看的资料,一般都会实现下面介绍的这些函数,具体介绍放在python和c++实现中。

1.python实现

(1)节点设计

按照单链表的定义可知,节点包含数据域data和指针域next

但是由于next和python的内置函数next()重名,所以指针域使用pointer表示。

代码如下:

(2)链表类:single_linked_list

上述node类对象即为链表的基本组成结构,可以用于实现头结点、普通节点和尾结点。

因此,链表类只需要提供头指针:

(3)判断链表是否为空:is_empty()函数

实际上,只需要判断头指针是否指向node类对象(或是否等于none),就可判断一个链表是否为空:

(4)头插法:add()函数

在链表头进行节点插入是很常见的插入操作,这种方式使得“先插入的节点在链表尾部”。头插法需要将头指针指向新的节点,并让新的节点指向原来的头结点:

(5)尾插法:append()函数

如果想要链表节点次序和插入次序相同,就需要使用尾插法。在插入之前需要判断链表是否为空,如果不为空才能进行插入(可以调用前面定义的is_empty()函数,但是下述代码没有)。

此外,还需要进行链表的遍历操作,找到最后一个节点。单链表只能从表头开始访问,所以每次尾插都必须遍历。

(6)在任意位置插入:insert()函数

前面介绍的头插法和尾插法,其原理相对简单,但是并不能完全满足插入需求。如果知道目标插入的位置,可以采用insert()函数实现任意位置的节点插入。

需要注意的是,在实现insert()函数时必须考虑到“position”参数可能出现的几种情况。比如python中并没有明确的类型要求,所以要检查“position”是不是int类型。

对于核心的节点插入实现功能,需要找到目标插入位置对应的节点,并使得这个节点指向新节点,让新节点指向原位置节点的后一个节点。这个过程类似于铁链中加入铁环的过程,要保证新铁环和原来的两个铁环相连接。

(7)计算链表长度:get_length()函数

对于调用者和类内部的其它函数来做,链表长度是一个非常有用的值。比如在插入函数insert()中,需要判断插入位置是不是大于链表长度。

计算链表长度的实现比较简单,只需要遍历链表的所有节点,并用计数器来统计节点的数目即可。

(8)遍历所有节点:traversal()函数

链表、树、图等结构都需要遍历操作,其中链表的遍历比较简单,只需要依次的访问所有节点即可。

(9)搜索:search()函数

前面提到搜索有按值搜索和按位置搜索两种,它们的原理和实现都十分相似,所以仅以按值搜索为例。

需要注意的是,insert()函数需要判断链表是否为空,并且需要考虑到目标值不在链表中的情况,分别对应不同的返回值。

(10)删除:delete()函数

上述的查找中以“按值查找”为例,这次删除中同样以“按值删除”为例,“按位置删除”的实现与之类似。

按值删除,即删除目标值对应的目标节点。在进行遍历时,需要记录当前节点和当前节点的前一个节点。因为,一旦查找大目标值所在的目标节点,需要令目标节点的前一个节点指向目标节点的下一个节点,即完成节点的删除。

2.c++实现

前面的python实现中已经分析了各个函数的作用,以及对应的实现过程。虽然python和c++的语法不同,但是核心过程是类似的,所以下面不再重复对过程的叙述。

(1)节点设计

由于c++的指针必须指定类型,所以需要使用空指针null作为pointer的值。

(2)链表类:singlelinkedlist

遵循声明和实现分类的策略,先对各个函数进行声明。

(3)判断链表是否为空:isempty()函数

(4)头插法:add()函数

(5)尾插法:append()函数

(6)在任意位置插入:insert()函数

(7)计算链表长度:getlength()函数

(8)遍历所有节点:traversal()函数

(9)搜索:search()函数

(10)删除:remove()函数

总结:

在使用python实现时,头结点数据域data是有效的。这种方式使得代码中需要很多的“if-else”判断结构,增加了代码的复杂性。

在使用c++实现时,头结点数据域data是无效的,这种方式使得代码更加简洁。

到此这篇关于c++和python实现单链表及其原理的文章就介绍到这了,更多相关c++,python单链表内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!