Python实现单链表

一:链表

常见的线性表有数组与链表。链表又可以分为单链表、双向链表、环形链表。今天我们主要来进行单链表的相关操作,包括增、删、查、改、链表的反转、链表的连接等。

二:链表 & 数组

链表作为数据结构的一种,与数组相比,它有什么优点与不足呢?
优点

  • 链表不占用连续的内存,采用离散的内存存储数据;数组采用一段连续的内存。
  • 在添加和删除数据时,对原有数据的移动较小;而数组则需要大量移动原有的数据(试想:如果在数组的中间插入一个元素,那么数组的后半部分都要往后移动一个单位)

不足

  • 链表在查询和遍历数据的时候比较慢,不像数组可以直接使用索引访问某个数据。

三:链表的构建

节点类
我们知道链表是由一个个节点连接而成的,所以我们先创建一个节点类

# Student类(节点类)         一个Student对象就是一个节点
class Student:
    def __init__(self,SchNum,name,score):
        self.SchNum = SchNum
        self.name = name
        self.score = score
        self.next = None

链表类
一个链表所需的属性有:头节点、尾节点、链表大小

# 链表类
class Link:

    # 构造函数
    def __init__(self):
        self.head = Student(None,None,None)            # 头节点为空
        self.tail = self.head
        self.size = 1

创建了链表我们还需要对它进行增、删、改、查等操作。如果一个链表连这些功能都无法实现的话,那么它的用处也就不大了。


增加元素
增加元素是将一个新的节点增加在链表的尾部,要增加一个节点,我们需要一下步骤:

  1. 将链表尾节点的下一个节点指向新节点
  2. 将新节点作为尾节点
  3. 链表的长度+1
# 添加节点
    def add(self,SchNum,name,score):
        stu = Student(SchNum,name,score)        # 创建新节点
        self.tail.next = stu                    # 尾节点的下一个节点为新节点
        self.tail = stu                         # 尾节点为新节点
        self.size = self.size + 1

插入节点
如果我们想要将一个节点插入到链表中,我们需要哪些步骤呢?

  1. 首先 ,推进到要插入的位置的前一个节点before处
  2. before.next为新节点
  3. 新节点.next为原链表before的下一个节点
  4. 链表的长度+1
   # 插入节点(此节点作为第index个节点)
    def insert(self,SchNum,name,score,index):
        if(index > self.size):
            print('链表还没有这么长哟!请输入小一点的整数......')
        else:
            stu = self.head
            insert_stu = Student(SchNum,name,score)
            for i in range(index - 1):
                stu = stu.next                      # 推进到要插入的位置
            insert_stu.next = stu.next
            stu.next = insert_stu
            self.size = self.size + 1

删除节点
我们假设一个链表结构 before—>delete—>after。如果我们想要删除delete节点,那么我们只需要将before的下一个节点指向after就可以了,然后链表长度-1

  # 删除节点(索引为index)
    def delete(self,index):
        if (index > self.size):
            print('链表还没有这么长哟!请输入小一点的整数......')
        else:
            stu = self.head
            for i in range(index - 1):
                stu = stu.next
            temp = stu.next
            stu.next = temp.next
            self.size = self.size - 1

改变指定位置节点的数据

# 改变指定节点的数据
    def change(self,SchNum,name,score,index):
        if (index > self.size):
            print('链表还没有这么长哟!请输入小一点的整数......')
        else:
            stu = self.head
            for i in range(index):                  # 推进到要改变节点的位置
                stu = stu.next
            stu.SchNum =SchNum
            stu.name = name
            stu.score =score

返回某节点的数据
要得到某节点的数据,我们只需推进到该节点的位置,然后获取它的data属性即可。

 # 返回节点的数据
    def getData(self,index):
        if(index > self.size):                                  # 判断是否超过链表的长度
            print('链表还没有这么长哟!请输入小一点的整数......')
        else:
            stu = self.head
            for i in range(index):
                stu = stu.next
            return [stu.SchNum,stu.name,stu.score]

