13

我必须用我选择的语言为作业实现 QuickSort 算法,我选择了 Python。

在讲座中,我们被告知 QuickSort 是内存高效的,因为它可以就地工作。即,它没有用于递归的输入数组部分的额外副本。

考虑到这一点,我尝试在 Python 中实现 QuickSort 算法,但不久之后意识到,为了编写一段优雅的代码,我必须在递归时将数组的一部分传递给函数本身。由于每次执行此操作时 Python 都会创建新列表,因此我尝试使用 Python3(因为它支持 nonlocal 关键字)。以下是我的注释代码。

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)

现在,我不确定我是否做得很好,或者我错过了什么。我已经编写了一个更加 Pythonic 的 QuickSort 版本,但这肯定不能就地工作,因为它会不断返回输入数组的一部分并将它们连接起来。

我的问题是,这是用 Python 做的吗?我已经搜索了 Google 和 SO,但还没有找到真正的 QuickSort 就地实现,所以我认为最好问问。

4

7 回答 7

17

当然不是最好的方法,再加上这个著名的算法会有几十个完美的实现..这是我的,很容易理解

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)

好的,首先是分区子程序的单独函数。它采用数组、感兴趣的起点和终点以及枢轴的索引。这个功能应该清楚

然后快速排序在整个数组上第一次调用分区子程序;然后调用 recursevely 本身将所有内容排序到枢轴和之后的所有内容。

问你是否不明白

于 2013-07-21T14:50:14.980 回答
2

我最近开始学习python,以下是我使用python实现快速排序的尝试。希望它是有帮助的。欢迎反馈:)

#!/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() 
于 2014-04-04T10:03:39.160 回答
1

这是另一个实现:

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 
于 2016-01-21T23:38:15.127 回答
0

这就是我想出的。该算法是就地的,看起来不错并且是递归的。

# `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
于 2015-07-26T07:08:24.610 回答
0

说明:Pivot 始终是给定数组中的最后一个元素。在我的方法中,我跟踪小于和大于枢轴的数字之间的“边界”。边框是“更大”组中第一个数字的索引。在每次迭代结束时,我们将“边界”下的数字与枢轴数字交换。

和代码:

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)
于 2017-07-30T20:33:31.307 回答
0
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]))
于 2018-10-31T07:17:40.740 回答
0

以枢轴为最右侧元素的就地实现

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)

解释 - 分区

1.将枢轴作为“结束” - 最右边的元素
2.将枢轴按排序顺序移至其正确位置。
3.比较元素与枢轴元素
    当检查所有元素时循环中断,由[begin]重叠[pivot]指示。begin==pivot
        如果 elem[begin] > elem[pivot]
            元素大于 elem[pivot]。它在错误的地方。
            如果 (pivot-1) != begin
            交换元素 [pivot] 和 [begin]则必须
            在枢轴交换元素 [pivot] 和 [pivot-1] 之后在当前 [begin] 位置,
            无论哪种方式,pivot 都向左移动了一个位置。所以将枢轴减1
        Else
            开始的元素是 <= 到枢轴和枢轴的左侧。
            所以它在一个可接受的位置
            增量开始检查下一个元素

说明 - 快速排序

1.选择一个枢轴,将其放置在数组中的正确位置并使用“partition()”方法获取其索引。
2.使用begin,end和partition_index将
数组划分为pivot的左侧和右侧 3.在pivot的左侧和右侧数组上调用快速排序

于 2020-07-28T17:10:37.963 回答