python语言实现双向链表

python语言实现双向链表

双向链表顾名思义,存在两个指针头指针尾指针,分别指向前驱节点和后续节点。


双向链表所包含的操作如下:

  • is_empty() 链表为空
  • length() 链表长度
  • trvael() 便利链表
  • add(item) 头部添加元素
  • append(item) 尾部添加元素
  • insert(pos, item) 指定位置添加元素
  • remove(item) 删除节点
  • search(item) 查找元素

相对于单向链表的区别:

  • 1.多了头指针,指向前驱节点
  • 2.添加、删除节点与单链表不同,判空、长度、遍历、查找都相同,具体代码请参照此链接
  • 3.删除节点操作需要考虑多种情况,删除头结点或尾节点需判断后继节点是否为空

添加节点

头部添加节点

头部添加节点步骤如下:

  • 1.将新节点尾指针指向原头结点
  • 2.不为空链表时,原有头结点头指针指向新节点
  • 3.头结点变更为新节点

头插法

代码如下:

def add(self, item):
    '''头部添加元素'''
    node = Node(item)
    # 先让新节点指向头结点后部分的链表,保证后面不丢失
    node.next = self._head
    # 考虑特殊情况空链表,此时头结点为None,没有前指针,指向node会报错
    if self._head is not None:
        self._head.pre = node
    # 再更换头结点
    self._head = node

头插注意点有两处:

  • 1.考虑链表为空

此时头结点为空,没有头指针、尾指针,所以为空时以下代码会报错:

self._head.pre = node

采用的解决方案是利用头节点的头指针时先判断头结点是否为空(若为空,则表明是空链表),不为空在利用头结点的头指针,为空添加的节点就是头结点,无需再为其头指针分配指向。

  • 2.添加节点时需分配后继节点的前指针

尾部插入节点

尾部插入节点步骤如下:

  • 1.判断是否为空节点,若为空节点直接添加作为头结点。
  • 2.若不为空节点,遍历寻找尾节点,尾节点尾指针指向新节点,
  • 3.新节点头指针指向原尾节点。

尾插法

代码如下:

def append(self, item):
    '''尾部添加元素'''
    node = Node(item)
    # 判断是否为空链表,为空直接添加作为头结点
    if self.is_empty():
        self._head = node
    else:
        cur = self._head
        # 寻找尾节点
        while cur.next != None:
            cur = cur.next
        # 尾节点尾指针指向新节点
        cur.next = node
        # 新节点头指针指向原尾节点
        node.prev = cur

指定位置插入节点

指定位置插入节点步骤如下:

  • 1.判断位置是否合理,小于等于1作为头插,大于链表长度作为尾插。
  • 2.遍历找到指定位置索引,将节点插到位置索引之前。
  • 3.建立连接,新节点尾指针指向原节点,原节点头指针指向新节点,这里原节点是指索引节点和索引节点的前驱节点。

新节点与原节点连接方式如下图:
指定位置插入

代码如下:

def insert(self, pos, item):
    '''指定位置添加'''
    # 当pos=1时,也是头插
    if pos <= 1:
        self.add(item)
    # 当pos大于等于尾节点索引时为尾插,索引从0开始,length-1即为尾部节点位置索引
    elif pos >= self.length()-1:
        self.append(item)
    else:
        node = Node(item)
        cur = self._head
        count = 0
        # 寻找要插入的位置游标
        while count < pos:
            count += 1
            cur = cur.next
        # 新节点尾指针,指向索引节点
        node.next = cur
        # 新节点头指针,指向索引节点前驱节点
        node.prev = cur.prev
        # 索引节点头指针,指向新节点
        cur.prev = node
        # 索引节点前驱结点尾指针,指向新节点
        cur.prev.next = node

注意,新节点与原有节点连接顺序并无强制要求,按照便于理解的方式来编写即可。

删除节点

双向链表节点变动都要考虑头指针和尾指针,当要删除的节点为头结点时,若此时链表只有一个节点,后继节点为None,不存在头指针,此时引用会报错当要删除的节点为尾节点时,后继节点也为None,无需考虑后继节点前指针。所以这两种情况作为特殊情况单独考虑。

