什么是列表?
列表是一种简单强大的数据及结构,提供了丰富的操作接口
一种数据项按照相对位置存放的数据集,被称为无序表
例如:考试分数集合 “54,26,93,17,77,31”
如果用无序表表示就是[54,26,93,17,77,31]
无序表的操作
- List() : 创建一个空列表
- add(item) : 添加一个数据项
- remove() : 从列表移除
- search() : 在列表中查找item
- isEmpty() : 返回列表是否为空
- size() : 返回列表包含了多少数据项
- append(item) : 添加一个数据项到末尾
- index(item) : 返回数据项在表中的位置
- insert(pos,item) : 将数据项插入到pos位置
- pop() : 从列表末尾移除
采用链表实现无序表
为了实现无序表,可以采用链表的方案。虽然列表数据结构要求保持数据项的前后相对位置,但这种前后位置的保持,并不要求数据项一次存放在连续的存储空间
如下图,数据项存放位置并没有规则,但如果在数据项之间建立链向指引,就可以保持其前后相对位置
链表的实现:节点Node
链表实现的最基本元素是节点,每个节点包含两个信息:数据项本事,以及指向下一个节点的引用信息
注意next为None的意义是没有下一个节点
可以采用连接节点的方式构建数据集来实现无序表
链表第一个和最后一个节点很重要,如果访问到链表中的所有节点,就必须从第一个节点开始沿着链表遍历下去
所以无序表必须要有对第一个节点的引用信息,设立一个属性head,保存对第一个节点的引用空表的head和None
随着数据项的加入,无序表的head始终指向链条的第一个节点
接下来,考虑如何实现向无序表中添加数据项,实现add方法
由于无序表并没有限定数据项之间的顺序,新加入的数据项可以加入到原表的任何位置,按照性能考虑,应添加到最容易加入到的位置上
由链表结构我们知道,要访问到整条链上的所有数据项,都必须从表头head开始沿着next链接逐个向后查询,所以添加新数据项最快捷的位置是表头,整个链表的首位置
add方法
按照下图的代码调用,形成链表,30是最先加入数据项,所以称为链表最后一个。
而54是最后加入的所以是第一个数据项
链表的实现:size
size:从链表头head开始遍历到表尾同时变量累加经过的节点个数
def size(self):
current = self.head
count = 0
while current != None
count = count + 1
current = current.getNext()
return count
search
从链表头head开始遍历到表尾,同时判断当前节点的数据项是否目标
def search(self, item):
current = self.head
found = False
while current != None and not fount:
if current.getData() == item:
found = True
else:
current = current.getNext()
return found
remove(item)
首先要找到item。这个过程跟search一样,但在删除节点时,需要特别的技巧
current指向的是当前匹配数据项的节点
而删除需要把前一个节点的next指向current的下一个节点
所以我们在search current的同时,还要维护前一个(previous)节点的引用
找到item之后,current指向item节点,previous指向前一个节点,开始执行删除,需要区分两种情况 1.current是首个节点;2.位于链条中间的节点
def remove(self, item):
current = self.head
previous = None
found = False
while not found:
if current.getData() == item:
found = True
else:
previous = current
current = current.getNext()
if previous == None:
self.head = current.getNext()
else:
previous.setNext(current.getNext())
有序表 OrderedList
什么是有序表?
有序表是一种数据项依照其某可比性质(如整数大小,字母先后顺序)来决定在列表中的位置
越“小”的数据项越靠近列表的头,越靠“前”
基本操
- OrderedList() : 创建一个空列表
- add(item) : 添加一个数据项
- remove(item) : 从列表移除
- search(item) : 在列表中查找item
- isEmpty() : 返回列表是否为空
- size() : 返回列表包含了多少数据项
- pop(pos) : 从列表指定位置移除
在实现有序表的时候,需要记住的是,数据项的相对位置,取决于他们之间的‘大小’比较
以整数数据项为例,(17,26,31,54,77,93)的链表形式如图
有序表的实现
同样采用链表方法实现
Node定义相同
OrderedList也设置一个head来保存链表表头的引用
class OrderedList:
def __init__(self):
self.head = None
对于isEmpty/size/remove这些方法,与节点的次序无关,所以其实现跟UnorderedList是一样的
search/add方法则需要有修改
search
在无序表的search中,如果需要查找数据项不存在,则会搜索整个链表,知道表尾
对于有序表来说,可以利用链表节点有序排列的特性,来为search节省不存在数据项的查找时间
一旦当前节点的数据项大于所要查找的数据项,则说明链表后面的已经不可能又要查找到的数据项,可以直接返回False
如我们要在下图查找数据项45
def search(self, item):
current = self.head
found = False
stop = False
while current != None and not fount and not stop:
if current.getData() == item:
found = True
else:
if current.getData() > item:
stop = True
else:
current = current.getNext()
return found
add方法
相比无序表,改变最大的方法add,因为add方法必须保证加入的数据项添加在合适的位置,已维护整个链表的有序性
比如在(17,26,54,77,93)的有序表中,加入数据项31,我们需要沿着链表,找到第一个比31大的数据项54,将31插入到54的前面
由于涉及到的插入位置是当前节点之前,而链表无法得到“前驱”节点的引用
所以要跟remove方法类似,引入一个previous的引用,跟随当前节点current
一旦找到首个比31大的数据项,previous就派上用场了
代码实现:
def add(self, item):
current = self.head
previous = None
stop = False
while current != None and stop:
if current.getData() > item:
stop =True
else:
previous = current
current = current.getNext()
temp = Node(item)
if previous == None:
temp.setNext(self.head)
self.head = temp
else:
temp.setNext(current)
previous.setNext(temp)
链表实现的算法分析
对于链表复杂度的分析,主要是看相应的方法是否涉及到链表的遍历
对于一个包含节点数为n的链表
isEmpty是O(1),因为仅需要检查head是否为None
size是O(n),因为除了遍历到表尾,没有其它办法得知节点的数量
search/remove以及有序表的add方法,则是O(n),
因为涉及到链表的遍历,按照概率其平均操作的次数是n/2
无序表的add方法是O(1),因为仅需要插入到表头
链表实现的List,跟Python内置的列表数据类型,在有些相同方法的实现上的时间复杂度不同
主要是因为Python内置的列表数据类型是基于顺序存储来实现的,并进行了优化。