简体   繁体   English

Python 中的就地快速排序

[英]In-place QuickSort in Python

I had to implement the QuickSort algorithm for a homework in a language of my choice and I chose Python.我必须用我选择的语言为作业实现 QuickSort 算法,我选择了 Python。

During the lectures, we've been told that QuickSort is memory efficient because it works in-place;在讲座中,我们被告知 QuickSort 是内存高效的,因为它就地工作; ie, it has no additional copies of parts of the input array for recursions.即,它没有用于递归的输入数组部分的额外副本。

With this in mind, I tried to implement the QuickSort algorithm in Python, but shortly afterwards realized that in order to write an elegant piece of code I would have to pass parts of the array to the function itself while recursing.考虑到这一点,我尝试在 Python 中实现 QuickSort 算法,但不久之后意识到为了编写一段优雅的代码,我必须在递归时将数组的一部分传递给函数本身。 Since Python creates new lists each time I do this, I have tried using Python3 (because it supports the nonlocal keyword).由于每次执行此操作时 Python 都会创建新列表,因此我尝试使用 Python3(因为它支持 nonlocal 关键字)。 The following is my commented code.以下是我注释的代码。

def quicksort2(array):
# Create a local copy of array.
arr = array

    def sort(start, end):
        # Base case condition
        if not start < end:
            return

        # Make it known to the inner function that we will work on arr
        # from the outer definition
        nonlocal arr

        i = start + 1
        j = start + 1

        # Choosing the pivot as the first element of the working part
        # part of arr
        pivot = arr[start]

        # Start partitioning
        while j <= end:
            if arr[j] < pivot:
                temp = arr[i]
                arr[i] = arr[j]
                arr[j] = temp
                i += 1
            j += 1
        temp = arr[start]
        arr[start] = arr[i - 1]
        arr[i - 1] = temp
        # End partitioning

        # Finally recurse on both partitions
        sort(start + 0, i - 2)
        sort(i, end)
    sort(0, len(array) - 1)

Now, I'm not sure whether I did the job well or am I missing something.现在,我不确定我是否做得很好,还是我遗漏了什么。 I have written a more Pythonic version of QuickSort but that surely doesn't work in-place because it keeps returning parts of the input array and concatenates them.我写了一个更 Pythonic 的 QuickSort 版本,但这肯定不能就地工作,因为它不断返回输入数组的一部分并将它们连接起来。

My question is, is this the way of doing it in Python?我的问题是,这是在 Python 中的做法吗? I have searched both Google and SO but haven't found a true in-place implementation of QuickSort, so I thought it'd be best to ask.我已经搜索了 Google 和 SO,但还没有找到真正的 QuickSort 就地实现,所以我认为最好问一下。

Sure not the best way, plus this famous algorithm will have dozens of perfect implementations.. this is mine, quite easy to understand当然不是最好的方法,加上这个著名的算法会有几十个完美的实现..这是我的,很容易理解

def sub_partition(array, start, end, idx_pivot):

    'returns the position where the pivot winds up'

    if not (start <= idx_pivot <= end):
        raise ValueError('idx pivot must be between start and end')

    array[start], array[idx_pivot] = array[idx_pivot], array[start]
    pivot = array[start]
    i = start + 1
    j = start + 1

    while j <= end:
        if array[j] <= pivot:
            array[j], array[i] = array[i], array[j]
            i += 1
        j += 1

    array[start], array[i - 1] = array[i - 1], array[start]
    return i - 1

def quicksort(array, start=0, end=None):

    if end is None:
        end = len(array) - 1

    if end - start < 1:
        return

    idx_pivot = random.randint(start, end)
    i = sub_partition(array, start, end, idx_pivot)
    #print array, i, idx_pivot
    quicksort(array, start, i - 1)
    quicksort(array, i + 1, end)

Ok first a seperate function for the partition subroutine.好的,首先是分区子程序的单独函数。 It takes the array, the start and end point of interest, and the index of pivot.它需要数组、感兴趣的起点和终点以及枢轴的索引。 This functions should be clear这个功能应该清楚

Quicksort then call the partition subroutine for the first time on the whole array;然后快速排序在整个数组上第一次调用分区子程序; then call recursevely itself to sort everything up to the pivot and everything after.然后调用 recursevely 本身将所有内容排序到主元和之后的所有内容。

ask if you dont understand something问你是否有不明白的地方

I have started learning python lately and the following is my attempt at implementing quicksort using python.我最近开始学习python,以下是我使用python实现quicksort的尝试。 Hope it is helpful.希望它有帮助。 Feedback is welcomed :)欢迎反馈:)

#!/usr/bin/python

Array = [ 3,7,2,8,1,6,8,9,6,9]

def partition(a, left, right):

    pivot = left + (right - left)/2
    a[left],a[pivot] = a[pivot], a[left] # swap
    pivot = left
    left += 1

    while right >= left :
        while left <= right and a[left] <= a[pivot] :
            left += 1
        while left <= right and a[right] > a[pivot] :
            right -= 1

        if left <= right:
            a[left] , a[right] = a[right], a[left] # swap
            left += 1
            right -= 1
        else:
            break

    a[pivot], a[right] = a[right] , a[pivot]

    return right


def quicksort(array , left,right):
    if left >= right:
        return  
    if right - left == 1:
        if array[right] < array[left]:
            array[right], array[left] = array[left] , array[right]
            return           

    pivot = partition(array, left, right)

    quicksort(array, left, pivot -1)
    quicksort(array, pivot+1,right)         

def main():
    quicksort(Array, 0 , len(Array) -1)   
    print Array 

main() 

Here is another implementation:这是另一个实现:

def quicksort(alist):
    if len(alist) <= 1:
        return alist
    return part(alist,0,len(alist)-1)

