2020-07-09更新
细细琢磨了一下以前的这篇文章,感觉这样不太能体现链表的精髓,要想真的想深入研究链表这种数据结构,在没有指针的语言中,还是应该用静态链表来模拟真正链表比较好。
对于静态链表,个人认为要先想想下面几点:
静态链表的存储结构是什么?
没有指针,怎么来模拟指针?怎么模拟C语言中地址的概念
怎么去模拟内存管理?
OK, 先来聊聊第1、2点,静态链表在没有指针的语言中用数组来实现,用一组地址连续的存储单元来存放数据(第一次了解到这里,我也是懵圈的,数组???这不就是顺序表吗,怎么和链表扯上关系?),有意思的就来了,我们就用数组的下标来代替地址吧!!!对,要学会静态链表,你就先要把数组看做一个内存空间,数组下标就是这个空间的地址。C有指针就相当于可以在整个内存空间的海洋里遨游,而静态链表就是只能在自己搭建的舞台上起舞。
当你认识到数组来模拟一片内存空间,下标就是地址的时候,我们就要来解决实际问题了,也就是怎么模拟内存的管理?
一、肯定是定义节点item类,其中data保存数据,next保存下一个节点的下标(也可以理解为下一个的位置或者地址),静态链表类SLinkList,在初始化函数__init__里创建一个size+1大小的列表link(因为我的设计是将0节点设为头结点,不保存数据,用来记录链表长度length,链表尾部rear等信息)。
现在内存空间创建好了,那么问题又来了,我们怎么知道哪些节点是链表里面的,哪些节点是空闲的?在这里我想到了一个比较巧妙的方法,不用另外创建一个数组去存储空闲空间信息。因为初始化的时候所有的空间都是空闲的,我们可以先将所有的空闲节点连起来(也就是第i个节点保存第i+1个节点的位置),然后头结点用一个变量space指向第一个空闲空间的位置,这样我们就保持space永远指向一个空闲节点的位置,即可随时知道那个位置是空闲的了。代码如下,通过代码可以比较直观的理解我的意思。
1 classitem (object):2 def __init__(self, data):3 self.data =data4 self.next =None5
6 classSLinkList(object):7 '''
8 静态链表,就是用数组来模拟链表,那么数组的下标就当做是节点的地址,9 这个概念是静态链表的核心10 '''
11 def __init__(self, size = 100):12 '''
13 初始化主要是用于初始化链表的大小,而非创建链表14 '''
15 self.link = [item(None) for i in range(size + 1)] #申请size大小的节点空间[0,1,2,...,size],其中下标0的节点作为头结点
16 self.link[0].next = None #表示空表
17 self.link[0].space = 1 #指向第一个节点,因为初始化时第一个节点为空闲节点
18
19 i = 1
20 while i <21 self.link i>
22 i += 1
23
24 self.link[i].next = None #空闲表尾指向None
25
26 self.length = 0 #链表长度
27 self.rear = 0 #表尾指针
二、空闲空间已经有了,那么要怎么管理这片空间呢?C语言中新建一个节点会先用malloc函数申请一个内存空间,删除一个节点我们会用free函数来释放内存,在静态链表中我们需要自己实现,因为之前头结点space永远都会指向一个空闲节点的位置,所有我设计的malloc_SL函数,直接从space获取一个空闲节点的位置,然后space再指向下一个空闲节点位置即可,而释放空间函数free_SL同理,先将待释放的点保存space指向的空闲位置,再将space指向该点即可模拟内存回收的过程。代码如下:
1 defMalloc_SL(self):2 '''
3 类似于C中malloc函数申请空间,返回空闲节点的下标4 '''
5 i =self.link[0].space6 ifself.link[0].space:7 self.link[0].space =self.link[i].next8
9 returni10
11 defFree_SL(self, k):12 '''
13 释放空间,并返回下标k节点的值14 '''
15 self.link[k].data =None16 self.link[k].next =self.link[0].space17 self.link[0].space = k
三、完成上面两点,静态链表的核心基本就说完了,其他的增删改查操作和c中的链表就差不多了,再提一次就是,静态链表的数组下标就相当于位置,地址!这里我再介绍一下的往链表末尾添加节点的操作函数Append(),流程就是先用Malloc_SL函数申请一个空间节点的位置,然后在该位置添加数据,next指向None,成为新的表尾,再将表尾指针指向该新加入的节点,表长length + 1,代码如下:
1 defAppend(self, data):2 '''往链表表尾添加元素, 并返回新添加元素的下标'''
3 node_index =self.Malloc_SL()4
5 if notnode_index:6 print("Append: NO SPACE!")7 returnFalse8
9 self.link[node_index].data =data10 self.link[node_index].next =None11 self.link[self.rear].next =node_index12 self.rear =node_index13 self.length += 1
14 return node_index
四、最后我实现了几个链表的操作,其他操作有读者自己去琢磨:
CreateSLinkList_R---->用尾插法插入元素,创建一个链表
CreateSLinkList_F---->用头插法插入元素,创建一个链表
DeleteElement--------->根据数据删除第一个匹配到的节点
InsertBefore------------>在第k个节点前插入新节点
Walk--------------------->遍历整个链表,并输出数据
Detail-------------------->输出各节点的详细信息
全部代码如下:
1 classitem (o