class Node(object):
# 初始化时它就只是个孤家寡人,前不着村后不着店,prior和next都是空
def __init__(self, item):
self.item = item
self.prior = None
self.next = None
初始化:双向链表的构造函数和单链表一样,只需要设置空的头节点即可。
class DeLink(object):
def __init__(self):
self.__head = None
事实上,双向链表比单链表的节点多了一个属性prior,其他的方法照样可以沿用。参照博客 https://blog.csdn.net/sf9898/article/details/104946291 将单链表的代码拷贝下来,在单链表的class Node下按照上面说的那样子改造,加一个prior属性,其他的别改,直接运行并没有毛病。其原因在于增加了一个并没有使用到的prior属性,运行的实际上还是单链表。
正如前面讲的,prior存在的意义是为了查找前一个节点,这也是双向链表的优点。那么就可以有利用prior属性查找节点的方法。
接下来按照双向链表的原理重新构造一下class DeLink
size 方法不需要讲究,利用原来的单链表方法即可,毕竟只是求个数。当然,判空和遍历也可以沿用。
def isEmpty(self):
return self.__head is None
def size(self):
count = 0
cur = self.__head
while cur:
count += 1
cur = cur.next
return count
def travel(self):
cur = self.__head
while cur:
print(cur.item, end=' ')
cur = cur.next
print('')
查找,删除数值等函数亦是可以沿用,加上了prior属性之后变化的主要是插入和删除,其他的沿用即可。接下来重点看看插入和删除。
头部插入
# 头部插入
def addFront(self, item):
node = Node(item) # 新的节点只有item,next和prior都是None
node.next = self.__head # 新的节点的next指向当前的第一个节点
if self.__head:
self.__head.prior = node # 不为空时设置当前第一个节点的prior指向新来的node
self.__head = node # 新来的node成为新的头结点
尾部插入:尾部插入需要将原来的尾部节点last的next指向新的node(原来的last的next是指向None的),node的prior指向last(这一步要有,这样通过node的prior就可以找到last了)。为了减少工作量,直接在原有的单链表上面改了。
# 尾部插入
def addBack(self, item):
node = Node(item)
if self.__head is None:
self.__head = node
return
cur = self.__head
pre = None
while cur:
pre = cur
cur = cur.next
# 当cur 为最后一个节点时带入,pre更新为最后一个节点,cur更新为最后一个节点的下一个节点即为空,
# 下一次while cur 时会退出循环,此时的pre表示的就是最后一个节点,将node挂到pre的后面即可
pre.next = node # 这里的pre相当于例子中的last
# 仅加了下面这句
node.prior = pre
中间插入:需要注意的是要找到插入位置的前一个节点。如图,找到pre,之后:
pre.next = node
node.prior = pre
node.next = nd
nd.prior = node
# 插入节点, 节点在插入后是第 pos 个节点,当然这个函数也可以实现头部插入和尾部插入的功能
# 故pos取值1时,即头部插入,取值size+1时是尾部插入。因此取值的合法范围是[1,size + 1]
def insert(self, pos, item):
if pos > (self.size() + 1) or pos < 1:
return
if pos == 1:
self.addFront(item)
return
node = Node(item)
cur = self.__head
pre = None
for i in range(pos - 1):
pre = cur
cur = cur.next
# 1) pre.next = node
# 2) node.prior = pre
# 3) node.next = nd
# 4) nd.prior = node
pre.next = node
node.prior = pre
node.next = cur
if cur:
cur.prior = node
头部删除:更新头结点为头结点的下一个节点,原头结点的next指向None,现头结点的prior指向None。
# 删除头部结点
def removeFront(self):
cur = self.__head
self.__head = self.__head.next
cur.next = None
self.__head.prior = None
删除尾部结点
# 删除尾部节点
def removeBack(self):
# 空节点时
if self.__head is None:
return
# 只有一个节点
if self.__head and self.__head.next is None:
self.__head = None
return
# 链表节点有两个及以上
cur = self.__head # 当前节点
pre = None # 前一个节点
cn = cur.next # 后一个节点
# 刚开始cur取到的是第一个节点,cn是第二个
while cn:
pre = cur
cur = cur.next
cn = cur.next
# 仅修改此处
pre.next = None
cur.prior = None
中间删除
# 删除指定数值的节点
def delete(self, item):
if self.__head is None:
return
if self.__head.item == item:
self.__head = None
return
cur = self.__head.next # 取第二个节点
pre = self.__head # 第一个节点
while cur:
if cur.item == item:
# 仅修改此处
pre.next = cur.next
cur.prior = None
cur.next.prior = pre
cur.next = None
break
else:
pre = cur
cur = cur.next
参照之前单链表进行测试,下面是完整代码,如有错误之处还望批评指出。
class Node(object):
# 初始化时它就只是个孤家寡人,前不着村后不着店,prior和next都是空
def __init__(self, item):
self.item = item
self.prior = None
self.next = None
class DeLink(object):
def __init__(self):
self.__head = None
def isEmpty(self):
return self.__head is None
# 头部插入
def addFront(self, item):
node = Node(item) # 新的节点只有item,next和prior都是None
node.next = self.__head # 新的节点的next指向当前的第一个节点
if self.__head:
self.__head.prior = node # 设置当前第一个节点的prior指向新来的node
self.__head = node # 新来的node成为新的头结点
# 尾部插入
def addBack(self, item):
node = Node(item)
if self.__head is None:
self.__head = node
return
cur = self.__head
pre = None
while cur:
pre = cur
cur = cur.next
# 当cur 为最后一个节点时带入,pre更新为最后一个节点,cur更新为最后一个节点的下一个节点即为空,
# 下一次while cur 时会退出循环,此时的pre表示的就是最后一个节点,将node挂到pre的后面即可
pre.next = node # 这里的pre相当于例子中的last
node.prior = pre
def size(self):
count = 0
cur = self.__head
while cur:
count += 1
cur = cur.next
return count
def travel(self):
cur = self.__head
while cur:
print(cur.item, end=' ')
cur = cur.next
print('')
# 删除头部节点
def removeFront(self):
cur = self.__head
self.__head = self.__head.next
cur.next = None
self.__head.prior = None
# 删除尾部节点
def removeBack(self):
# 空节点时
if self.__head is None:
return
# 只有一个节点
if self.__head and self.__head.next is None:
self.__head = None
return
# 链表节点有两个及以上
cur = self.__head # 当前节点
pre = None # 前一个节点
cn = cur.next # 后一个节点
# 刚开始cur取到的是第一个节点,cn是第二个
while cn:
pre = cur
cur = cur.next
cn = cur.next
# 仅修改此处
pre.next = None
cur.prior = None
# 查找链表中有没有item,有返回True,没有则返回False
def search(self, item):
cur = self.__head
res = False
while cur:
if cur.item == item:
res = True
break
else:
cur = cur.next
return res
# 查找某个数的下标
# def searchIndex(self, item):
# if self.search(item):
# # 存在的话才进行
# cur = self.__head
# index = -1
# while cur:
# index += 1
# if cur.item == item:
# break
# else:
# cur = cur.next
# return index
# else:
# return -1
# 删除指定数值的节点
def delete(self, item):
if self.__head is None:
return
if self.__head.item == item:
self.__head = None
return
cur = self.__head.next # 取第二个节点
pre = self.__head # 第一个节点
while cur:
if cur.item == item:
pre.next = cur.next
cur.prior = None
cur.next.prior = pre
cur.next = None
break
else:
pre = cur
cur = cur.next
# 插入节点, 节点在插入后是第 pos 个节点,当然这个函数也可以实现头部插入和尾部插入的功能
# 故pos取值1时,即头部插入,取值size+1时是尾部插入。因此取值的合法范围是[1,size + 1]
def insert(self, pos, item):
if pos > (self.size() + 1) or pos < 1:
return
if pos == 1:
self.addFront(item)
return
node = Node(item)
cur = self.__head
pre = None
for i in range(pos - 1):
pre = cur
cur = cur.next
# 1) pre.next = node
# 2) node.prior = pre
# 3) node.next = nd
# 4) nd.prior = node
pre.next = node
node.prior = pre
node.next = cur
if cur:
cur.prior = node
ll = DeLink()
print(ll.isEmpty()) # 初始化时为空,理应输出True
for i in range(5):
ll.addFront(i)
print('size:', ll.size()) # 5个数
ll.travel() # 4 3 2 1 0
for i in range(5):
ll.addBack(i)
ll.travel() # 4 3 2 1 0 0 1 2 3 4
ll.removeFront()
ll.travel() # 3 2 1 0 0 1 2 3 4
print('----')
ll.removeBack()
ll.travel() # 3 2 1 0 0 1 2 3
ll.insert(9, 12)
ll.travel() # 3 2 1 0 0 1 2 3 12
print(ll.search(13)) # 没有,False
print(ll.search(1)) # True
ll.delete(2)
ll.travel() # 3 1 0 0 1 2 3 12
print('------')
# print(ll.searchIndex(12))
ll.insert(8, 100)
ll.travel() # 3 1 0 0 1 2 3 100 12
结果
可以发现写起来代码量会多一点点,事实上,很多东西用单链表就能解决了,双向链表比单链表多了一个prior,这一点是双向链表的优点,同时也是它的缺点,毕竟多一个元素就多占一点空间,因此还是单链表用的比较多。