双向链表删除节点步骤如下:

  • 1.遍历链表,当前节点值等于要删除的值时:判断是否为头结点,若是头结点再判断是否链表只包含单个节点,判断完毕进行节点删除。
  • 2.非头结点时判断是否为尾节点,判断完毕进行节点删除。
  • 3.删除节点操作为:
    • 1.删除节点前驱节点的尾指针,指向删除节点的后驱节点。
    • 2.删除节点后驱节点的头指针,指向删除节点的前驱节点。

删除节点的操作如下图:
删除节点

代码如下:

def remove(self, item):
    '''删除节点'''
    cur = self._head
    # 遍历链表
    while(cur != None):
        # 找到需删除的节点时
        if cur.elem == item:
            # 考虑头结点情况
            if cur == self._head:
                self._head = cur.next
             # 若链表不止包含一个节点,及cur节点的后继节点不为None
                if cur.next:
                    cur.next.prev = None
            else:
                cur.prev.next = cur.next
                # 若不为尾节点,不为尾节点时cur的后继节点不为None
                if cur.next:
                    cur.next.prev = cur.prev
            break
        # 没找到需删除的元素时,指针后移
        else:
            cur = cur.next

ALL IN

完整代码如下:

class Node(object):
    """定义节点"""
    def __init__(self, item):
        self.elem = item
        self.next = None
        self.prev = None


class DoubleLinkList(object):
    """双链表,添加、删除节点与单链表不同,判空、长度、遍历、查找都相同"""
    def __init__(self, node=None):
        self._head = node

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

    def length(self):
        '''返回链表长度'''
        # cur用来移动遍历数组
        cur = self._head
        # count记录数量
        count = 0
        while cur != None:
            count += 1
            cur = cur.next
        return count

    def trval(self):
        '''遍历列表'''
        # cur用来移动遍历数组
        cur = self._head

        while cur != None:
            print(cur.elem, end=" ")
            cur = cur.next

    def add(self, item):
        '''头部添加元素'''
        node = Node(item)
        # 先让新节点指向头结点后部分的链表,保证后面不丢失
        node.next = self._head
        # 考虑特殊情况空链表,此时头结点为None,没有前指针,指向node会报错
        if self._head is not None:
            self._head.pre = node
        # 再更换头结点
        self._head = node

        #node.next.prev = node
    def append(self, item):
        '''尾部添加元素'''
        node = Node(item)
        # 判断是否为空链表,为空直接加到头结点后面
        if self.is_empty():
            self._head = node
        else:
            cur = self._head
            # 找到尾结点,添加到尾结点后面
            while cur.next != None:
                cur = cur.next
            cur.next = node
            node.prev = cur


    def insert(self, pos, item):
        '''指定位置添加'''
        # 当pos=1时,也是头插
        if pos <= 1:
            self.add(item)
        elif pos >= self.length()-1:
            self.append(item)
        else:
            node = Node(item)
            cur = self._head
            count = 0
            # 寻找要插入的位置游标
            while count < pos:
                count += 1
                cur = cur.next
            cur.prev.next = node
            cur.prev = node
            node.next = cur
            node.prev = cur.prev
            

    def remove(self, item):
        '''删除节点'''
        #pre = None
        cur = self._head
        while(cur != None):
            if cur.elem == item:
                # 考虑头结点情况
                if cur == self._head:
                    self._head = cur.next
                    # 判断链表是否只有一个节点
                    if cur.next:
                        cur.next.prev = None
                else:
                    cur.prev.next = cur.next
                    # 尾结点单独考虑
                    if cur.next:
                        cur.next.prev = cur.prev
                break
            else:
                #pre = cur
                cur = cur.next

    def search(self, item):
        '''查找节点是否存在'''
        cur = self._head
        while(cur != None):
            if cur.elem == item:
                return  true
            else:
                cur = cur.next
        return False


if __name__ =="__main__":
    
    ll = DoubleLinkList()
    print(ll.is_empty())
    ll.add(4)
    ll.add(1)
    ll.append(2)
    ll.insert(1,3)
    ll.add(4)
    ll.add(5)
    ll.insert(5,333)
    ll.trval()

对于双链表,需注意节点变动要同时考虑头指针和尾指针,还要考虑是否存在后继节点,不存在后继节点则相关指针也不存在,节点变动时不予考虑。
哈哈哈

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值