【问题标题】:Sort linked list by using Quick sort in python在python中使用快速排序对链表进行排序
【发布时间】:2021-03-04 17:46:56
【问题描述】:

我是 python 新手。我有以下存储在链接列表中的书籍列表,我想使用快速排序对它们进行排序,但不幸的是,我遇到了一个问题。

class Node:
    def __init__(self, data=None):
        self.data = data
        self.prev = None
        self.next = None


class DoublyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None

    def append_value(self, x):
        if not isinstance(x, Node):
            x = Node(x)
        if self.is_empty():
            self.head = x
        else:
            current = self.head
            while current.next:
                current = current.next
            current.next = x
            x.prev = current
        self.tail = x

    def length(self):
        count = 0
        current = self.head
        while current:
            count += 1
            current = current.next
        return count

    def is_empty(self):
        return self.head is None

    def __str__(self):
        to_print = ''
        current = self.head
        while current:
            to_print += f'{current.data} <-> '
            current = current.next
        if to_print:
            return f'[{to_print[:-5]}]'
        return '[]'

    def quick_sort(self, arr):
        if self.length() < 2:
            return self
        else:
            pivot = self.tail.data
            smaller, equal, larger = [], [], []
            current = self.head
            while current:
                if current.data < pivot:
                    smaller.append(current.data)
                elif current.data == pivot:
                    equal.append(current.data)
                else:
                    larger.append(current.data)
                current = current.next
        return self.quick_sort(smaller) + equal + self.quick_sort(larger)

这是快速排序方法,但它在“返回 self.quick_sort(smaller) + equal + self.quick_sort(larger)”上给了我 RecursionError。如何使用快速排序对链表进行排序?

my_list = DoublyLinkedList()
my_list.append_value('In Search of Lost Time')
my_list.append_value('Ulysses by James Joyce')
my_list.append_value('Within a Budding Grove')
my_list.append_value('The Guermantes Way')
my_list.append_value('In Search of Lost Time')
my_list.append_value('Sodom & Gomorrah')
my_list.append_value('One Hundred Years of Solitude')
my_list.append_value('War and Peace')

print(f'List of Books: {my_list}')

print(f'Quick Sort: {my_list.quick_sort(my_list)}')

【问题讨论】:

  • 第 1 步是使用 GUI 调试器调试您的程序,并逐行逐行查看代码的执行与您的预期不同的地方。听起来您还没有以这种方式调试过。
  • 我尝试调试代码。但这并没有解决问题。
  • 当我这样做时,问题就在这里:更小,相等,更大 = [],[],[] 链表不接受 。我想我必须将其转换为 main.Node'>,但我无法这样做(尝试调试无法解决问题)。谢谢。
  • 旁注:将链表中的值复制到标准列表中感觉有点像作弊:每个值都会在某个时候附加到列表中。如果这样做,您可能想知道为什么不将链接列表完全复制到列表中,对该列表进行排序,然后从中创建链接列表。如果这是一项作业,那么我无法想象应该使用标准列表。

标签: python linked-list quicksort


【解决方案1】:

确实,发生错误是因为您将标准列表传递给quick_sort,而它期望参数是双向链表实例。

此外,如果在这个算法中允许使用列表会很奇怪,因为人们可能想知道为什么首先会有一个双向链表。如果您要使用列表,那么您不妨将数据存储在列表中并对列表进行排序。这不是目的。

其他一些关于代码的cmets:

  • quick_sort 是该类的方法,因此不需要将链表 also 作为参数传递。相反,传递两个参数来标识该链表中小节的开始和结束节点。

  • 最后的return 语句有问题。如果确实quick_sort 将返回一个链接列表,那么+ 不是您可以在它们上使用的运算符。此外,快速排序应该是一种就地排序算法,而+ 建议创建一个新列表。由于它是就地的,您应该能够返回self

所以,...您确实需要通过遍历链表并对其进行操作来解决这个问题。

这是一种方法。首先定义两个允许在列表中任意位置插入和删除节点的方法:

def remove_node(self, node):
    if node.prev:
        node.prev.next = node.next
    else:
        self.head = node.next
    if node.next:
        node.next.prev = node.prev
    else:
        self.tail = node.prev
    node.next = node.prev = None
    return node

def insert_after(self, prev, node):
    if not isinstance(node, Node):
        node = Node(node)
    node.prev = prev
    if prev:
        node.next = prev.next
        prev.next = node
    else:
        node.next = self.head
        self.head = node
    if node.next:
        node.next.prev = node
    else:
        self.tail = node
    return self

后者实际上是比您已有的append_value 更灵活的方法。你实际上可以利用这个新的来定义你的:

def append_value(self, x):
    return self.insert_after(self.tail, x)

然后按如下方式进行快速排序:

将开始和结束节点作为参数。从列表中提取(删除)结束节点并将其视为枢轴。让两个参考沿着这个子列表走向彼此,从末端开始。当它们的值位于枢轴值的好的一侧时,让该参考值靠得更近。一旦你有两个值与枢轴相反的引用,交换这两个节点中的数据。一旦这两个引用相互交叉,您就确定了两个分区。将枢轴节点注入到列表中,就在这两个分区之间。最后,使用递归对两个分区做同样的事情:

def quick_sort(self, start=None, end=None):
    if not start and not end:  # set default arguments:
        start = self.head
        end = self.tail
    if not start or not end or start == end or end.next == start:
        # The sublist is empty or has just one value
        return self
    # Extract the pivot node from the list
    end, pivot = end.prev, self.remove_node(end)
    pivotdata = pivot.data
    # Define two references that will walk towards eachother:
    right = end
    left = start
    while left and left.prev != right:  # While they didn't cross over
        if left.data <= pivotdata:
            left = left.next
        elif right.data >= pivotdata:
            right = right.prev
        else: # Swap values, not the nodes:
            left.data, right.data = right.data, left.data
            left = left.next
            right = right.prev
        
    # Insert the pivot node between the two partitions
    self.insert_after(right, pivot)
    # Sort the non-empty partitions recursively
    if start.prev != pivot:
        self.quick_sort(start, right)
    if end.next != pivot:
        self.quick_sort(left, end)
    return self

【讨论】:

  • 您的回答有帮助。告诉我更多关于'while left and left.prev != right:'的信息。没收到?我认为你正在一个一个地移动左右指针,直到两者不相等(一个指向下一个的左指针和一个指向上一个的右指针)。并且,只要左节点与右节点相等,就交换节点的值。这是你做对了吗?
  • 还有,这个东西有什么作用? if end.next == pivot: end = pivot if start.prev == pivot: start = pivot
  • 移动leftright 的过程类似于标准快速排序(在数组上)。它们从子列表的最末端开始,然后彼此靠近,直到彼此超越。因此,当循环结束时,right 位于left 的左侧(或者它们从列表中删除时变为None)。
  • 关于你的第二个问题:它处理当枢轴恰好小于(或大于)子列表中的 all 值时发生的边界情况。在这种情况下,我们将枢轴 next 重新插入到子列表中(在 start 之前或在 end 之后)。如果发生这种情况,则在递归调用中只有一个分区需要处理。请注意,您引用了我稍后更新的代码版本。没错,但我更喜欢这种方式。
猜你喜欢
  • 2020-03-03
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 2022-11-13
  • 2011-07-10
  • 2020-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多