链表(三)
2019-10-11 本文已影响0人
小董不太懂
单向链表
class Node():
'''节点'''
def __init__(self,elem):
self.elem = elem
self.next = None
class SingleLinkList(object):
'''单链表'''
def __init__(self,node=None):
#内部私有函数,链表头的初始化,可以直接建立空链表
self.__head = node
def is_empty(self):
"""判断链表是否为空"""
return self.__head == None
def length(self):
"""返回链表的长度"""
#cur游标,用来移动遍历节点
#count,用来记录节点数量
count = 0
cur = self.__head
while cur != None:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历链表"""
cur = self.__head
while cur != None:
print(cur.elem,end=' ')
cur = cur.next
def add(self, item):
"""头部添加节点"""
node = Node(item)
node.next = self.__head
self.__head = node
def append(self, item):
"""尾部添加节点"""
node = Node(item)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
def insert(self, pos, item):
"""在指定位置添加节点"""
pre = self.__head
count = 0
if pos <= 0:
self.add(item)
elif pos >= (self.length()-1):
self.append(item)
else:
while count < (pos-1):
count += 1
pre = pre.next
node = Node(item)
node.next = pre.next
pre.next = node
def remove(self, item):
"""删除一个节点"""
cur = self.__head
pre = None
while cur != None:
if cur.elem == item:
#先判断是否为头节点,然后再去处理头节点
if cur == self.__head:
self.__head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
def search(self, item):
"""查找节点是否存在"""
cur = self.__head
while cur != None:
if cur.elem == item:
return True
else:
cur = cur.next
return False
根据这段代码,结合下图,是不是进一步加深了我们对时间复杂度的理解。
- 链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。
- 注意虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。
双向链表
100节点是40节点的前驱节点,6节点是40节点的后继节点。一种更复杂的链表是“双向链表”或“双面链表”。每个节点有两个链接:一个指向前一个节点,当此节点为第一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值。
代码实现,我们就在单向链表的基础上更改
-
我们先看看双链表的构造
是不是和单链表如出一辙,无需更改程序。
-
我们看看双链表的探空功能:
这样是不是又和单链表一样了。
- 我们看看求长度的功能:
我们只需要计数,是不是完全可以把它当成单链表,只需要一个游标next就可以实现求长度啊,
遍历的travel和求长度一样都可以看作是单链表,简单粗暴。
-
我们看看从头部添加的功能:
我们是不是要先操作新节点啊
这样就可以实现。
但如果用代码实现的话,可以有两种方式,与指向的前后顺序相关:
1 方法一
def add(self, item):
"""头部添加节点"""
node = Node(item)
node.next = self.__head
self.__head = node
node.next.prev = node
2 方法二
def add(self, item):
"""头部添加节点"""
node = Node(item)
node.next = self.__head
self.__head.prev = node
self.__head = node
-
实现链表尾部添加元素的功能:
尾插法是不是首先要定位啊,定位到最后一个元素,这个过程双链表和单链表是没有区别的,定位到最后一个元素后 只要实现这样的指向就OK了吧,那要怎么实现呢。
def append(self, item):
"""尾部添加节点"""
node = Node(item)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
node.prev = cur
考虑一下特殊情况,加入这个链表为空链表呢,程序也是没问题的。
-
实现insert插入功能:
我们先截个单链表中的insert代码 我们看看双链表中需要怎么更改呢? 这一部分是不需要更改的吧,位置小于等于0我们就调用头插法,pos大于等于(length() - 1,我们就调用尾插法。唯一需要更改的就是,最后的指向问题。 这么一来是不是就一目了然
- 先操作node节点,让node节点分别指向节点6和节点40,node.next = cur,node.prev = cur.prev
-
然后让节点40和节点6分别对接node节点,cur.prev.next = node,cur.prev = node
这么写也可以。
那么完整的代码怎么实现呢,显然需要做一些更改,首先pre这个游标就不需要了,我们只需要一个cur游标,且count < pos即可。
def insert(self, pos, item):
"""在指定位置添加节点"""
cur = self.__head
count = 0
if pos <= 0:
self.add(item)
elif pos >= (self.length() - 1):
self.append(item)
else:
while count < pos:
count += 1
cur = cur.next
node = Node(item)
node.next = cur
node.prev = cur.prev
cur.prev.next = node
cur.prev = node
测试结果没问题
-
实现删除的功能:
那如果要删除的是头节点要怎么处理呢?
这样是不是OK
那如果原有的链表只有一个节点要如何处理呢?我们还是直接看代码吧。
def remove(self, item):
"""删除一个节点"""
cur = self.__head
while cur != None:
if cur.elem == item:
# 先判断是否为头节点,然后再去处理头节点
if cur == self.__head:
self.__head = cur.next
if cur.next:
cur.next.prev = None
else:
cur.prev.next = cur.next
if cur.next:
cur.next.prev = cur.prev
break
else:
cur = cur.next
测试结果一切正常