一、双向链表的结构
1.1 节点的实现
链表中的每一个结点都由前驱结点、数据区和后继结点三部分组成,在Python或者其他语言中没有一个数据类型可以同时储存这三个个数据,因此我们可以定义节点这样一个类,而结点中的前驱结点、数据区和后继结点可以想象成该类的三个属性。
在定义一个新的节点时,我们没有办法确定该结点的前驱结点和后继结点,因此我们可以先将其设置为空,当有下一个节点时,在重新对其赋值。
class Node(object):
"""双向链表的节点"""
def __init__(self,item,):
#item存放的数据区域
self.item = item
#prev存放前驱结点的标识
self.prev = None
#next存放下一个节点的标识
self.next = None
1.2 双向链表的操作
is_empty() 链表是否为空
length() 链表长度
travel() 遍历整个链表
append(item) 链表尾部添加元素
add(item) 链表头部添加元素
insert(pos, item) 指定位置添加元素
search(item) 查找节点是否存在
remove(item) 删除节点
1.3 单链表的操作实现
1.3.1 判断双向链表是否为空
如果双向链表为空,则其首结点为空(self.__head = None);如果双向链表不为空,则其首结点不为空(self.__head = node)。
class Node(object):
"""双向链表的节点"""
def __init__(self,item):
self.item = item
self.prev = None
self.next = None
class DoubleLinkList(object):
def __init__(self,node = None):
# 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
# 否则,等于实参值
self.__head = node
def is_empty(self):
"""单链表是否为空"""
# if self.__head == None:
# return True
# return False
return self.__head == None
node = Node(100)
d = DoubleLinkList(node)
print(d.is_empty())
1.3.2 双向链表的长度
定义一个游标cur,让cur从头指针head出发直到遍历到为指针None
定义一个计数变量count,cur移动一次,count就加1
class Node(object):
"""双向链表的节点"""
def __init__(self,item):
self.item = item
self.prev = None
self.next = None
class DoubleLinkList(object):
def __init__(self,node = None):
# 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
# 否则,等于实参值
self.__head = node
def length(self):
"""链表长度"""
count = 0
cur = self.__head
while cur != None:
count += 1
cur = cur.next
return count
node = Node(100)
d = DoubleLinkList(node)
print(d.length())
1.3.3 遍历双向链表
遍历链表和求链表长度的思想基本一致,唯一不同的就是我们不需要计数变量但每循环一次,我们就需要打印该节点的数据
class Node(object):
"""双向链表的节点"""
def __init__(self,item):
self.item = item
self.prev = None
self.next = None
class DoubleLinkList(object):
def __init__(self,node = None):
# 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
# 否则,等于实参值
self.__head = node
def travel(self) :
"""遍历整个链表"""
cur = self.__head
while cur != None:
#打印节点中的数据
print(cur.item,end = ' ')
cur = cur.next
node = Node(100)
d = DoubleLinkList(node)
d.travel()
1.3.4 向双向链表尾部添加元素
向链表尾部添加节点时,需要进行两个操作:
1.创建一个新的节点 node = SingleNode(item)
2.遍历到最后一个节点,令其的next属性等于新的节点cur.next == node,新节点的prev属性等于当前节点node.prev = cur
注意,当链表为空时,self.__head = None,而None类型没有next属性
class SingleNode(object):
"""单链表的节点"""
def __init__(self,item):
self.item = item
self.next = None
class SingleLinkList(object):
def __init__(self,node = None):
# 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
# 否则,等于实参值
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
node.prev = cur
d = DoubleLinkList()
d.append(200)
d.append(220)
d.append(890)
d.travel()
1.3.5 链表头部添加元素
向链表头部添加节点,需要进行一下三步操作:
1.创建一个新的节点 node = SingleNode(item)
2.令原先的首指针等于新节点的下一个节点标识node.next = self.__head,原先的首节点前驱结点是新节点,self.__head.prev = node
3.令新节点等于首指针 self.__head = node
4.考虑空链表的情况
class Node(object):
"""双向链表的节点"""
def __init__(self,item):
self.item = item
self.prev = None
self.next = None
class DoubleLinkList(object):
def __init__(self,node = None):
# 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
# 否则,等于实参值
self.__head = node
def add(self,item):
"""链表头部添加元素"""
node = Node(item)
if self.is_empty():
self.__head = node
else:
node.next = self.__head
self.__head.prev = node
self.__head =node
d = DoubleLinkList()
d.add(200)
d.append(220)
d.append(890)
d.add(67)
d.travel()
1.3.6 insert(pos, item) 指定位置添加元素
在指定位置插入节点需要进行以下三步操作
1.通过循环找到指定插入位置的前一个节点
count < pos-1
2.将当前节点下一个节点赋值给新节点的下一个节点标识node.next = cur.next;新节点的前驱结点为当前节点 node.pre = cur;将新节点赋值给当前节点下一个节点的前驱结点cur.next.prev = node;当前节点的后继结点为新节点cur.next = node
需要考虑特殊两种情况,当pos <= 0 时,头部插入add();当pos > length()时,尾部插入append()
class Node(object):
"""双向链表的节点"""
def __init__(self,item):
self.item = item
self.prev = None
self.next = None
class DoubleLinkList(object):
def __init__(self,node = None):
# 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
# 否则,等于实参值
self.__head = node
def insert(self,pos,item):
node = Node(item)
if pos <= 0:
#头部插入
self.add(item)
elif pos > self.length()-1:
#尾部插入
self.append(item)
else:
count = 0
cur = self.__head
while count < pos-1:
count += 1
cur = cur.next
#循环结束,定位到要插入节点的前一个节点
node.next = cur.next
node.prev = cur
cur.next = node
cur.next.prev = node
d = DoubleLinkList()
d.append(200)
d.append(220)
d.append(890)
d.insert(2,67)
d.travel()
注意:节点位置从0开始。
1.3.7 查找节点是否存在
遍历链表,如果当前节点的item值等于所查找的item值,则返回True,如果不是则游标后移。如果遍历完整个链表仍没有找到,则返回False。
class Node(object):
"""双向链表的节点"""
def __init__(self,item):
self.item = item
self.prev = None
self.next = None
class DoubleLinkList(object):
def __init__(self,node = None):
# 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
# 否则,等于实参值
self.__head = node
def search(self,item):
cur = self.__head
while cur != None:
if cur.item == item:
return True
cur = cur.next
return False
d = DoubleLinkList()
d.append(200)
d.append(220)
d.append(890)
d.insert(2,67)
d.travel()
print(d.search(200))
1.3.8 删除指定元素
删除指定节点,具体操作如下
1.遍历链表,判断cur.item == item
2.如果相等,则令pre.next= cur.next
3.不相等则指针继续后移 pre= cur cur= cur.next
注意特殊情况
a.删除头节点且链表长度为1;
b.删除头节点且链表长度不为1;
c.删除尾节点
class Node(object):
"""双向链表的节点"""
def __init__(self,item):
self.item = item
self.prev = None
self.next = None
class DoubleLinkList(object):
def __init__(self,node = None):
# 向参数传递默认值,如果没有向该参数传递实参,则该参数等于默认值
# 否则,等于实参值
self.__head = node
def remove(self,item):
#空链表
if self.is_empty():
return
#非空链表
cur = self.__head
if cur.item == item:
#头节点是要删除的节点,且链表长度不为1
if cur.next != None:
self.__head = cur.next
else:
#删除头结点,且链表长度为1
self.__head = None
else:
while cur != None:
if cur.item == item:
#删除中间节点
if cur.next != None:
cur.prev.next = cur.next
cur.next.prev = cur.prev
break
else:
#删除尾节点
cur.prev.next = None
break
else:
cur = cur.next
d = DoubleLinkList()
d.append(200)
d.append(220)
d.append(890)
d.insert(2,67)
d.travel()
print()
d.remove(200)
d.travel()
def remove(self, item):
"""删除指定元素"""
if self.is_empty():
return
cur = self.__head
if cur.item == item:
# 头节点是要删除的节点,且链表长度不为1
if cur.next != None:
self.__head = cur.next
else:
# 删除头结点,且链表长度为1
self.__head = None
else:
while cur != None:
if cur.item == item:
# 删除中间节点或尾节点
cur.prev.next = cur.next
if cur.next != None:
#删除的是中间节点时,需要修改前驱结点
cur.next.prev = cur.prev
break
else:
cur = cur.next