这里主要记录我的实现过程中的思考和遇到的错误
书上介绍了设计LList1链表时,需要怎么去考虑,首先可以先画出要实现的大致功能和思路:
然后根据书里的模式,建立了ADT模型:
ADT LList1(LList)
LList1(self) # 初始化结点指针
prepend(self, elem) # 根据elem创建新结点,并插入到首端
append(self, elem) # 根据elem创建新结点,并插入到尾端
inster_add(self, elem, i) # 根据elem创建新结点,并插入到下标i所指示的位置
pop(self) # 删除首端结点,并返回结点值
pop_last(self) # 删除尾端结点,并返回结点值
inster_pop(self, i) # 删除下标i所指定的结点,并返回结点值
根据书上说的,我们需要考虑每个变化操作在首端时候如何运行、尾端时如何运行、还有一般情况时如何运行。
书上要求给出返回表长度的方法,可以用一个计数器_len
来实现,每增加一个结点就+1
,删除一个结点就-1
。
然后就要考虑这个计数器放置在哪里,我们想要的是每个LList1都有自己的结点计数器,互相之间不干扰,那么我们就将它设置为实例变量,并在初始化函数__init__(self)
中将其置零self._len = 0
,我将它设置到LList
类,这样每一个从LList
派生的类都可以使用这个计数器.
对于inster_add
方法:
#将新创建的节点插入到指定的 i 处
# 1、空表插入或者下标为首节点时,调用prepend方法
# 2、i > 表长时,将节点尾端插入,调用append方法
# 3、i 在表长范围之内时,循环找到下标为 i-1
# 的节点,改变其next,使之链接更新为新节点,新节点next指向
def inster_add(self, elem, i):
if self._head is None or i == 0:
return self.prepend(elem)
if i >= self._len: # i 超出下标范围;
return self.append(elem)
else:
n = 0
p = self._head
while n < (i - 1): # 取下标为i结点的前一个结点
p = p.next
n += 1
p.next = LNode(elem, p.next)
self._linkedlistlen(1)
当添加的是首端元素时,我们直接调用prepend
方法,好处在于不用重复的写一样的代码,而且当我们改变首端元素的表现形式时,直接修改prepend
方法就可以了,方便维护;尾端元素也是同理。
请注意一下上面的return self.prepend(elem)
和return self.append(elem)
,最开始我写的是self.prepend(elem)
,这样就产生了一个问题:
我希望判断首端元素添加完成之后,直接退出整个函数,但程序流程执行完prepend(self, elem)
方法后,会继续运行剩下的程序,导致最终又执行了eles
块内的程序,一个元素会被插入两遍,self._rear
指向第一次插入的元素,并没有更新。
解决问题的方式是,在每个判断里增加return
;
例如:return self.prepend(elem)
下面是全部代码:
class LList1(LList):
def __init__(self):
LList.__init__(self)
self._reat = None # 尾指针设置为None
#根据下标值 i 返回节点下标为 i 的值; 超出范围返回尾节点的值
def inster_print(self, i):
if self._head is None:
return None
if i > (self._len - 1):
return self._reat.elem
n = 0 #记录循环次数,即下标
p = self._head
while n != i:
p = p.next
n += 1
return p.elem
#在前端插入节点时,需要考虑表为空的插入情况。
def prepend(self, elem):
if self._head is None:
self._head = LNode(elem)
self._reat = self._head
self._linkedlistlen(1)
else:
self._head = LNode(elem, self._head)
self._linkedlistlen(1)
#在表尾插入节点时,需要更新self._reat;还要考虑表空时候如何插入。
def append(self, elem):
if self._head is None: # 当表为空时
self._head = LNode(elem, self._head)
self._reat = self._head #头尾指针指向同一个节点
self._linkedlistlen(1)
else:
self._reat.next = LNode(elem)
self._reat = self._reat.next
self._linkedlistlen(1)
#将新创建的节点插入到指定的 i 处
# 1、空表插入或者下标为首节点时,调用prepend方法
# 2、i > 表长时,将节点尾端插入,调用append方法
# 3、i 在表长范围之内时,循环找到下标为 i-1
# 的节点,改变其next,使之链接更新为新节点,新节点next指向
def inster_add(self, elem, i):
if self._head is None or i == 0:
return self.prepend(elem)
if i >= self._len: # i 超出下标范围
return self.append(elem)
else:
n = 0
p = self._head
while n < (i - 1): # 取下标为i结点的前一个结点
p = p.next
n += 1
p.next = LNode(elem, p.next)
self._linkedlistlen(1)
#pop操作后,如果表为空,将尾指针设置为None,避免inster_print调用尾指针
def pop(self):
e = LList.pop(self)
if self._head is None:
self._reat = self._head
return e
#删除尾节点。考虑删除尾节点时候需要更新self._reat;删除节点为最后一个节点时,需要将self._head指向None。
def pop_last(self):
if self._head is None:
raise LinkedListUnderflow("in pop_last")
p = self._head
if p.next is None:
e = p.elem
self._head = None
self._reat = None #当表为空时,将尾指针设置为None,以支inster _print方法
self._linkedlistlen(-1)
return e
while p.next.next is not None:
p = p.next
e = p.next.elem
p.next = None
self._reat = p
self._linkedlistlen(-1)
return e
#删除下标为 i 的节点
#1、考虑表为空时,抛出异常
#2、i 超出下标范围时,抛出index异常
#3、i 为首、尾下标时,调用已有方法
#4、正常情况时候,首、尾指针不改变
def inster_pop(self, i):
if self._head is None:
raise LinkedListUnderflow("in pop_inster")
if i > (self._len - 1):
raise LinkedListIndex("index error")
elif i == 0:
return self.pop()
elif i == (self._len - 1):
return self.pop_last()
else:
p = self._head
n = 1 # 下标
while n < i: #循环,直到得到下标节点的前一个节点
p = p.next
n += 1
e = p.next.elem
p.next = p.next.next
self._linkedlistlen(-1)
return e
class LinkedListUnderflow(ValueError):
pass
class LinkedListIndex(IndexError):
pass
那么,就这样,欢迎指正,谢谢!