好久没有写了,今天来开个新坑。最近在看数据结构与算法,因为比较熟悉python语言,就选择了它的python语言版本来学习,现在就记录一下。
无序列表的实现
我们需要构造一个链表来实现无序列表。链表,顾名思义是前后两项之间有连接的数据结构,它不受限与连续的内存空间,而是通过每一个结点的指针域来找到它的下一个结点(后继),因此链表的增删十分方便,更改指针域指针的指向即可,而不用调整大量数据的存储位置。
构造结点类
首先需要构造链表中的基本元素:结点(Node)
class Node:
def __init__(self,initdata):
self.data = initdata
self.next = None
def getData(self):
return self.data
def getNext(self):
return self.next
def setData(self,newdata):
self.data = newdata
def setNext(self,newnext):
self.next = newnext
每个结点包括两个组成部分,数据(data)和指向它的后继的指针(next)。
构造无序列表类
接着就是无序列表(UnorderedList)本体的构造
class UnorderedList:
def __init__(self):
self.head = None
在构造链表时,头指针(head)是必须的,它指向链表中的第一个结点,如果是空链表就为None。有时,为了操作的统一,链表还会有头结点,头指针指向它,它的后继是链表的第一个结点,而它的数据域是无意义的,可以存放链表长度等公共信息。一个链表头指针必须有但头结点可以没有。
定义方法
接下来我们来定义无序列表的各种方法。
isEmpty
判断无序列表是否为空
add
向无序列表中增加元素
由于是无序列表,新增结点的位置无关紧要,并且链表本身维护了一个头指针,指向链表头部,所以直接在链表头部新增结点最简单。
注意这里必须先设定新增结点的后继为第一个结点,再更改头指针的指向,否则会导致链表其他结点丢失,无法访问。
size
返回无序列表的元素个数
最后一个结点的next是None,所以后继为None时表示已经到达列表尾部,循环结束。
search
查询无序列表中是否有指定元素
remove
移除无序列表中的指定元素
删除操作需要找到指定的元素,然后把它的前驱和它的后继连接起来,由于链表不能通过当前结点得到它的前驱,所以这里设置了一个previous变量,用来存储当前结点的前驱。
循环结束时如果previous仍然时None,说明我们要找的元素就在第一个位置,此时不能把它的前驱(previous)和后继(getNexTt)相连,因为此时previous是NoneType,并不是结点类。此时我们只要把链表头指针的指向设置为当前结点的后继即可。
这里我们假定指定的元素在无序列表中存在。
index
查询指定元素在无序列表中的索引
这里依然假定指定的元素在无序列表中存在。
insert
在无序列表的指定位置插入指定元素
这里通常的做法是和remove操作一样,设置一个previous,在找到指定位置后,把temp设为当前结点的前驱,previous的后继。也可以像我这样,做个小改动,把pos为0的情况单独处理,定位之后把temp设为当前结点的后继的前驱,当前结点的后继。注意这时需要向后多判断一个位置,即if i+1 == pos:
这里假定指定的位置不会超出无序列表的大小。
append
在无序列表尾部添加元素
通常的做法是以后继为None为终止条件循环找到链表尾,把新增结点设为尾部结点的后继。这样时间复杂度为O(n)
一种偷懒的做法是通过size()方法得到整个无序列表的长度,在通过insert()方法,以长度减1为参数在列表尾部插入新增元素,但这样更加耗时,因为有两个时间复杂度为O(n)的操作。
但是如果在无序列表类中多维护一个尾结点的属性,同时小改动一下add方法,就会简单很多。
这时append方法的时间复杂度仅为O(1)。
rear为None表示列表为空,此时add方法需要更新rear属性的值,并且append操作等价于add操作。
同时需要注意在append方法中设定rear的后继和更新rear的顺序,顺序相反会导致添加结点失败。
pop
从无序列表中删除并返回指定位置的元素,如果不指定位置,则删除并返回最后一个元素
和删除操作一样,这里需要新增一个previous变量来记录当前结点的前驱。定位后previous仍然为None时说明当前时链表头部,更改头指针的指向即可。
这里可以体现出python动态语言的方便之处,如果是其他静态语言,我们需要定义两个函数pop(self)和pop(self,pos)用来实现两个不同情况下的操作。这里我们只需要将pos参数设置默认为-1来表示不指定位置在尾部操作的情况,因为指定的位置不可能是-1。
注意不指定位置参数在尾部操作时需要更新尾结点rear属性。
这样我们就基本实现了无序列表的构造。但是python内置的list并不是这样的,从方法的时间复杂度上可以看出,我们的pop()方法是O(n),但python内置list的pop()方法是O(1)。