算法设计与分析——链表

分类目录:《算法设计与分析》总目录

链表是一种这样的数据结构,其中的各对象按线性顺序排列。数组的线性顺序是由数组下标决定的,然而与数组不同的是,链表的顺序是由各个对象里的指针决定的。链表为动态集合提供了一种简单而灵活的表示方法,并且能支持《算法设计与分析——栈和队列》中列出的所有操作。

如下图所示,双向链表 L L L的每个元素都是一个对象,每个对象有一个关键字 k e y key key和两个指针: n e x t next next p r e v prev prev。对象中还可以包含其他的辅助数据(或称卫星数据)。设 x x x为链表的一个元素,则 x . n e r t x.nert x.nert指向它在链表中的后继元素, x . p r e v x.prev x.prev指向它的前驱元素。如果 x . p r e v = N I L x.prev=NIL x.prev=NIL,则元素 x x x没有前驱,因此是链表的第一个元素,即链表的。如果 x . n e r t = N I L x.nert=NIL x.nert=NIL,则元素 x x x没有后继,因此是链表的最后一个元素,即链表的。属性 L . h e a d L.head L.head指向链表的第一个元素。如果 L . h e a d = N I L L.head=NIL L.head=NIL,则链表为空。
双向链表
链表可以有多种形式。它可以是单链接的或双链接的,可以是已排序的或未排序的,可以是循环的或非循环的。如果一个链表是单链接的,则省略每个元素中的 p r e v prev prev指针。

class Node(object):
    """双向链表的结点"""

    def __init__(self, item):
        # item存放数据元素
        self.item = item
        # next 指向下一个节点的标识
        self.next = None
        # prev 指向上一结点
        self.prev = None

如果链表是已排序的,则链表的线性顺序与链表元素中关键字的线性顺序一致;据此,最小的元素就是表头元素,而最大的元素则是表尾元素。如果链表是未排序的,则各元素可以以任何顺序出现。在循环链表中,表头元素的 p r e v prev prev指针指向表尾元素,而表尾元素的 n e x t next next指针则指向表头元素。我们可以将循环链表想象成一个各元素组成的圆环。在本文余下的部分中,我们假设所处理的链表都是未排序的且是双链接的。

class BilateralLinkList(object):
    """双向链表"""

    def __init__(self):
        self._head = None

    def is_empty(self):
        """判断链表是否为空"""
        return self._head is None

    def length(self):
        """链表长度"""
        # 初始指针指向head
        cur = self._head
        count = 0
        # 指针指向None 表示到达尾部
        while cur is not None:
            count += 1
            # 指针下移
            cur = cur.next
        return count

    def items(self):
        """遍历链表"""
        # 获取head指针
        cur = self._head
        # 循环遍历
        while cur is not None:
            # 返回生成器
            yield cur.item
            # 指针下移
            cur = cur.next

    def add(self, item):
        """向链表头部添加元素"""
        node = Node(item)
        if self.is_empty():
            # 头部结点指针修改为新结点
            self._head = node
        else:
            # 新结点指针指向原头部结点
            node.next = self._head
            # 原头部 prev 指向 新结点
            self._head.prev = node
            # head 指向新结点
            self._head = node

    def append(self, item):
        """尾部添加元素"""
        node = Node(item)
        if self.is_empty():  # 链表无元素
            # 头部结点指针修改为新结点
            self._head = node
        else:  # 链表有元素
            # 移动到尾部
            cur = self._head
            while cur.next is not None:
                cur = cur.next
            # 新结点上一级指针指向旧尾部
            node.prev = cur
            # 旧尾部指向新结点
            cur.next = node

    def insert(self, index, item):
        """ 指定位置插入元素"""
        if index <= 0:
            self.add(item)
        elif index > self.length() - 1:
            self.append(item)
        else:
            node = Node(item)
            cur = self._head
            for i in range(index):
                cur = cur.next
            # 新结点的向下指针指向当前结点
            node.next = cur
            # 新结点的向上指针指向当前结点的上一结点
            node.prev = cur.prev
            # 当前上一结点的向下指针指向node
            cur.prev.next = node
            # 当前结点的向上指针指向新结点
            cur.prev = node

    def remove(self, item):
        """ 删除结点 """
        if self.is_empty():
            return
        cur = self._head
        # 删除元素在第一个结点
        if cur.item == item:
            # 只有一个元素
            if cur.next is None:
                self._head = None
                return True
            else:
                # head 指向下一结点
                self._head = cur.next
                # 下一结点的向上指针指向None
                cur.next.prev = None
                return True
        # 移动指针查找元素
        while cur.next is not None:
            if cur.item == item:
                # 上一结点向下指针指向下一结点
                cur.prev.next = cur.next
                # 下一结点向上指针指向上一结点
                cur.next.prev = cur.prev
                return True
            cur = cur.next
        # 删除元素在最后一个
        if cur.item == item:
            # 上一结点向下指针指向None
            cur.prev.next = None
            return True

    def find(self, item):
        """查找元素是否存在"""
        return item in self.items()

	def search(k):
		"""搜索某个元素的key"""
		x = self._head
		while x != None and x != k:
			x = x.next
		return x

哨兵是一个哑对象,其作用是简化边界条件的处理。例如,假设在链表 L L L中设置个对象 L . n o n e L.none L.none,该对象代表 N o n e None None,但也具有和其他对象相同的各个属性。对于链表代码中出现的每一处对 N o n e None None的引用,都代之以对哨兵 L . n o n e L.none L.none的引用。如下图所示,这样的调整将一个常规的双向链表转变为一个有哨兵的双向循环链表,哨兵 L . n o n e L.none L.none位于表头和表尾之间。属性 L . n o n e . n e x t L.none.next L.none.next指向表头, L . n o n e . p r e v L.none.prev L.none.prev指向表尾。类似地,表尾的 n e x t next next属性和表头的 p r e v prev prev属性同时指向 L . n o n e L.none L.none。因为 L . n o n e . n e x t L.none.next L.none.next指向表头,我们就可以去掉属性 L . h e a d L.head L.head,并把对它的引用代替为对 L . n o n e . n e x t L.none.next L.none.next的引用。下图显示,一个空的链表只由一个哨兵构成, L . n o n e . n e x t L.none.next L.none.next L . n o n e . p r e v L.none.prev L.none.prev同时指向 L . n o n e L.none L.none.

有哨兵的双向循环链表
哨兵基本不能降低数据结构相关操作的渐近时间界,但可以降低常数因子。在循环语句中使用哨兵的好处往往在于可以使代码简洁,而非提高速度。我们应当慎用哨兵。假如有许多个很短的链表,它们的哨兵所占用的额外的存储空间会造成严重的存储浪费。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

von Neumann

您的赞赏是我创作最大的动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值