前情提要:Python数据结构与算法_8_链表、无序链表
接下来我们研究有序链表。
什么是有序链表
如果给定一个链表,他的节点数据元素都是的整数,如77, 26, 31, 93, 17, 54。如果这些链表里的节点是以升序排列的有序链表,那么它会被写作17, 26, 31, 54, 77, 93。由于 17 是最小的元素,因此它就成了链表的第一个元素(视为节点)。同理,由于 93 是最大的元素,因此它就会在链表的最后一个位置。
在有序链表中,元素的相对位置取决于它们的基本特征。它们通常以升序或者降序排列,并且我们假设元素之间能进行有意义的比较。有序链表的众多操作与无序链表的相同。
实现有序链表(OrderedLinkList)
我们来思考一下有序链表的基本方法:
- 首先是创建一个空的有序链表。它不需要参数,且会返回一个空链表。OrderedLinkList()
- 向链表中添加 item,同时保持整个链表的顺序。它接受一个元素作为参数,无返回值。add(item)
- 如果 item 已经在链表中,从链表中移除 item。如果不在链表中则返回 False。它接受一个元素作为参数,并且修改链表。remove(item)
- 在链表中搜索 item 。 它接受一个元素作为参数,并且返回布尔值。search(item)
- 检查链表是否为空。它不需要参数,并且返回布尔值。isEmpty()
- 返回链表中元素的个数。它不需要参数,并且返回一个整数。length()
在实现有序链表时必须记住,元素的相对位置取决于它们的基本特征。整数有序链表 17, 26, 31, 54, 77, 93 可以用如下图所示的链式结构来表示。
相比于无序链表,有序链表节点的构造方法并不发生变化,如下:
# 构造节点
class Node:
def __init__(self, initdata):
self.data = initdata # 数据元素
self.next = None # 指向下一节点的引用(python变量赋值的本质是引用)
def getData(self): # 获取节点的数据元素值
return self.data
def getNext(self): # 获取下一节点的引用 若有节点则直接视作为下一节点
return self.next
def setData(self, newdata): # 给节点的数据元素设置值
self.data = newdata
def setNext(self, newnext): # 给节点设置对下一节点的引用
有序链表与无序链表的构造方法是相同的,都是 head 引用指向 None,代表是一个空链表。如下所示:
class OrderedLinkList:
def __init__(self):
self.head = None
因为 isEmpty 和 length 仅与链表中的节点数目有关,而与实际的元素值无关,所以这两个方法在有序链表中的实现与在无序链表中一样。同理,由于仍然需要找到目标元素并且通过更改链接来移除节点,因此 remove 方法的实现也一样。
isEmpty、length、remove 方法实现如下:
class OrderedLinkList:
def __init__(self):
self.head = None
def isEmpty(self):
return self.head == None
def length(self):
current = self.head
count = 0
while current != None:
count = count + 1
current = current.getNext()
return count
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 current == None:
return False
if previous == None:
self.head = current.getNext()
else:
previous.setNext(current.getNext())
剩下的两个方法,search 和 add ,都需要做一些修改。
在无序链表中搜索时,需要逐个遍历节点,直到找到目标节点或者没有节点可以访问。这个方法同样适用于有序链表,但前提是链表必须包含目标元素。如果目标元素不在链表中,可以利用元素有序排列这一特性尽早终止搜索(代码第12~13行)。
search 方法实现如下:
class OrderedLinkList:
def __init__(self):
self.head = None
def search(self, item):
current = self.head
found = False
while current != None and not found:
if current.getData() == item:
found = True
else:
if current.getData() > item:
return False
else:
current = current.getNext()
return found
需要修改最多的是 add 方法。对于无序链表,add 方法可以简单地将一个节点放在链表的头部,这是最简便的访问点。不巧,这种做法不适合有序链表。我们需要在已有链表中为新节点找到正确的插入位置。
假设要向有序链表 17, 26, 54, 77, 93 中添加 31。add 方法必须确定新元素的位置在 26 和 54 之间。下图展示了我们期望的结果。像之前解释的一样,需要遍历链表来查找新元素的插入位置。当访问完所有节点(current指向None )或者当前值大于要添加的元素时,就找到了插入位置。在本例中,遇到 54 使得遍历过程终止。
add 方法实现代码如下:
class OrderedLinkList:
def __init__(self):
self.head = None
def add(self, item):
current = self.head
previous = None
stop = False
while current != None and not 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)
和无序链表一样,由于 current 无法提供对将要修改节点的访问,因此使用额外的引用 previous 是十分必要的。上述代码中第 7~8 行初始化两个外部引用,第 13~14 行保证 previous 一直跟在current 后面。只要还有节点可以访问,并且当前节点的值小于或等于要插入的元素,判断条件就会允许循环继续执行。在循环停止时,就找到了新节点的插入位置。
一旦创建了新节点,唯一的问题就是它会被添加到链表的开头还是中间某个位置。previous == None (第17行)提供了解决方法。