前面已经总结过单向链表了,现在总结一下双向链表。
单向链表和双向链表区别在哪?
单向链表,顾名思义,其方向是单向的,从一个节点只能获取下一个节点,无法获取节点的上一个节点的信息。
双向链表,其方向是双向的,通过一个节点,我们既可以获取该节点的下一个节点信息,亦可以获取上一个节点的信息
(一) 双向链表
结构
双向链表的结构如下图所示:节点方向是双向的,1节点是链表头部,4节点是链表尾部。
图
一
链
表
结
构
图 一 链表结构
图一链表结构
如图二所示:每一个节点包含一个数据域和两个指针域,前面的指针域用于存放上一个节点的地址,后面的指针用于存放下一个节点的地址。图一中的1节点为头部节点,其前指针域为空,4节点为尾部节点,其后一个指针域为空。
图
二
节
点
结
构
图二 节点结构
图二节点结构
(二) 编程实现
双向链表在实现的方式上与单向链表类似,只是有些许差别,通过下面编程可以看大。
(1) 双向链表的操作
下面一步一步的编程来实现双向链表
(2) 编程实现
(2.1)创建节点类
以下代码实现了创建节点的了,包含了数据域以及两个指针域。
class Node:
"""双向链表节点实现"""
def __init__(self, data):
"""创建一个节点"""
self.data = data #数局域
self.pre = None #前指针域pre
self.next = None #后指针域next
(2.2)创建双向链表类
在代码实现时,我给链表添加了self.__tail 属性,表示尾部节点的指针,用于记录链表的尾部节点。这样这样的目的是是:在链表额一些操作,如尾部添加节点,删除节点,或者是反向遍历节点时,这个属性是很有用的,会让这个操作更加方便。
class doubleLinkedList(object):
"""双向链表的实现"""
def __init__(self, node=None):
self.__head = node #链表尾部
self.__tail = node #链表头部
以下过程的函数均是在这个类中的。
(2.3)链表是否为空
与单向链表类似
def is__empty(self):
"""链表是否为空"""
return self.__head == None
(2.4) 链表长度
与单向链表类似
def length(self):
"""链表长度"""
count = 0
curNode = self.__head
while curNode:
count += 1
curNode = curNode.next
return count
(2.5)链表的遍历(正向)
与单向链表类似
def scan(self):
"""遍历链表"""
curNode = self.__head
if not curNode: return
while curNode:
print(curNode.data, end=" ")
curNode = curNode.next
(2.6)双向链表的反向遍历
def reverseScan(self):
curNode = self.__tail
while curNode:
print(curNode.data, end=' ')
curNode = curNode.pre
(2.7)链表头部添加节点
与单向链表类似。
def add(self, item):
"""头部添加元素"""
curNode = self.__head
if not curNode:
self.__head = Node(item)
self.__tail = self.__head
return
else:
node = Node(item) # 创建节点
node.next = self.__head
self.__head.pre = node
self.__head = node
(2.8)链表尾部添加节点
添加的方式与单向链接类似。需要注意的是,从尾部添加节点时,在节点添加完成以后,链表的尾部指针需要移动到新添加到尾部的节点
def append(self, item):
"""尾部添加"""
if self.is__empty():
self.add(item)
return
node = Node(item)
node.pre = self.__tail
self.__tail.next = node
self.__tail = node # 链表的尾部变为新插入的节点
从上面代码可以看出,当链表中设计了self.__tail这个属性后,在尾部添加元素是非常方便快捷的。为什么呢?如果没有这个属性的话,当需要在尾部插入节点时,必须先遍历元素,找到尾部节点(尾部节点的next指针为None),然后开始插入,用大O表示法表示其时间复杂度的话,应该是O(n)。如果具有这个属性的话,无需遍历,其时间复杂度为O(1)
(2.9) 链表指定位置插入数据
该换算带有position这个位置参数,因此需要对其讨论,具有三种情况:
- position<=1 此时按照头部插入处理
- position>self.length()-1 ,即插入的位置大于链表的长度,按照尾部插入处理
- 中间插入。中间插入是不会影响尾部指针self.__tail的。
注意:position表示该节点是第position+1个元节点,其位置是position
def insert(self, position, item):
"""指定位置插入链表"""
"""position:表示第position+1个元素"""
if position <= 1: # 按照头部插入处理
self.add(item)
return
elif position > self.length() - 1: # 如果插入的位置大于链表的长度,则按尾部插入处理
self.append(item)
return
else: #中间插入
node = Node(item)
count = 0
targetNode = self.__head
while count < position - 1:
targetNode = targetNode.next
count += 1
# 结束循环后,targetNode指向position-1的位置
node = Node(item) # 创建新节点
node.pre = targetNode
node.next = targetNode.next
targetNode.next.pre = node
targetNode.next = node
(2.10)移除链表节点(按元素)
这些需要考虑三种情况:
- 需要移除的节点为头节点。删除头节点后,需要将头节点指针往后移动。
- 移除的节点为尾部节点。移除尾部节点后,需要将尾部节点指针往前移动。
- 移除的节点为中间节点
def remove(self, item):
targetNode = self.__head
while targetNode:
if targetNode.data == item:
# 如果是删除的节点是头部
if targetNode == self.__head:
self.__head = targetNode.next
self.__head.pre = None
return
elif targetNode == self.__tail: # 如果删除的是尾部节点
self.__tail = targetNode.pre
targetNode.pre.next = None
return
targetNode.pre.next = targetNode.next
targetNode.next.pre = targetNode.pre
break
targetNode = targetNode.next
(2.11) 查找节点是否存在(按元素)
与单向链表类似。
def search(self, item):
curNode = self.__head
while curNode:
if curNode.data == item:
return True
curNode = curNode.next
return False
(三)完整代码
下面将完整的代码贴出来。
class Node:
"""双向链表节点实现"""
def __init__(self, data):
"""创建一个节点"""
self.data = data
self.pre = None
self.next = None
class doubleLinkedList(object):
"""双向链表的实现"""
def __init__(self, node=None):
self.__head = node
self.__tail = node
def is__empty(self):
"""链表是否为空"""
return self.__head == None
def length(self):
"""链表长度"""
count = 0
curNode = self.__head
while curNode:
count += 1
curNode = curNode.next
return count
def scan(self):
"""遍历链表"""
curNode = self.__head
if not curNode: return
while curNode:
print(curNode.data, end=" ")
curNode = curNode.next
def reverseScan(self):
"""双向链表的反向遍历"""
curNode = self.__tail
while curNode:
print(curNode.data, end=' ')
curNode = curNode.pre
def add(self, item):
"""头部添加元素"""
curNode = self.__head
if not curNode:
self.__head = Node(item)
self.__tail = self.__head
return
else:
node = Node(item) # 创建节点
node.next = self.__head
self.__head.pre = node
self.__head = node
def append(self, item):
"""尾部添加"""
if self.is__empty():
self.add(item)
return
node = Node(item)
node.pre = self.__tail
self.__tail.next = node
self.__tail = node # 链表的尾部变为新插入的节点
def insert(self, position, item):
"""指定位置插入链表"""
"""position:表示第position+1个元素"""
if position <= 1: # 按照头部插入处理
self.add(item)
return
elif position > self.length() - 1: # 如果插入的位置大于链表的长度,则按尾部插入处理
self.append(item)
return
else: #中间插入
node = Node(item)
count = 0
targetNode = self.__head
while count < position - 1:
targetNode = targetNode.next
count += 1
# 结束循环后,targetNode指向position-1的位置
node = Node(item) # 创建新节点
node.pre = targetNode
node.next = targetNode.next
targetNode.next.pre = node
targetNode.next = node
def remove(self, item):
targetNode = self.__head
while targetNode:
if targetNode.data == item:
# 如果是删除的节点是头部
if targetNode == self.__head:
self.__head = targetNode.next
self.__head.pre = None
return
elif targetNode == self.__tail: # 如果删除的是尾部节点
self.__tail = targetNode.pre
targetNode.pre.next = None
return
targetNode.pre.next = targetNode.next
targetNode.next.pre = targetNode.pre
break
targetNode = targetNode.next
def search(self, item):
curNode = self.__head
while curNode:
if curNode.data == item:
return True
curNode = curNode.next
return False
#测试代码
dll = doubleLinkedList()
dll.add(1) # 头部插入1
dll.append(2) # 尾部插入2
dll.append(3)
dll.append(4)
dll.append(5)
dll.insert(3, 6) # 3的位置插入6
print("链表数据:", end=" ")
print("链表长度:", dll.length())
print("遍历链表:", end='')
dll.scan()
print()
print("数据6是否存在:", end='')
print(dll.search(6))
print("删除元素5:")
dll.remove(5)
print("遍历链表:", end='')
dll.scan()
print()
print("反向遍历链表:", end='')
dll.reverseScan()
代码运行结果如图三所示,从显示结果上来看,以上代码应该是正确的。
图
三
运
行
结
果
图三 运行结果
图三运行结果
如有不合适的地方,敬请指出,谢谢。