4. 单链表
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4. 单链表相关的知识,希望对你有一定的参考价值。
链式结构--单链表
1.线性结构和链式结构的区别:
线性结构内存是连续的,可以通过下标访问的
链式结构内存是不连续的,无法通过下标访问
(链式结构append时会很方便,find的时候会很麻烦,因为要从头到尾的遍历、寻找)
通过链接指针的方式,指向下一个。
2.单链表(LinkedList)
由一个一个节点通过指针的方式,把节点给串起来,root节点是一个入口,可以去遍历。
每一块区域都会代表一个值,节点称之为node。
每个节点除了value外,还要一个指针指向下一个元素,如图:节点
这里通过class来定义一个node属性,它包含值,还包含指向下一个元素的指针,
单链表的结构如下:
data:root(入口)、length(长度)
linkedlist
method:init(初始化的方法)、append(追加元素)、appendleft(左侧追加元素)、iter_node(遍历节点)、remove(删除及诶单)、find(查找节点)、popleft(删掉头节点)
代码实现:
class Node(object): #Node节点 def __init__(self, value=None, next=None): self.value, self.next = value, next #多重赋值 class LinkedList(object): def __init__(self, maxsize=None): #maxsize表示最大容量,None表示无限扩容 self.maxsize = maxsize #赋值 self.root = Node() #需要一个根节点,是一个空节点 self.length = 0 #需要一个长度,默认是0 self.tailnode = None def __len__(self): #魔术方法,定义len方法 return self.length #这里返回记录的长度值 def append(self, value): if self.maxsize is not None and len(self) > self.maxsize: #条件 raise Exception('LinkedList Is Full') #抛出异常 node = Node(value) #构造新加入的节点,并赋值为value tailnode = self.tailnode if tailnode is None: #这个表明尾节点是None,则说明链表只有root节点这一个节点 self.root.next = node #把root的next指针指向新加入的节点 else: tailnode.next = node #当尾节点不是None的时候,将尾节点的next指针指向新加入节点 self.tailnode = node #更新链表,现在新加入的节点是链表的尾节点 self.length += 1 #将链表长度值加1 def appendleft(self, vlaue): #定义往左边插入节点,位置为root节点的后面,实现往左边插入的操作 headnone = self.root.next #定义头节点为root节点的下一个节点 node = Node() #构造新加入的节点 self.root.next = node #把root的next节点指向新加入的节点 node.next = headnone #把新加入的节点的next节点指向原来的头节点 #这两步就把链表串起来了,实现在root节点后面和原头节点前面添加节点 def iter_node(self): #遍历,从头节点,遍历到尾节点 curnode = self.root.next #从第一个节点遍历,curnode为root节点的下一个节点 while curnode is not self.tailnode: #只要curnode不是尾节点 yield curnode #就一直yield当前节点 curnode = curnode.next #更新curnode为下一节点 yield curnode #这个while循环只移动到了最后一个节点,然后还要把当前节点更新到最后一个节点, #也要把他yield出来,这样就实现了完整的遍历操作。 def __iter__(self): for node in self.iter_node(): yield node.value def remove(self, value): #查找并删除这个节点,这里是O(n),必须从头查到尾,没法通过下标查找到 prevnode = self.root #定义prevnode为根节点 #curnode = self.root.next for curnode in self.iter_node(): #遍历链表,当前节点为curnode if curnode.value == value: #如果当前遍历到的curnode的值和要删除的值一致 prevnode.next = curnode.next #将prevnode的next指针指向到当前要删除的curnode节点的下一个节点 if curnode is self.tailnode: #如果这个curnode刚好是尾节点 self.tailnode = prevnode #则将prevnode就是尾节点 del curnode #删除节点 self.length -= 1 #长度减1 return 1 #表明删除成功 else: #否则如果当前遍历到的curnode的值不是要删除的值 prevnode = curnode #则更新prevnode的位置到遍历的这个非删除的节点,并继续遍历下一个curnode节点 return 1 #删除完成返回1 def find(self, value): #查找操作,也是要从头遍历,这也是单链表查找比较慢的原因,时间复杂度O(n) index = 0 #索引从0开始 for node in self.iter_node(): #遍历链表 if node.value == value: #如果要查找的值等于这个node的值 return index #返回这个node的索引 index += 1 #索引+1 return -1 #没有找到指定的值则返回-1 def popleft(self): #相当于把第一个节点删掉 if self.root.next is None: #如果root节点的下一个节点是None raise Exception('Pop From Empty') #抛出异常 headnode = self.root.next #将root的下一个节点定义为头节点 self.root.next = headnode.next #再将root的next指针指向上面头节点的下一个节点 self.length -= 1 #将长度减1 value = headnode.value #同pop操作最后会返回一个值,并打印出来 del headnode #删除原来的头节点 return value #返回被popleft掉的值 def clean(self): #清空操作,清空链表 for node in self.iter_node(): #遍历节点,并删除遍历到的节点 del node self.root.next = None #将root节点的下一个节点指向空 self.length = 0 #将长度值置0 #单元测试 def test_linked_list(): lst = LinkedList() lst.append(0) lst.append(1) lst.append(2) assert len(lst) == 3 assert lst.find(2) == 2 assert lst.find(3) == -1 lst.remove(0) assert len(lst) == 2 assert lst.find(0) == -1 assert list(lst) == [1, 2] #断言list只有[1, 2] lst.appendleft(0) assert list(lst) == [0 ,1 ,2] assert len(lst) == 3 headvalue = lst.popleft() #验证popleft方法 assert headvalue == 0 assert len(lst) == 2 assert list(lst) == [1, 2] lst.clean() #清空链表 assert len(lst) == 0
# pytest linked_list.py
执行测试
平均时间复杂度:
链表操作 | 平均时间复杂度 |
---|---|
linked_list.append(value) | O(1) |
linked_list.appendleft(value) | O(1) |
linked_list.find(value) | O(n) |
linked_list.remove(value) | O(n) |
以上是关于4. 单链表的主要内容,如果未能解决你的问题,请参考以下文章