一、回顾列表
首先,在创建链表之前,先来回顾回顾列表的创建,列表的创建在计算机的内存当中要开辟一块连续的内存空间,在找其中的元素的时候,直接通过列表当中的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()
创建完成!