获取链表的大小
这恐怕是链表中最简单的操作了,直接返回链表的size属性即可。

    # 返回节点的长度
    def getSize(self):
        return self.size


反转链表
这个操作比较复杂一点,但是也不要害怕哟。我们知道,在链表中,当前节点知道它的下一个节点在哪,但是对它的上一个节点毫不知情。所以如果要进行链表的反转,我们需要三个指针来对链表进行操作。

原地反转
prev指向头节点
pcur是当前需要反转的节点
dummy是头节点

步骤如下:

  1. prev连接下一次需要反转的节点
  2. 反转节点pcur
  3. 将dummy向后移动一位
  4. pcur指向下一次需要反转的节点
 # 反转链表(就地反转)
    def jiudi_invert(self,head):
        dummy = Student(None,None,None)
        dummy.next = head                       # 创建新节点,新节点的下一个指向头(head)节点
        prev = dummy.next                       # prev也指向头(head)节点
        pcur = prev.next                        # pcur是当前需要反转的节点

        while(pcur != None):                    # 循环条件:直到尾节点结束
            prev.next = pcur.next               # prev连接下一次需要反转的节点
            pcur.next = dummy.next              # 反转
            dummy.next = pcur                   # 纠正头节点的位置(将头节点向后移动一位)
            pcur = prev.next                    # pcur指向下一次需要反转的节点

        self.head = dummy.next  

Python实现单链表_第1张图片
新建链表,头节点插入法
pcur是要插入到新链表中的节点
prev是pcur的next

步骤如下:

  1. prev保存下一个要插入的节点
  2. 把pcur插入到新链表中
  3. 调整头节点的指向
  4. 下一个要插入的节点为prev
 # 反转链表(创建新链表,头节点插入法)
    def new_invert(self,head):
        dummy = self.head                       # 头节点当做新链表的尾节点
        pcur = head.next
        count = 0
        while(count < self.size - 1):
            prev = pcur.next                    # prev保存下一个要插入的节点
            pcur.next = dummy                   # 把pcur插入到新链表中
            dummy = pcur                        # 调整头节点的指向
            pcur = prev                         # 下一个要插入的节点为prev
            count = count + 1

        self.head = dummy

连接两个链表
这个操作也相对比较简单,直接将link1的尾节点的next指向link2的头节点就可以啦。

 # 连接两个链表
    def connectLink(self,link):
        self.tail.next = link.head.next
        self.size = self.size + link.size - 1       # 链表大小相加

四:代码示例



# Student类(节点类)         一个Student对象就是一个节点
class Student:
    def __init__(self,SchNum,name,score):
        self.SchNum = SchNum
        self.name = name
        self.score = score
        self.next = None

