如何干净地使用 QuickSort 对链表进行排序 - Python

Posted

技术标签:

【中文标题】如何干净地使用 QuickSort 对链表进行排序 - Python【英文标题】:How to cleanly use QuickSort to sort Linked List - Python 【发布时间】:2021-12-15 19:30:46 【问题描述】:

使用快速排序对链表进行排序的最简洁方法是什么?我目前有以下,但不是很好。我正在使用诸如 sort(self) 之类的函数,因此我可以简单地使用 list.sort() 并且我会通过快速排序方法对我的链表进行排序。

可能的方法但不知道如何实现:从当前列表(self)开始,让pivot为列表头部的数据,并创建两个新的链表:一个称为smaller(包含其数据的所有元素小于枢轴)和另一个(包含所有剩余元素,除了枢轴)。然后,调用smaller.sort()和other.sort(),将当前列表设置为更小,然后追加pivot并与other合并。

如果有人有任何想法......

# Node of LinkedList
class Node :
    
    def __init__(self, data) :
        # set node value
        self.data = data
        self.next = None
    
class MyLinkedList :
    
    # Class constructors
    def __init__(self) :
        self.head = None
        self.tail = None
    
    # insert node at last of linke list
    def insert(self, value) :
        # Create a node
        node = Node(value)
        if (self.head == None) :
            # When linked list empty add first node
            self.head = node
            self.tail = node
        else :
            # Add new node at end of linked list
            self.tail.next = node
            self.tail = node
        
    
    # Display linked list nodes
    def display(self) :
        if (self.head != None) :
            print("\n Linked List :", end = "")
            temp = self.head
            while (temp != None) :
                print(" ", temp.data, end = "")
                temp = temp.next
                if (temp == self.head) :
                    # avoid loop
                    return
                
            
        else :
            print("Empty Linked List", end = "")
        
    
    # Find last node of linked list
    def last_node(self) :
        temp = self.head
        while (temp != None and temp.next != None) :
            temp = temp.next
        
        return temp
    
    # Set of given last node position to its proper position
    def parition(self, first, last) :
        # Get first node of given linked list
        pivot = first
        front = first
        temp = 0
        while (front != None and front != last) :
            if (front.data < last.data) :
                pivot = first
                # Swap node value
                temp = first.data
                first.data = front.data
                front.data = temp
                # Visit to next node
                first = first.next
            
            # Visit to next node
            front = front.next
        
        # Change last node value to current node
        temp = first.data
        first.data = last.data
        last.data = temp
        return pivot
    
    # Perform quick sort in given linked list
    def quick_sort(self, first, last) :
        if (first == last) :
            return
        
        # Find pivot node
        pivot = self.parition(first, last)
        if (pivot != None and pivot.next != None) :
            self.quick_sort(pivot.next, last)
        
        if (pivot != None and first != pivot) :
            self.quick_sort(first, pivot)
        
    

def main() :
    obj = MyLinkedList()
    # Create linked list
    obj.insert(41)
    obj.insert(5)
    obj.insert(7)
    obj.insert(22)
    obj.insert(28)
    obj.insert(63)
    obj.insert(4)
    obj.insert(8)
    obj.insert(2)
    obj.insert(11)
    print("\n Before Sort ", end = "")
    obj.display()
    obj.quick_sort(obj.head, obj.last_node())
    print("\n After Sort ", end = "")
    obj.display()

if __name__ == "__main__": main()

【问题讨论】:

“我没有节点类、链表类等”:你的第一个代码块有它们。那么你想要什么:是否有类的解决方案?如果您只包括一次尝试,并准确指出它的问题所在,那就太好了。目前你的问题“任何想法”太宽泛了。 简单看一下我目前的快速排序,不难看出效率不高,感觉还可以改进。没有类的提及是指不同的代码(我已删除以避免混淆)并且原始帖子已被更正。 ~~~~ 我是否应该将其作为一种解决方法:将数据值放入数组中,使用快速排序对数组进行排序,然后将所有内容追加回链表? 如果您的代码有效,并且您要求在效率(或最佳实践等)方面进行审查,那么这个问题更适合CodeReview 【参考方案1】:

虽然您的代码似乎可以工作,但有时需要不要将值从一个节点移动到另一个节点,而是移动节点本身(保持节点值与节点实例相关联)。

以下是我建议在单链表中实现类似快速排序的算法的方法。我删除了tail 属性并将大部分逻辑移到Node 类中:

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

    def __iter__(self):
        curr = self
        while curr:
            node = curr
            curr = curr.next
            yield node

    def values(self):
        return (node.data for node in self)

    def partition(self):
        nodes = iter(self)
        next(nodes)  # skip self
        left = self   # left partition always has pivot node as its tail
        pivotvalue = self.data
        right = None
        for curr in nodes:
            # Prefix the current node to the relevant partition
            if curr.data < pivotvalue:
                curr.next = left
                left = curr
            else:
                curr.next = right
                right = curr
        self.next = None  # Terminate the left partition
        return left, right 
        
    def quick_sort(self):
        if not self.next:  # Base case: one node only
            return self
        left, right = self.partition()
        # Left partition has at least one node (the pivot node, which remains its tail)
        left = left.quick_sort()
        # Right partition could be empty 
        if right:
            right = right.quick_sort()
        self.next = right  # Chain the two sorted partitions
        return left

    def is_sorted(self):
        values = self.values()
        prev = next(values)
        for data in values:
            if data < prev:
                return False
            prev = data
        return True


class MyLinkedList:
    def __init__(self, *values):
        self.head = None
        self.prefix(*values)
    
    def prefix(self, *values):
        for data in reversed(values):
            self.head = Node(data, self.head)
            
    def values(self):
        if self.head:
            return self.head.values()

    def __str__(self):  # Preferred over a display() method
        return "[" + "->".join(map(str, self.values())) + "]"

    def quick_sort(self):
        self.head = self.head and self.head.quick_sort()

    def is_sorted(self):
        return self.head is not None and self.head.is_sorted()

    
from random import shuffle

def main():
    # Perform 100 tests with shuffled lists of 20 values
    for _ in range(100):
        values = list(range(20))
        shuffle(values)

        obj = MyLinkedList(*values)
        print("Before Sort")
        print(obj)
        obj.quick_sort()
        print("After Sort")
        print(obj)
        if not obj.is_sorted():
            raise ValueError("not sorted!")

if __name__ == "__main__":
    main()

【讨论】:

我完全重写了我的答案。让我知道这是否有用。

以上是关于如何干净地使用 QuickSort 对链表进行排序 - Python的主要内容,如果未能解决你的问题,请参考以下文章

使用插入排序对链表进行并行排序

C语言如何对链表的数进行排序?

在C中对链表进行排序[关闭]

在 C 中对链表进行排序

使用优先队列对链表进行排序

[Leetcode] Sort list 对链表进行排序