链表之双向链表

前面已经总结过单向链表了,现在总结一下双向链表。

链表之单向链表

单向链表和双向链表区别在哪?
单向链表,顾名思义,其方向是单向的,从一个节点只能获取下一个节点,无法获取节点的上一个节点的信息。
双向链表,其方向是双向的,通过一个节点,我们既可以获取该节点的下一个节点信息,亦可以获取上一个节点的信息

(一) 双向链表

结构

双向链表的结构如下图所示:节点方向是双向的,1节点是链表头部,4节点是链表尾部。
在这里插入图片描述
图 一 链 表 结 构 图 一 链表结构
如图二所示:每一个节点包含一个数据域和两个指针域,前面的指针域用于存放上一个节点的地址,后面的指针用于存放下一个节点的地址。图一中的1节点为头部节点,其前指针域为空,4节点为尾部节点,其后一个指针域为空。

在这里插入图片描述
图 二 节 点 结 构 图二 节点结构

(二) 编程实现

双向链表在实现的方式上与单向链表类似,只是有些许差别,通过下面编程可以看大。

(1) 双向链表的操作

下面一步一步的编程来实现双向链表

(2) 编程实现

(2.1)创建节点类

以下代码实现了创建节点的了,包含了数据域以及两个指针域。

class Node:
    """双向链表节点实现"""

    def __init__(self, data):
        """创建一个节点"""
        self.data = data   #数局域
        self.pre = None    #前指针域pre
        self.next = None   #后指针域next

(2.2)创建双向链表类

在代码实现时,我给链表添加了self.__tail 属性,表示尾部节点的指针,用于记录链表的尾部节点。这样这样的目的是是:在链表额一些操作,如尾部添加节点,删除节点,或者是反向遍历节点时,这个属性是很有用的,会让这个操作更加方便。

class doubleLinkedList(object):
    """双向链表的实现"""

    def __init__(self, node=None):
        self.__head = node  #链表尾部
        self.__tail = node  #链表头部

以下过程的函数均是在这个类中的。

(2.3)链表是否为空

与单向链表类似

    def is__empty(self):
        """链表是否为空"""
        return self.__head == None

(2.4) 链表长度

与单向链表类似

    def length(self):
        """链表长度"""
        count = 0
        curNode = self.__head
        while curNode:
            count += 1
            curNode = curNode.next
        return count

(2.5)链表的遍历(正向)

与单向链表类似

    def scan(self):
        """遍历链表"""
        curNode = self.__head
        if not curNode: return
        while curNode:
            print(curNode.data, end=" ")
            curNode = curNode.next

(2.6)双向链表的反向遍历

    def reverseScan(self):
        curNode = self.__tail
        while curNode:
            print(curNode.data, end=' ')
            curNode = curNode.pre

(2.7)链表头部添加节点

与单向链表类似。

    def add(self, item):
        """头部添加元素"""
        curNode = self.__head
        if not curNode:
            self.__head = Node(item)
            self.__tail = self.__head
            return
        else:
            node = Node(item)  # 创建节点
            node.next = self.__head
            self.__head.pre = node
            self.__head = node

(2.8)链表尾部添加节点

添加的方式与单向链接类似。需要注意的是,从尾部添加节点时,在节点添加完成以后,链表的尾部指针需要移动到新添加到尾部的节点

    def append(self, item):
        """尾部添加"""
        if self.is__empty():
            self.add(item)
            return
        node = Node(item)
        node.pre = self.__tail
        self.__tail.next = node
        self.__tail = node  # 链表的尾部变为新插入的节点

从上面代码可以看出,当链表中设计了self.__tail这个属性后,在尾部添加元素是非常方便快捷的。为什么呢?如果没有这个属性的话,当需要在尾部插入节点时,必须先遍历元素,找到尾部节点(尾部节点的next指针为None),然后开始插入,用大O表示法表示其时间复杂度的话,应该是O(n)。如果具有这个属性的话,无需遍历,其时间复杂度为O(1)

(2.9) 链表指定位置插入数据

该换算带有position这个位置参数,因此需要对其讨论,具有三种情况:

  1. position<=1 此时按照头部插入处理
  2. position>self.length()-1 ,即插入的位置大于链表的长度,按照尾部插入处理
  3. 中间插入。中间插入是不会影响尾部指针self.__tail的。

注意:position表示该节点是第position+1个元节点,其位置是position

    def insert(self, position, item):
        """指定位置插入链表"""

        """position:表示第position+1个元素"""

        if position <= 1:  # 按照头部插入处理
            self.add(item)
            return
        elif position > self.length() - 1:  # 如果插入的位置大于链表的长度,则按尾部插入处理
            self.append(item)
            return
        else:      #中间插入
            node = Node(item)
            count = 0
            targetNode = self.__head
            while count < position - 1:
                targetNode = targetNode.next
                count += 1
            # 结束循环后,targetNode指向position-1的位置
            node = Node(item)  # 创建新节点
            node.pre = targetNode
            node.next = targetNode.next
            targetNode.next.pre = node
            targetNode.next = node