# 链表类
class Link:

    # 构造函数
    def __init__(self):
        self.head = Student(None,None,None)            # 头节点为空
        self.tail = self.head
        self.size = 1


    # 添加节点
    def add(self,SchNum,name,score):
        stu = Student(SchNum,name,score)        # 创建新节点
        self.tail.next = stu                    # 尾节点的下一个节点为新节点
        self.tail = stu                         # 尾节点为新节点
        self.size = self.size + 1


    # 插入节点(此节点作为第index个节点)
    def insert(self,SchNum,name,score,index):
        if(index > self.size):
            print('链表还没有这么长哟!请输入小一点的整数......')
        else:
            stu = self.head
            insert_stu = Student(SchNum,name,score)
            for i in range(index - 1):
                stu = stu.next                      # 推进到要插入的位置
            insert_stu.next = stu.next
            stu.next = insert_stu
            self.size = self.size + 1


    # 删除节点(索引为index)
    def delete(self,index):
        if (index > self.size):
            print('链表还没有这么长哟!请输入小一点的整数......')
        else:
            stu = self.head
            for i in range(index - 1):
                stu = stu.next
            temp = stu.next
            stu.next = temp.next
            self.size = self.size - 1


    # 改变指定节点的数据
    def change(self,SchNum,name,score,index):
        if (index > self.size):
            print('链表还没有这么长哟!请输入小一点的整数......')
        else:
            stu = self.head
            for i in range(index):                  # 推进到要改变节点的位置
                stu = stu.next
            stu.SchNum =SchNum
            stu.name = name
            stu.score =score


    # 返回节点的数据
    def getData(self,index):
        if(index > self.size):                                  # 判断是否超过链表的长度
            print('链表还没有这么长哟!请输入小一点的整数......')
        else:
            stu = self.head
            for i in range(index):
                stu = stu.next
            return [stu.SchNum,stu.name,stu.score]

    # 返回节点的长度
    def getSize(self):
        return self.size


    # 反转链表(就地反转)
    def jiudi_invert(self,head):
        dummy = Student(None,None,None)
        dummy.next = head                       # 创建新节点,新节点的下一个指向头(head)节点
        prev = dummy.next                       # prev也指向头(head)节点
        pcur = prev.next                        # pcur是当前需要反转的节点

        while(pcur != None):                    # 循环条件:直到尾节点结束
            prev.next = pcur.next               # prev连接下一次需要反转的节点
            pcur.next = dummy.next              # 反转
            dummy.next = pcur                   # 纠正头节点的位置(将头节点向后移动一位)
            pcur = prev.next                    # pcur指向下一次需要反转的节点

        self.head = dummy.next                  # 链表彻底反转完成,尾节点变为头节点

    # 反转链表(创建新链表,头节点插入法)
    def new_invert(self,head):
        dummy = self.head                       # 头节点当做新链表的尾节点
        pcur = head.next
        count = 0
        while(count < self.size - 1):
            prev = pcur.next                    # prev保存下一个要插入的节点
            pcur.next = dummy                   # 把pcur插入到新链表中
            dummy = pcur                        # 调整头节点的指向
            pcur = prev                         # 下一个要插入的节点为prev
            count = count + 1

        self.head = dummy


    # 连接两个链表
    def connectLink(self,link):
        self.tail.next = link.head.next
        self.size = self.size + link.size - 1       # 链表大小相加


def main():
    link = Link()               # 创建链表

    newlink = Link()            # 创建newlink,用作链表连接
    for i in range(5):
        newlink.add(i,'我是新链表!!!',i)

    print('添加节点 => 1 , 插入节点 => 2 , 删除节点 => 3 , 改变节点数据 => 4 , 遍历链表 => 5 , 获取链表长度 => 6')
    print('反转链表(就地反转) => 7 , 连接两个链表 => 8 , 反转链表(头节点插入法) => 9 , 退出 => 0')
    select = -1

    while select != 0:
        select = int(input('请选择:'))

        if(select == 1):                                    # 添加
            print('请输入相应数据')
            SchNum = int(input('学号:'))
            name = input('姓名:')
            score = input('成绩:')
            link.add(SchNum,name,score)
        elif(select == 2):                                  # 插入
            print('请输入相应数据')
            SchNum = int(input('学号:'))
            name = input('姓名:')
            score = input('成绩:')
            index = int(input('要插入的位置(整数):'))
            link.insert(SchNum,name,score,index)
        elif(select == 3):                                  # 删除
            index = int(input('要删除节点的索引:'))
            link.delete(index)
        elif(select == 4):                                  # 改变数据
            print('请输入相应数据')
            SchNum = int(input('学号:'))
            name = input('姓名:')
            score = input('成绩:')
            index = int(input('要改变的位置(整数):'))
            link.change(SchNum,name,score,index)
        elif(select == 5):                                  # 遍历
            print('学号------姓名--------成绩')
            for i in range(link.size):
                data = link.getData(i)
                print(str(data[0]) + '   ' + str(data[1]) + '   ' + str(data[2]))
        elif(select == 6):                                  # 获取长度
            print('链表的长度为:'+str(link.getSize()))
        elif(select == 7):
            link.jiudi_invert(link.head)
        elif(select == 8):
            link.connectLink(newlink)
        elif(select == 9):
            link.new_invert(link.head)


if __name__ == '__main__':
    main()

下面是运行结果:
Python实现单链表_第2张图片

你可能感兴趣的:(Python)