标签:lse vpd eth 移动 white 插入 ddl tle import
链式结构--单链表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) |
标签:lse vpd eth 移动 white 插入 ddl tle import
原文地址:http://blog.51cto.com/286577399/2149163