(Python3)数据结构——04.单链表的原理及实现

前言

  • 有python基础
  • 当然,要是学过数据结构自然是最好的

原理

在这里插入图片描述

  • 图片来自某度,图中是几种常见的链表,接下来讨论最常用的单链表
  • 链表有数据域和指针域(或者说是链接域),数据域item,链接域next。在C语言中,next是一个指针,用来指向下一个节点。同样的,在Python中,next存在的意义也是为了标记下一个节点。
  • 链表是由一堆节点组合起来(或者说串起来)的,由于有next,因此链表的数据不需要存在一块连续的存储空间,可以分散存储,毕竟通过next可以找到。

实现

  • 链表涉及的操作有点多,前面栈、队列该有的它基本上都有。
  • 初始化:由于链表是由节点串起来的,节点又不是Python里面自带的数据类型,因此还是定义一个节点类,在它的构造函数里面加上item和next属性
class Node(object):
    def __init__(self, item):
        self.item = item		# node的item值为传入的item
        self.next = None		# node初始化时next为空,即没有后续节点
  • 有了节点之后就可以定义链表了,链表的属性写一个头结点即可,初始化的时候依旧是设置为空,建议head设置为私有属性
class Link(object):
    def __init__(self):
        self.__head = None
  • 虽然和栈、队列这些数据结构一样有某些函数(判空、遍历等),但是写法上有些不同。判空函数在链表中是看头结点是不是为空,而在栈和队列中一般是看items的长度是不是为0或者items这个list是不是为空。
    def isEmpty(self):
        return self.__head is None
  • 在求size的时候,队列是在数元素个数,链表是在数节点的个数(元素个数也对,因为传入的元素item后面生成节点,item的数目等于节点数)
    def size(self):
        count = 0
        cur = self.__head		# 当头结点为空的时候,不进行下面的while循环,直接返回0
        while cur:
            count += 1
            # 计数完之后,cur更新为后续的节点,当cur为空的时候就会退出循环
            cur = cur.next		
        return count
  • 遍历:从头结点开始,当头结点为空时输出为空,不为空时将节点的item依次打印
  def travel(self):
        cur = self.__head
        while cur:
            print(cur.item, end=' ')
            cur = cur.next
        print('')
  • 栈的插入方式是对栈顶元素操作,而队列是队尾插入,双向队列则是队头队尾都可以。(数据结构中的队列不考虑中间插队的情况)而在链表中,可以头部加入,也可以尾部加入,甚至可以中间插入。
  • 头部插入:要插入什么元素?因此需要有一个参数接收想插入的元素。接收到参数后,生成一个节点,这个节点生成的时候调用class Node的构造函数,item为传入的元素,next为None,即后续节点为空。要使得这个节点插入到链表头,即使得这个新的节点成为新的头头,就应该使得这个节点的next指向当前的头结点,之后再把头结点更新即可。
  • 灵魂画手重出江湖

在这里插入图片描述

	# 头部插入
    def addFront(self, item):
        node = Node(item)
        node.next = self.__head
        self.__head = node
  • 尾部插入:尾部插入的时候需要注意的一点是怎样找到尾部

在这里插入图片描述

	# 尾部插入
    def addBack(self, item):
        node = Node(item)
        if self.__head is None:
            self.__head = node
            return
        cur = self.__head
        pre = None
        while cur:
            pre = cur
            cur = cur.next 
            # 当cur 为最后一个节点时带入,pre更新为最后一个节点,cur更新为最后一个节点的下一个节点即为空,
            # 下一次while cur 时会退出循环,此时的pre表示的就是最后一个节点,将node挂到pre的后面即可
        pre.next = node
  • 任意位置的插入:需要1个代表位置的参数,和一个item。插入节点, 节点在插入后是第 pos 个节点,当然这个函数也可以实现头部插入和尾部插入的功能。故pos取值1时,即头部插入,取值size+1时是尾部插入。因此取值的合法范围是[1,size + 1]。
  • 找到插入位置的前一个节点,这个节点假设为a,a.next 为b,要插入的节点为node。那么,需要将a.next = node ; node.next = b这样即可实现插入。

在这里插入图片描述

	# 插入(这一块代码自己觉得还是可以改进)
    def insert(self, pos, item):
        if pos > (self.size() + 1) or pos < 1:
            return
        if pos == 1:
            self.addFront(item)
            return
        node = Node(item)
        cur = self.__head
        pre = None
        for i in range(pos - 1):
            pre = cur
            cur = cur.next
        pre.next = node
        node.next = cur
  • 接下来看下删除的部分
  • 头部删除:将原来的头结点的下一个节点设置为头结点,将原来头结点的next设置为None
    在这里插入图片描述
	# 删除头部节点
    def removeFront(self):
        cur = self.__head
        self.__head = self.__head.next
        cur.next = None
  • 删除尾部节点:空的时候不用管,只有一个的时候将头结点置于None即可,其余情况如图
    在这里插入图片描述
