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

人气:904 发布:2022-10-16 标签: python list sorting linked-list quicksort

问题描述

使用快速排序对链接列表进行排序的最简洁方法是什么?我目前有以下内容,但不是很好。我想要一个类似Sort(Self)的函数,这样我就可以简单地使用list.sort(),并且我可以使用快速排序方法对我的链表进行排序。

潜在的方法,但不确定如何实现:从当前列表(Self)开始,让Pivot作为列表头部的数据,并创建两个新的链表:一个称为Small(包含数据小于Pivot的所有元素),另一个(包含除Pivot以外的所有剩余元素)。然后,调用Smaller.sort()和ther.sorte(),将当前列表设置为较小,然后追加Pivot并与其他列表合并。

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

# 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("
 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("
 Before Sort ", end = "")
    obj.display()
    obj.quick_sort(obj.head, obj.last_node())
    print("
 After Sort ", end = "")
    obj.display()

if __name__ == "__main__": main()

推荐答案

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

下面是我建议如何在单链表中实现类似快速排序的算法。我删除了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()

480