def part(alist,start,end):
    pivot = alist[end]  
    border = start
    if start < end:  
        for i in range(start,end+1):
            if alist[i] <= pivot:
                alist[border], alist[i] = alist[i], alist[border]
                if i != end:
                    border += 1
        part(alist,start,border-1)  
        part(alist,border+1,end)  
    return alist 

Here's what I came up with.这是我想出的。 The algorithm is in-place, looks nice and is recursive.该算法是就地的,看起来不错并且是递归的。

# `a` is the subarray we're working on
# `p` is the start point in the subarray we're working on
# `r` is the index of the last element of the subarray we're working on

def part(a,p,r):
   k=a[r] #pivot 
   j,q=p,p
   if p<r: # if the length of the subarray is greater than 0
       for i in range(p,r+1):
           if a[i]<=k:
               t=a[q]
               a[q]=a[j]
               a[j]=t
               if i!=r:
                   q+=1
               j+=1
           else:
               j+=1
       part(a,p,q-1) # sort the subarray to the left of the pivot
       part(a,q+1,r) # sort the subarray to the right of the pivot  
   return a
def quicksort(a):
   if len(a)>1:
      return part(a,0,len(a)-1)
   else:
      return a

Explanation: Pivot is always the last element in given array.说明:枢轴始终是给定数组中的最后一个元素。 In my approach I keep track of the 'border' between numbers smaller and bigger than pivot.在我的方法中,我跟踪小于和大于枢轴的数字之间的“边界”。 Border is an index of first number in 'bigger' group. Border 是“更大”组中第一个数字的索引。 At the end of each iteration we swap number under 'border' with the pivot number.在每次迭代结束时,我们将“边界”下的数字与枢轴编号交换。

And the code:和代码:

def qs(ar, start, end):
    if (end-start < 1):
        return
    if (end-start == 1):
        if(ar[start] > ar[end]):
            tmp = ar[start]
            ar[start] = ar[end]
            ar[end] = tmp
        return
    pivot = ar[end - 1]
    border_index = start
    i = start
    while(i <= end - 1):
        if (ar[i] < pivot):
            if i > border_index:
                tmp = ar[i]
                ar[i] = ar[border_index]
                ar[border_index] = tmp
            border_index += 1
        i+=1
    ar[end-1] = ar[border_index]
    ar[border_index] = pivot
    qs(ar, start, border_index)
    qs(ar, border_index + 1, end)

qs(ar, 0, n)
def quicksort(arr):
    lesser = []
    equal = []
    greater = []
    if len(arr)>1:
        pivot = arr[len(arr)-1]
        start = 0
        while start<len(arr):
            if arr[start] > pivot:
                greater.append(arr.pop(start)) 
            elif arr[start] < pivot:
                lesser.append(arr.pop(start))
            else:
                start = start + 1

        return (quicksort(lesser) + arr + quicksort(greater))

    else:
        return (arr)

print(quicksort([2,333,-22,0,54, 22, 37, 0.3, -2, 22]))

An in place implementation with pivot as the right most element以枢轴为最右侧元素的就地实现

def partition(array, begin, end):
    pivot = end
    while begin < pivot:
        if array[begin] > array[pivot]:
            if begin != pivot - 1:
                array[pivot], array[pivot - 1] = array[pivot - 1], array[pivot]
            array[begin], array[pivot] = array[pivot], array[begin]
            pivot-=1
        else:
            begin+=1
    return pivot



def quicksort(array, begin=0, end=None):
    if end is None:
        end = len(array) - 1
    if begin < end:
        partition_index = partition(array, begin, end)
        quicksort(array, begin, (partition_index - 1))
        quicksort(array, (partition_index + 1), end)

Explanation - partition说明 - 分区

1.Taking pivot as "end" - right most element 1.以支点为“结束”——最右边的元素
2.Bring pivot to its right position in sorted order. 2.按排序顺序将枢轴带到正确的位置。
3.Comparing element at begin with pivot element 3.比较开始时的元素与枢轴元素
Loop breaks when all elements are checked indicated by [begin] overlapping [pivot].当所有元素都被检查时循环中断,由 [begin] 重叠 [pivot] 指示。 begin==pivot开始==支点
If elem[begin] > elem[pivot]如果 elem[begin] > elem[pivot]
The element is greater than elem[pivot].元素大于 elem[pivot]。 It is at a wrong place.它在错误的地方。 It has to come after pivot它必须在枢轴之后出现
Swap elements [pivot] and [pivot-1] if (pivot-1) != begin交换元素 [pivot] 和 [pivot-1] if (pivot-1) != begin
Swap elements [pivot] and [begin]交换元素 [pivot] 和 [begin]
Now the element at pivot is either in position [pivot-1] or at current [begin] position现在枢轴处的元素位于 [pivot-1] 位置或当前 [begin] 位置
Either way pivot has moved one place to the left.无论哪种方式,枢轴都向左移动了一个位置。 So decrement pivot by 1所以将枢轴减 1
Else别的
element at begin is <= to pivot and to the left of pivot.元素在开始是 <= 枢轴和枢轴的左侧。
So it is at an acceptable position所以它处于可以接受的位置
Increment begin to check for next element增量开始检查下一个元素

Explanation - quick sort说明 - 快速排序

1.Choosing a pivot, placing it in correct position in array and getting its index using the method "partition()". 1.选择一个枢轴,将其放置在数组中的正确位置并使用“partition()”方法获取其索引。
2.Dividing array into left side and right side of pivot using begin, end and partition_index 2.使用begin、end和partition_index将数组划分为pivot的左侧和右侧
3.Calling quicksort on left and right side array of pivot 3.在pivot的左右侧数组上调用quicksort

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM