链表是一种无序列表,无序列表只需要维持元素之间的相对位置,但是并不需要在连续的内存空间中维护这些位置信息。
链表里的元素连接时靠每一个元素都维护一份信息,该信息就是下一个元素的位置,那么这些元素的相对位置就能通过指向下一个元素的链接来表示。
但是我们必须要指明链表中的第一个元素的位置,一旦知道之后,就可以访问下一个元素,指向链表的第一个元素的引用叫做头(head)
节点(node)时构建链表的基本数据结构,每一个节点对象都有两份信息,数据变量,即该节点的元素,和指向下一个节点的引用。
下面的代码我们展示了Node类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
如上所述,无序列表(unordered list)是基于节点集合来构建的,每一个节点都通过显式的引用·来指向下一个节点,只需要知道第一个节点的位置,其后的元素就可以一一找到。因此,它的里面必须包含指向第一个节点的引用
class UnorderedList:
def __init__(self):
self.head = None
开始的时候链表为空。在链表中,用None来表明没有元素。
下面的代码检查是否为空
def isEmpty(self):
return self.head == None
要将元素加入链表中,需要实现add方法,但是需要知道新的元素要被放在链表的哪个位置?由于链表是无序的,因此新元素相对于已有元素的位置并不重要,新的元素可以放在任何位置。因此,将新元素放在最简便的位置是最合理的选择。
由于链表只提供一个入口(头部),因此其他所有节点都只能通过第一个节点以及next链接来访问,意味着添加新节点的最简便的位置就是头部。
我们把新元素放在链表的第一个元素,已有的元素连接到该元素的后面
def add(self, item):
temp = Node(item)
temp.setNext(self.head)
self.head = temp
我们必须先将新的节点的next引用指向房前链表中的第一个节点,才能开始修改头节点,使其指向新创建的节点。如果顺序颠倒,由于头节点是唯一指向列表节点的外部引用,因此所有的已有节点都将丢失并且无法访问。
为了实现length方法,我们必须遍历链表并且记录访问过多少节点。下列代码展示了如何遍历节点。
def length(self):
current = self.head
count = 0
while current != None:
count +=1
current = current.getNext()
return count
current的作用就是外部引用,他在第二行被初始化为链表的头节点,计算开始时,没有访问任何节点,所以count初始化为一。
在链表中实现搜索也要进行遍历技术,每访问一个节点,检查该节点的元素与要搜索的是否相同。`
def search(self, item):
current= self.head
found = False
while current != None and not found:
if current.getData() == item:
found = True
else:
current = current.getNext
return found
remove方法在逻辑上分为两步,第一步遍历列表并查找要移除的元素,找到后要将它移除,第二步要修改上一个节点的链接,使它指向下下一个节点。这正是个难点,因此我们在这使用双指针的方法,我们创建一个新的变量previous使得它总是指向current上一次访问的节点,这样一来,就可以进行修改,代码如下:
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())
必须要将previous移动到current的节点然后再移动current,这一过程称为蠕动。