(2.10)移除链表节点(按元素)

这些需要考虑三种情况:

  1. 需要移除的节点为头节点。删除头节点后,需要将头节点指针往后移动。
  2. 移除的节点为尾部节点。移除尾部节点后,需要将尾部节点指针往前移动。
  3. 移除的节点为中间节点
    def remove(self, item):
        targetNode = self.__head
        while targetNode:
            if targetNode.data == item:
                # 如果是删除的节点是头部
                if targetNode == self.__head:
                    self.__head = targetNode.next
                    self.__head.pre = None
                    return
                elif targetNode == self.__tail:  # 如果删除的是尾部节点
                    self.__tail = targetNode.pre
                    targetNode.pre.next = None
                    return
                targetNode.pre.next = targetNode.next
                targetNode.next.pre = targetNode.pre
                break
            targetNode = targetNode.next

(2.11) 查找节点是否存在(按元素)

与单向链表类似。

    def search(self, item):
        curNode = self.__head
        while curNode:
            if curNode.data == item:
                return True
            curNode = curNode.next
        return False

(三)完整代码

下面将完整的代码贴出来。

class Node:
    """双向链表节点实现"""

    def __init__(self, data):
        """创建一个节点"""
        self.data = data
        self.pre = None
        self.next = None


class doubleLinkedList(object):
    """双向链表的实现"""

    def __init__(self, node=None):
        self.__head = node
        self.__tail = node

    def is__empty(self):
        """链表是否为空"""
        return self.__head == None

    def length(self):
        """链表长度"""
        count = 0
        curNode = self.__head
        while curNode:
            count += 1
            curNode = curNode.next
        return count

    def scan(self):
        """遍历链表"""
        curNode = self.__head
        if not curNode: return
        while curNode:
            print(curNode.data, end=" ")
            curNode = curNode.next

    def reverseScan(self):
        """双向链表的反向遍历"""
        curNode = self.__tail
        while curNode:
            print(curNode.data, end=' ')
            curNode = curNode.pre


    def add(self, item):
        """头部添加元素"""
        curNode = self.__head
        if not curNode:
            self.__head = Node(item)
            self.__tail = self.__head
            return
        else:
            node = Node(item)  # 创建节点
            node.next = self.__head
            self.__head.pre = node
            self.__head = node

    def append(self, item):
        """尾部添加"""
        if self.is__empty():
            self.add(item)
            return
        node = Node(item)
        node.pre = self.__tail
        self.__tail.next = node
        self.__tail = node  # 链表的尾部变为新插入的节点

    def insert(self, position, item):
        """指定位置插入链表"""

        """position:表示第position+1个元素"""

        if position <= 1:  # 按照头部插入处理
            self.add(item)
            return
        elif position > self.length() - 1:  # 如果插入的位置大于链表的长度,则按尾部插入处理
            self.append(item)
            return
        else:      #中间插入
            node = Node(item)
            count = 0
            targetNode = self.__head
            while count < position - 1:
                targetNode = targetNode.next
                count += 1
            # 结束循环后,targetNode指向position-1的位置
            node = Node(item)  # 创建新节点
            node.pre = targetNode
            node.next = targetNode.next
            targetNode.next.pre = node
            targetNode.next = node

    def remove(self, item):
        targetNode = self.__head
        while targetNode:
            if targetNode.data == item:
                # 如果是删除的节点是头部
                if targetNode == self.__head:
                    self.__head = targetNode.next
                    self.__head.pre = None
                    return
                elif targetNode == self.__tail:  # 如果删除的是尾部节点
                    self.__tail = targetNode.pre
                    targetNode.pre.next = None
                    return
                targetNode.pre.next = targetNode.next
                targetNode.next.pre = targetNode.pre
                break
            targetNode = targetNode.next

    def search(self, item):
        curNode = self.__head
        while curNode:
            if curNode.data == item:
                return True
            curNode = curNode.next
        return False



#测试代码
dll = doubleLinkedList()

dll.add(1)  # 头部插入1
dll.append(2)  # 尾部插入2
dll.append(3)
dll.append(4)
dll.append(5)
dll.insert(3, 6)  # 3的位置插入6

print("链表数据:", end=" ")
print("链表长度:", dll.length())
print("遍历链表:", end='')
dll.scan()
print()
print("数据6是否存在:", end='')
print(dll.search(6))
print("删除元素5:")
dll.remove(5)
print("遍历链表:", end='')
dll.scan()
print()
print("反向遍历链表:", end='')
dll.reverseScan()

代码运行结果如图三所示,从显示结果上来看,以上代码应该是正确的。
在这里插入图片描述
图 三 运 行 结 果 图三 运行结果

如有不合适的地方,敬请指出,谢谢。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值