簡體   English   中英

反向分組鏈表

[英]reverse a linked list in groups

我現在正在研究分組反轉鏈表,但遇到了一些問題。

問題是:

給定一個具有“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)
  File "/Users/PycharmProjects/tester/main.py", line 22, in reverseInGroupPart
    head.next = reverseInGroupPart(current, 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() function 沒有多大意義,因為它有一個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()

OUTPUT

> 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)

那么我們的output就是:

> 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()工作,然后返回 go 並遞歸地重新實現reverseLinkedList()

我會分小步解決這個問題。 對於初學者,class 結構可以進行一些大修以使其更易於使用。 我會創建一個LinkedList class 並利用__repr____len____iter__之類的 dunder 方法來使代碼更簡潔、更 Pythonic。 根據PEP-8使用snake_case而不是camelCase

from functools import reduce

class Node:
    def __init__(self, value, 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)

在深入研究算法之前,我應該注意鏈表不適合遞歸(除非語言是尾調用優化的),因為每個遞歸步驟只會將問題空間減少 1 個節點。 這會產生大量的調用開銷,如果列表有超過 1000 個元素,並且通常不會提供太多的優雅/可讀性來抵消這些缺點,則可能會導致堆棧崩潰。

另一方面,樹更適合遞歸,因為在遍歷平衡良好的樹期間調用堆棧經常彈出,將深度保持在對數而不是線性比例。 對於使用快速排序或合並排序或執行二進制搜索對列表進行排序也是如此。

鏈表算法也往往需要對前一個和下一個節點的許多引用,以及用於虛擬頭和尾的簿記節點,state 在離散堆棧幀中不容易訪問。 迭代地,您可以直接從循環塊訪問您需要的所有 state,無需參數。

也就是說,這是一個簡單的迭代反轉例程,我將用作編寫代碼的 rest 的基礎:

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

最后,添加面向客戶端的 function 分別反轉每一半:

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, value, 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 _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:
            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, i):
        try:
            return list(zip(self, range(i + 1)))[-1][0]
        except IndexError:
            raise IndexError(i)

    def __len__(self):
        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)
        ll.reverse_halves()
        print("reversed:", ll, "\n")

Output:

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]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM