python-想学好链表吗?从手动创建链表开始

一、回顾列表

首先,在创建链表之前,先来回顾回顾列表的创建,列表的创建在计算机的内存当中要开辟一块连续的内存空间,在找其中的元素的时候,直接通过列表当中的index即可,
在这里插入图片描述

那么我们可以来分析一下列表当中的增、删、查操作的时间复杂度,

如果是在列表末尾添加
那时间复杂度为O(1)
如果是在列表末尾删除
那时间复杂度为O(1)
列表的查找操作不论从哪个地方找
时间复杂度都为O(1)
如果是在列表中间某个位置添加
那时间复杂度为O(n)
如果是在列表中间某个位置删除
那时间复杂度为O(n)

那为什么在列表当中执行增加或删除的操作的时候时间复杂度会为O(n),可以参考下图,在列表进行中间位置插入元素的时候,首先计算机会开辟出原来列表大小2倍的内存空间,然后在新的空间中操作,为了防止元素被覆盖,值为40的元素会先向后移,然后值为30的元素会向后移,将下标为3的位置空出,然后将准备插入的值传进去,这样的话插入的操作就完成了。原来的旧列表会被python内部的垃圾回收机制处理掉。因此, 这样的元素移动操作带来的时间复杂度即为O(n)

删除操作与之同理,删掉其中的某个中间位置的元素的之后,后面的元素会向前移动一个位置,同样时间复杂度就成了O(n)
在这里插入图片描述


二、了解链表

列表回顾完之后,咱们进入正题,列表的创建是在内存空间当中是连续的,但是链表的创建在内存空间当中是不连续的,它是由多个节点连接而成,节点当中除了有具体的value之外,还存有指向下一个节点位置的指针,通过指针的链接,即可构成链表,链表的末尾节点指向None,这样构成了单向链表
在这里插入图片描述
链表不同于列表的一个明显特征就是列表当中我们可以随机访问,但是链表不允许,只能从头一个一个遍历到尾

当链表末尾节点的指针指向了头结点,那这个链表就变成了循环链表
在这里插入图片描述
如果节点当中出现了指向前一个节点的指针,那就变成了双向链表
在这里插入图片描述
链表了解了之后咱们来看看它的各项操作的时间复杂度各是多少:

插入删除查找
不论是从末尾插入还是从中间某个位置插入
时间复杂度都为O(1)
不论是从末尾删除还是从中间某个位置删除
时间复杂度都为O(1)
O(n)

这些都清楚之后,咱们来创建一个链表,实现增删查的操作
创建链表需要用到两个类,一个类用来创建节点,一个类用来创建链表
下面的链表创建只是单向链表,如果要创建双向链表的话那就需要Node类加入prev指针,创建LinkedList的时候需要加上尾节点tail

这个链表当中的操作一共是分为5个部分

插入删除查询打印链表获取链表长度
在链表头部插入节点
将头结点的next指向要插入的节点即可
删除头部节点
如果链表当中只有一个节点,那将头部节点的next指向None即可
如果有多个节点,将头部节点的next指向next的next即可
查询头部节点
直接返回节点的value
依次向后遍历,找到一个节点就打印一个节点,直到next为None 每次进行增删节点的时候都会相对应的增加或减少size,直接打印size即可
在链表尾部插入节点
在链表中一个个依次向后遍历,找到next为None的节点,将这个节点的next指向要插入的节点
删除尾部节点
同样依次向后一个个遍历,当节点的next的next为空时,将该节点的next指向空即可
查询尾部节点
依次向后遍历,当节点的next为空时,输出该节点的值
在链表指定位置插入节点
在链表中一个个依次向后遍历,找到指定的位置,将这个节点的next指向当前节点的next,当前节点的next指向要查入的节点
删除指定位置节点
同样依次向后一个个遍历,当找到对应的位置的时候,此时的节点是要删除的上一个节点,上一个的节点的next直接指向next的next即可
查询指定节点
依次向后遍历,到达指定位置的之后,返回节点的值

三、具体代码实现

class Node():

    def __init__(self, value = None, next = None):
        '''
        创建节点
        :param value:value初始值为None
        :param next: next初始值为None
        '''
        self.value = value
        self.next = next

class LinkedList():

    def __init__(self):
        self.head = Node()  # 创建头结点
        self.size = 0  # 链表初始长度为0

    # insert node at first  —— O(1)
    def insert_first(self, value):
        node = Node(value)
        node.next = self.head.next  # 第一步,要插入的node的next指向head的next
        self.head.next = node  # 第二步,head节点的next指向要插入的node
        # 别忘了链表的规模加一
        self.size += 1

    # insert node at last  —— O(n)
    def insert_last(self, value):
        # 创建一个node,用node从链表头部遍历到尾部,不要用自身的head
        new_node = Node(value)  # 要插入的node
        node = self.head
        while node.next != None:
            node = node.next
        node.next = new_node
        self.size += 1

    # insert the node to random position  —— O(n)
    def insert_at(self, value, pos):
        new_node = Node(value)  # 创建一个新节点
        node = self.head
        if pos < 0 or pos > self.size:  # 检查插入位置是否不符合条件
            raise IndexError
        for i in range(pos):  # 这个遍历只是为了找到插入的位置而已
            node = node.next
        new_node.next = node.next
        node.next = new_node
        self.size += 1

    # remove the first node of LinkedList —— O(1)
    def remove_first(self):
        if not self.head.next :
            raise ValueError('the LinkedList is empty')
        self.head.next = self.head.next.next
        self.size -= 1

    # remove the last node of LinkedList —— O(n)
    def remove_last(self):
        node = self.head
        if not node.next:
            raise ValueError('the LinkedList is empty')
        while node.next.next != None:
            node = node.next
        node.next = None
        self.size -= 1

    # remove the node from random position  —— O(n)
    def remove_at(self, pos):
        node = self.head
        if pos < 0 or pos >self.size:
            raise IndexError
        for i in range(pos):
            node = node.next
        node.next = node.next.next
        self.size -= 1

    # peek the first node in LinkedList  —— O(1)
    def peek_first(self):
        node = self.head
        if not node.next :
            raise ValueError('the LinkedList is empty')
        print('the first node of LinkedList:%s'%node.next.value)

    # peek the last node in LinkedList —— O(n)
    def peek_last(self):
        node = self.head
        if not node.next:
            raise ValueError('the LinkedList is empty')
        while node.next != None:
            node = node.next
        print('the last node of LinkedList:%s'%node.value)


    # peek the node of random position  —— O(n)
    def peek_at(self, pos):
        node = self.head
        if pos < 0 or pos > self.size:
            raise IndexError
        if not node.next:  # 如果为空,输出链表为空
            raise ValueError('LinkedList is empty')
        for i in range(pos):
            node = node.next
        print('the %s node of LinkedList:%s'%(pos,node.value))


    # print LinkedList —— O(n)
    def print_linkedList(self):
        node = self.head
        print('the LinkedList:', end=' ')
        while node.next != None:
            node = node.next
            print(node.value, end = ' ')
        print()

    # return the size of LinkedList
    def length(self):
        print('the size of LinkedList:%s'%self.size)

# 创建一个链表
Ll1 = LinkedList()
# 插入操作
Ll1.insert_first(8)
Ll1.insert_last(5)
Ll1.insert_first(20)
Ll1.print_linkedList()
Ll1.insert_at(2,1)
Ll1.print_linkedList()
# 查询操作
Ll1.peek_first()
Ll1.peek_last()
Ll1.peek_at(3)
# 删除操作
Ll1.remove_at(2)
Ll1.print_linkedList()
Ll1.remove_first()
Ll1.print_linkedList()
Ll1.length()
Ll1.remove_last()
Ll1.print_linkedList()
Ll1.length()

在这里插入图片描述
创建完成!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值