# 删除尾部节点
    def removeBack(self):
        # 空节点时
        if self.__head is None:
            return
        # 只有一个节点
        if self.__head and self.__head.next is None:
            self.__head = None
            return
        # 链表节点有两个及以上
        cur = self.__head 	 	# 当前节点
        pre = None 		 # 前一个节点
        cn = cur.next 		 # 后一个节点
        # 刚开始cur取到的是第一个节点,cn是第二个
        while cn:
        	# 将cur 取值为倒数第二个节点即cn 为最后一个节点带入
            pre = cur			# (接上面注释)更新为倒数第二个
            cur = cur.next		# (接上面注释)更新为最后一个
            cn = cur.next		# (接上面注释)更新为None,下一次就不会进行while循环
        pre.next = None		# 将倒数第二个节点的next 设置为None就连不上原来的最后一个了
  • 查找链表中有没有item,有返回True,没有则返回False
	# 查找链表中有没有item,有返回True,没有则返回False
    def search(self, item):
        cur = self.__head
        res = False
        while cur:
            if cur.item == item:
                res = True
                break
            else:
                cur = cur.next
        return res
  • 删除指定数值的节点:当找到指定数值的节点cur时,将pre节点的next指向cur.next即可
    在这里插入图片描述
# 删除指定数值的节点
    def delete(self, item):
        if self.__head is None:
            return
        if self.__head.item == item:
            self.__head = None
            return
        cur = self.__head.next  # 取第二个节点
        pre = self.__head  # 第一个节点
        while cur:
            if cur.item == item:
                pre.next = cur.next
                break
            else:
                pre = cur
                cur = cur.next
  • 大致方法有这些,文中如若有写错的地方还望批评指出。有些地方或许有更好的实现方式,可以多试试。
  • 完整代码
class Node(object):
    def __init__(self, item):
        self.item = item
        self.next = None


class Link(object):
    def __init__(self):
        self.__head = None

    def isEmpty(self):
        return self.__head is None

    # 头部插入
    def addFront(self, item):
        node = Node(item)
        node.next = self.__head
        self.__head = node

    # 尾部插入
    def addBack(self, item):
        node = Node(item)
        if self.__head is None:
            self.__head = node
            return
        cur = self.__head
        pre = None
        while cur:
            pre = cur
            cur = cur.next
            # 当cur 为最后一个节点时带入,pre更新为最后一个节点,cur更新为最后一个节点的下一个节点即为空,
            # 下一次while cur 时会退出循环,此时的pre表示的就是最后一个节点,将node挂到pre的后面即可
        pre.next = node

    def size(self):
        count = 0
        cur = self.__head
        while cur:
            count += 1
            cur = cur.next
        return count

    def travel(self):
        cur = self.__head
        while cur:
            print(cur.item, end=' ')
            cur = cur.next
        print('')

    # 删除头部节点
    def removeFront(self):
        cur = self.__head
        self.__head = self.__head.next
        cur.next = None

    # 删除尾部节点
    def removeBack(self):
        # 空节点时
        if self.__head is None:
            return
        # 只有一个节点
        if self.__head and self.__head.next is None:
            self.__head = None
            return
        # 链表节点有两个及以上
        cur = self.__head  # 当前节点
        pre = None  # 前一个节点
        cn = cur.next  # 后一个节点
        # 刚开始cur取到的是第一个节点,cn是第二个
        while cn:
            pre = cur
            cur = cur.next
            cn = cur.next
        pre.next = None

    # 查找链表中有没有item,有返回True,没有则返回False
    def search(self, item):
        cur = self.__head
        res = False
        while cur:
            if cur.item == item:
                res = True
                break
            else:
                cur = cur.next
        return res

    # 删除指定数值的节点
    def delete(self, item):
        if self.__head is None:
            return
        if self.__head.item == item:
            self.__head = None
            return
        cur = self.__head.next  # 取第二个节点
        pre = self.__head  # 第一个节点
        while cur:
            if cur.item == item:
                pre.next = cur.next
                break
            else:
                pre = cur
                cur = cur.next

    # 插入节点, 节点在插入后是第 pos 个节点,当然这个函数也可以实现头部插入和尾部插入的功能
    # 故pos取值1时,即头部插入,取值size+1时是尾部插入。因此取值的合法范围是[1,size + 1]
    def insert(self, pos, item):
        if pos > (self.size() + 1) or pos < 1:
            return
        if pos == 1:
            self.addFront(item)
            return
        node = Node(item)
        cur = self.__head
        pre = None
        for i in range(pos - 1):
            pre = cur
            cur = cur.next
        pre.next = node
        node.next = cur


ll = Link()
print(ll.isEmpty())		# 刚开始是空的,应为True
for i in range(5):
    ll.addFront(i)
print('size:', ll.size())		# 加入了5个,应为5
ll.travel()		# 打印输出,由于是头部添加,因此打印的应该是43210(忽略空格)
for i in range(5):
    ll.addBack(i)
ll.travel()		# 尾部添加,打印的应是一个对称的序列,4321001234
ll.removeFront()		# 删除头部元素,下面打印应是321001234
ll.travel()
print('----')
ll.removeBack()
ll.travel()		# 删除尾部元素,下面打印应是32100123
ll.insert(9, 12)		# 插入后,下面打印出来的第九个是12
ll.travel()
print(ll.search(13))		# 没有,应该是False
print(ll.search(1))			# 有的,应是True
ll.delete(2)		# 删除找到的第一个2
ll.travel()
  • 结果

在这里插入图片描述

发布了28 篇原创文章 · 获赞 12 · 访问量 4127

猜你喜欢

转载自blog.csdn.net/sf9898/article/details/104946291