问题描述
我现在正在研究以组为单位反转链表,但遇到了一些问题。
问题是:
给定一个包含‘n’个节点的 LinkedList,根据其大小按以下方式反转它:
如果“n”是偶数,则在一组 n/2 个节点中反转列表。如果 n 是奇数, 保持中间节点不变,反转前“n/2”个节点,然后 反转最后的“n/2”个节点。
我的方法是:
def lenLinkedlist(head):
count = 0
current = head
while current:
current = current.next
count += 1
return count
def reverseInGroupPart(head,n):
count = 0
prevIoUs,current,next = None,head,None
while current and count < n//2:
next = current.next
current.next = prevIoUs
prevIoUs = current
current = next
count += 1
# even
if n%2 == 0:
# current at middle right Now
# head supports to be middle Now
head.next = reverseInGroupPart(current,n)
# odd
else:
# current at middle Now
head.next = current
current.next = reverseInGroupPart(current.next,n)
return prevIoUs
def reverseGroups(head):
n = lenLinkedlist(head)
if n%2 == 0:
return reverseInGroupPart(head,n)
else:
return reverseInGroupPart(head,n)
class Node:
def __init__(self,_value,_next = None):
self.value = _value
self.next = _next
def print_list(self):
temp = self
while temp:
print(temp.value,end = ' ')
temp = temp.next
print()
def main():
head = Node(1)
head.next = Node(2)
head.next.next = Node(3)
head.next.next.next = Node(4)
head.next.next.next.next = Node(5)
head.next.next.next.next.next = Node(6)
print('original linked list is: ',end = '')
head.print_list()
result = reverseGroups(head)
print('reverse of linked list is ',end = '')
result.print_list()
main()
有错误:
Traceback (most recent call last):
File "/Users/PycharmProjects/tester/main.py",line 62,in <module>
main()
File "/Users/PycharmProjects/tester/main.py",line 58,in main
result = reverseGroups(head)
File "/Users/PycharmProjects/tester/main.py",line 33,in reverseGroups
return reverseInGroupPart(head,n)
File "/Users/PycharmProjects/tester/main.py",line 22,in reverseInGroupPart
head.next = reverseInGroupPart(current,n)
[PrevIoUs line repeated 993 more times]
File "/Users/PycharmProjects/tester/main.py",line 19,in reverseInGroupPart
if n%2 == 0:
RecursionError: maximum recursion depth exceeded in comparison
original linked list is: 1 2 3 4 5 6
Process finished with exit code 1
我尝试使用递归方法来解决问题,但不确定是什么导致了错误。谢谢。
解决方法
您的 reverseGroups()
函数没有多大意义,因为它有一个 if
在两个分支中执行相同的操作。
我将采取不同的方法。首先,我要将您的功能改为Node
的方法。接下来,我将使大多数方法递归,仅供练习。最后,我将使链表段的反转递归,但不是重新排列链表部分的高级逻辑,因为这似乎不是递归问题:
class Node:
def __init__(self,value,_next=None):
self.value = value
self.next = _next
def printLinkedlist(self):
print(self.value,end=' ')
if self.next:
self.next.printLinkedlist()
else:
print()
def lengthLinkedlist(self):
count = 1
if self.next:
count += self.next.lengthLinkedlist()
return count
def reverseLinkedList(self,length):
head,rest = self,self.next
if length > 1:
if rest:
head,rest = rest.reverseLinkedList(length - 1)
self.next.next = self
self.next = None
return head,rest
def reverseGroups(self):
head = self
length = self.lengthLinkedlist()
if length > 3:
tail = self
head,rest = self.reverseLinkedList(length//2) # left
if length % 2 == 1: # odd,skip over middle
tail.next = rest
tail = tail.next
rest = tail.next
tail.next,_ = rest.reverseLinkedList(length//2) # right
return head
if __name__ == '__main__':
head = Node(1)
head.next = Node(2)
head.next.next = Node(3)
head.next.next.next = Node(4)
head.next.next.next.next = Node(5)
head.next.next.next.next.next = Node(6)
head.next.next.next.next.next.next = Node(7)
print('original linked list is: ',end='')
head.printLinkedlist()
head = head.reverseGroups()
print('reverse of linked list is ',end='')
head.printLinkedlist()
输出
> python3 test.py
original linked list is: 1 2 3 4 5 6 7
reverse of linked list is 3 2 1 4 7 6 5
>
如果我们注释掉最后一个链接:
# head.next.next.next.next.next.next = Node(7)
那么我们的输出是:
> python3 test.py
original linked list is: 1 2 3 4 5 6
reverse of linked list is 3 2 1 6 5 4
>
对我来说,这个问题原来是一个仔细的簿记问题。我还必须首先迭代地实现 reverseLinkedList()
,让 reverseGroups()
工作,然后返回并递归地重新实现 reverseLinkedList()
。
我会分步解决这个问题。对于初学者来说,类结构可以进行一些大修,使其更容易使用。我会创建一个 LinkedList
类并利用诸如 __repr__
、__len__
和 __iter__
之类的 dunder 方法使代码更简洁、更 Pythonic。每个 PEP-8 使用 snake_case
而不是 camelCase
。
from functools import reduce
class Node:
def __init__(self,next_=None):
self.value = value
self.next = next_
def __repr__(self):
return str(self.value)
class LinkedList:
def __init__(self,els):
self.head = None
for e in reversed(els):
self.head = Node(e,self.head)
def __iter__(self):
curr = self.head
while curr:
yield curr
curr = curr.next
def __getitem__(self,i):
try:
return list(zip(self,range(i + 1)))[-1][0]
except IndexError:
raise IndexError(i)
def __len__(self): # possibly better to cache instead of compute on the fly
return reduce(lambda a,_: a + 1,self,0)
def __repr__(self):
return "[" + "->".join([str(x) for x in self]) + "]"
if __name__ == "__main__":
for length in range(8):
ll = LinkedList(list(range(length)))
print("original:",ll)
在深入研究算法之前,我应该注意链表不适合递归(除非语言经过 tail-call 优化),因为每个递归步骤仅将问题空间减少 1 个节点。这会导致大量调用开销,如果列表中的元素超过 1000 个,则可能会导致堆栈溢出,并且通常不会提供太多优雅/可读性的回报来抵消这些缺点。
另一方面,树更适合递归,因为在遍历平衡良好的树期间调用堆栈频繁弹出,将深度保持为对数而不是线性尺度。使用快速排序或归并排序或执行二分搜索对列表进行排序也是如此。
链表算法也往往需要对前一个和下一个节点的多次引用,以及用于虚拟头和尾的簿记节点,在离散堆栈帧中不容易访问的状态。以迭代方式,您可以直接从循环块中不带参数地访问您需要的所有状态。
也就是说,这里有一个简单的迭代反转例程,我将使用它作为编写其余代码的基础:
class LinkedList:
# ...
def reverse(self):
prev = None
curr = self.head
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
self.head = prev
这需要更通用:如果我可以重构这个算法以反转节点和子列表长度之间的列表子集,问题就基本解决了,因为我们可以将此算法应用于前半部分和后半部分单独列出。
第一步是避免硬编码 self.head
并将其作为参数传入,返回反向子列表的新头。这仍然存在(我认为这是一项要求):
class LinkedList:
# ...
def _reverse_from(self,curr):
prev = None
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
return prev
def reverse(self):
self.head = self.reverse_from(self.head)
接下来,我们可以添加一个索引计数器,以启用从节点开始的链表子集的反转。
为了使这个工作,新的尾节点(子列表的旧前节点)需要链接到反向子节后左边的列表的后面,否则我们将用旧的头/新尾节点指向到 None
并砍掉尾巴。
class LinkedList:
# ...
def _reverse_from(self,start,length=-1):
curr = start
prev = None
while curr and length != 0:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
length -= 1
if start:
# link the new tail (old head) with the back of the list
start.next = curr
return prev
最后,添加面向客户端的功能,分别反转每一半:
class LinkedList:
# ...
def reverse_halves(self):
length = len(self)
if length < 4:
return
mid_idx = length // 2
self.head = self._reverse_from(self.head,mid_idx)
if length % 2 == 0:
mid_idx -= 1
mid = self[mid_idx]
mid.next = self._reverse_from(mid.next)
将所有这些与样本运行放在一起,我们得到:
from functools import reduce
class Node:
def __init__(self,self.head)
def _reverse_from(self,length=-1):
curr = start
prev = None
while curr and length != 0:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
length -= 1
if start:
start.next = curr
return prev
def reverse(self):
self.head = self._reverse_from(self.head)
def reverse_halves(self):
length = len(self)
if length < 4:
return
mid_idx = length // 2
self.head = self._reverse_from(self.head,mid_idx)
if length % 2 == 0:
mid_idx -= 1
mid = self[mid_idx]
mid.next = self._reverse_from(mid.next)
def __iter__(self):
curr = self.head
while curr:
yield curr
curr = curr.next
def __getitem__(self,range(i + 1)))[-1][0]
except IndexError:
raise IndexError(i)
def __len__(self):
return reduce(lambda a,ll)
ll.reverse_halves()
print("reversed:",ll,"\n")
输出:
original: []
reversed: []
original: [0]
reversed: [0]
original: [0->1]
reversed: [0->1]
original: [0->1->2]
reversed: [0->1->2]
original: [0->1->2->3]
reversed: [1->0->3->2]
original: [0->1->2->3->4]
reversed: [1->0->2->4->3]
original: [0->1->2->3->4->5]
reversed: [2->1->0->5->4->3]
original: [0->1->2->3->4->5->6]
reversed: [2->1->0->3->6->5->4]