数据结构——03—单向链表

顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来并不是很灵活。
链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。

链表的定义
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)。
链表分为三类:单向链表,双向链表,循环链表。在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实现单个节点的代码如下:

# 单个节点实现
class SingleNode(object):
    
    # 单链表的节点
    def __init__(self, item):
        
        # 存放数据元素
        self.item = item
        
        # next下一个节点的标识
        self.next = None              # 实现单个节点,暂定为None
        

这里需要注意的是最后一句:self.next = None 这段代码是完成单个节点的编写,所以下一个节点的地址设定为None

实现链表的代码如下:

# 链表实现
class SingleLinkList(object):
    # 单链表  
    # 空的单链表
    # 非空单链表
    def __init__(self, node=None):
        self.__head = node
    

self.__head是指代链表的首地址值
这里需要注意的是参量node=None,这样写是为了防止是空链表的情况,假如不传参量node,就是空链表,此时默认为None,传参之后,self.__head=node,得到新链表。

实现链表的增删改查等操作的函数:
1、判断链表是否为空的函数

# 判断链表是否为空
    def is_empty(self):
        # if self.__head == None:
        #     return True
        # else:
        #     return False
        
        return self.__head == None   # 如果self.__head == None,返回true,否则返回False

这里需要注意的是,注释掉的是复杂版本的函数,简介版本代码就是
return self.__head == None,同样能达到要求。

2、计算链表长度函数

# 链表长度
    def length(self):
        # cur游标  指向首节点,用来遍历  相当于指针
        cur = self.__head  # 游标指向首地址
        
        count = 0          # 正常情况下count=1     空链表的情况下 count只能为0
        
        while cur != None:
            count += 1
            # 将游标后移动一位
            cur = cur.next         # AttributeError: 'builtin_function_or_method' object has no attribute 'next'    这个错误是上面单节点的设置中cur.next设置出错
        return count

出现过一个错误就是# AttributeError: ‘builtin_function_or_method’ object has no attribute ‘next’
原因就是单节点实现的过程中的self.next 的值设定出错。

3、遍历链表函数

 # 遍历整个链表
    def travel(self):
        cur =self.__head
        
        while cur != None:
            # 节点中的数据 item
            print(cur.item, end=' ')       # 防止第一个节点数据不会被打印,这个语句应该放在cur = cur.next前面
            cur = cur.next
        print(" ")        # 输出换行

