Python数据结构与算法_9_有序链表

前情提要: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

因为 isEmptylength 仅与链表中的节点数目有关,而与实际的元素值无关,所以这两个方法在有序链表中的实现与在无序链表中一样。同理,由于仍然需要找到目标元素并且通过更改链接来移除节点,因此 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行)提供了解决方法。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值