微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

反转组中的链表

如何解决反转组中的链表

我现在正在研究以组为单位反转链表,但遇到了一些问题。

问题是:

给定一个包含‘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]

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。