这里注意的是 print(cur.item, end=’ ')语句和cur = cur.next语句的顺序。
在这里插入图片描述

4、链表尾部添加元素函数

# 链表尾部添加元素
    def append(self, item):
        # 链表尾部添加元素数据
        # item 要插入的数据
        node = SingleNode(item)
        
        # 考虑空链表时候
        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head    # 如果是空链表,此时cur为None
            
            # 如果链表本身是空的,添加数据就会报错,:
            # AttributeError: 'NoneType' object has no attribute 'next'
            while cur.next != None:
                # 找到最后一个节点
                cur = cur.next
            # 将添加后的元素的地址标志
            cur.next = node

尾部添加元素函数实现的示意图:
在这里插入图片描述

5、链表头部添加元素函数

# 链表头部添加元素
    def add(self, item):
        node = SingleNode(item)   # 创建新节点,即要查入的新的元素
        
        # 第一步:将新的节点的地址指向初始链表的第一个元素
        node.next = self.__head
        
        # 第二步:将新链表的首地址指向新添加元素
        self.__head = node

头部添加元素示意图:
在这里插入图片描述

6、指定链表的位置添加元素函数

# 指定链表的位置添加元素    pos 指定位置
    def insert(self, pos , item):
        node = SingleNode(item)  # 创建新节点   item是插入的值
        
        # 考虑特殊情况
        # 头部插入
        if pos <= 0:
            self.add(item)
        # 尾部插入
        elif pos > self.length()-1:
            self.append(item)
        else:
            # 指定位置插入
            '''
            # 第一步:先执行将新节点的地址指向插入的后一项节点数据
            node.next = cur.next
            # 第二步:将插入元素的前一项的地址指向新元素
            cur.next = node
            '''
            # 引入一个新的游标pre,指代插入元素的前一项,则将上面的语句修改
            
            pre = self.__head    # 将pre当作新的游标进行使用,cur这个游标是代表插入数据的
            count = 0            # 需要用到count与插入位置做对比
            while count < (pos-1):     # 为了判断游标pre循环遍历停止的位置,停止在哪,就在哪里插入数值item
                count += 1
                pre = pre.next
            
            # 第一步:当前插入元素的地址指向之前指向的后一项元素地址
            node.next = pre.next
            # 第二步:前一项数据的地址指向新元素
            pre.next = node
    

指定链表的位置添加元素示意图:
在这里插入图片描述

7、删除节点函数

# 删除节点
    def remove(self, item):
        cur = self.__head
        pre = None    # 最开始的游标地址指向pre,默认地址为None
        
        while cur != None:
            if cur.item == item:   # 找到了指定元素
                # 删除第一个元素的情况  找到第一个地址
                if cur == self.__head:
                    self.__head = cur.next
                else:
                    # 删除元素
                    # 将删除元素的前一项的地址指向当前要删除元素指向的地址
                    pre.next = cur.next
                    # pre.next = pre.next.next     与上式表达意思相同
                break
                
            else:    # 继续循环遍历查找,游标地址值往后移动
                pre = cur
                cur = cur.next
        

删除节点示意图:
在这里插入图片描述

8、查找节点是否存在函数

 # 查找节点是否存在
    def search(self, item):
        cur = self.__head
        
        while cur != None:    # 不能使用cur.next!=None,防止尾节点查找不到    尾节点的next为None,此时尾节点可能会退出循环,不被执行
            if cur.item == item:
                return True
            else:
                # 游标继续执行 查询下一个节点
                cur = cur.next
        return False
        

9、实现链表整体代码如下:

# -*- encoding: utf-8 -*-
"""
@File    : single_link_list.py
@Time    : 2019/11/9 21:04
@Author  : chen

"""
'''
单链表
节点实现
链表实现
'''
# 节点实现
class SingleNode(object):
    
    # 单链表的节点
    def __init__(self, item):
        
        # 存放数据元素
        self.item = item
        
        # next下一个节点的标识
        self.next = None              # 实现单个节点,暂定为None
        
# 链表实现
# [] append empty
class SingleLinkList(object):
    # 单链表
    
    # 空的单链表
    # 非空单链表
    def __init__(self, node=None):
        self.__head = node
    
    # 判断链表是否为空
    def is_empty(self):
        # if self.__head == None:
        #     return True
        # else:
        #     return False
        
        return self.__head == None   # 如果self.__head == None,返回true,否则返回False
    
    # 链表长度
    def length(self):
        # cur游标  指向首节点,用来遍历  相当于指针
        cur = self.__head  # 游标指向首地址
        
        count = 0          # 正常情况下count=1     空链表的情况下 count只能为0
        
        while cur != None:
            count += 1
            # 将游标后移动一位
            cur = cur.next         # AttributeError: 'builtin_function_or_method' object has no attribute 'next'    这个错误是上面单节点的设置中cur.next设置出错
        return count

    # 遍历整个链表
    def travel(self):
        cur =self.__head
        
        while cur != None:
            # 节点中的数据 item
            print(cur.item, end=' ')       # 防止第一个节点数据不会被打印,这个语句应该放在cur = cur.next前面
            
            cur = cur.next
        print(" ")        # 输出换行
    
    # 链表尾部添加元素
    def append(self, item):
        # 链表尾部添加元素数据
        # item 要插入的数据
        node = SingleNode(item)
        
        # 考虑空链表时候
        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head    # 如果是空链表,此时cur为None
            
            # 如果链表本身是空的,添加数据就会报错,:
            # AttributeError: 'NoneType' object has no attribute 'next'
            while cur.next != None:
                # 找到最后一个节点
                cur = cur.next
            # 将添加后的元素的地址标志
            cur.next = node
    
    # 链表头部添加元素
    def add(self, item):
        node = SingleNode(item)   # 创建新节点,即要查入的新的元素
        
        # 第一步:将新的节点的地址指向初始链表的第一个元素
        node.next = self.__head
        
        # 第二步:将新链表的首地址指向新添加元素
        self.__head = node
    
    # 指定链表的位置添加元素    pos 指定位置
    def insert(self, pos , item):
        node = SingleNode(item)  # 创建新节点   item是插入的值
        
        # 考虑特殊情况
        # 头部插入
        if pos <= 0:
            self.add(item)
        # 尾部插入
        elif pos > self.length()-1:
            self.append(item)
        else:
            # 指定位置插入
            '''
            # 第一步:先执行将新节点的地址指向插入的后一项节点数据
            node.next = cur.next
            # 第二步:将插入元素的前一项的地址指向新元素
            cur.next = node
            '''
            # 引入一个新的游标pre,指代插入元素的前一项,则将上面的语句修改
            
            pre = self.__head    # 将pre当作新的游标进行使用,cur这个游标是代表插入数据的
            count = 0            # 需要用到count与插入位置做对比
            while count < (pos-1):     # 为了判断游标pre循环遍历停止的位置,停止在哪,就在哪里插入数值item
                count += 1
                pre = pre.next
            
            # 第一步:当前插入元素的地址指向之前指向的后一项元素地址
            node.next = pre.next
            # 第二步:前一项数据的地址指向新元素
            pre.next = node
    
    # 删除节点
    def remove(self, item):
        cur = self.__head
        pre = None    # 最开始的游标地址指向pre,默认地址为None
        
        while cur != None:
            if cur.item == item:   # 找到了指定元素
                # 删除第一个元素的情况  找到第一个地址
                if cur == self.__head:
                    self.__head = cur.next
                else:
                    # 删除元素
                    # 将删除元素的前一项的地址指向当前要删除元素指向的地址
                    pre.next = cur.next
                    # pre.next = pre.next.next     与上式表达意思相同
                break
                
            else:    # 继续循环遍历查找,游标地址值往后移动
                pre = cur
                cur = cur.next
        
    # 查找节点是否存在
    def search(self, item):
        cur = self.__head
        
        while cur != None:    # 不能使用cur.next!=None,防止尾节点查找不到    尾节点的next为None,此时尾节点可能会退出循环,不被执行
            if cur.item == item:
                return True
            else:
                # 游标继续执行 查询下一个节点
                cur = cur.next
        return False
        
        
        
        

# 一个节点存放了参量100,实例化
# node = SingleNode(100)



s = SingleLinkList()
# 测试
print(s.is_empty())
print(s.length())

s.append(1)
print(s.is_empty())
print(s.length())

# 链表尾部添加元素
s.append(2)
s.append(5)
s.append(9)
s.append(11)
print(s.is_empty())
print(s.length())
s.travel()

# 链表头部添加元素
s.add(10)
s.travel()

# 插入新数据
s.insert(-1, 555)    # 头部插入
s.insert(2, 333)     # 指定位置插入
s.insert(10, 999)    # 尾部插入
s.travel()

# 测试查找和删除
s.remove(999)        # 删除尾部元素
s.remove(555)        # 删除头部元素
s.remove(000)        # 删除不存在元素
s.travel()
print(s.search(1))
print(s.search(666))

对比链表和顺序表
在这里插入图片描述

发布了50 篇原创文章 · 获赞 9 · 访问量 2104

猜你喜欢

转载自blog.csdn.net/weixin_42118531/article/details/102991463