算法-快排

76 阅读3分钟

快排概念

随机从数组中取一个数字 pivot,将数组根据 arr[i]放在合适的位置,求出arr[i]的相等区间[n,m],对[left,n-1] 和 [m+1,right]继续进行分区,直到left>=right,停止。

分区Partition

二等分Partition

给定一个数组arr,一个数字pivot,将小于pivot的数放在数组左边,大于pivot的数放在数组右边。

利用left、right指针,其中 [0...left]代表比pivot小的数,[right...n-1]代表大于等于pivot的数。

  • 初始化时,left=-1, right=n
  • if arr[i] <= pivot,
def partition(arr, pivot):
    n = len(arr)
    left = -1
    right = n
    i = 0
    while i < right:
        if arr[i] <= pivot:
            left += 1
            i += 1
        else:
            right -= 1
            arr[right], arr[i] = arr[i], arr[right]
    return arr

三等分Partition

区分为 <pivot、=pivot、>pivot区间。

<pivot=pivot>pivot
...,leftleft+1, right-1right,...

只需要在二等分的判断基础上修改即可。

  • if arr[i] == pivot, then i++
  • if arr[i] < pivot, then left++, swap(arr[left], arr[i]), i++

a、if i == left+1,直接left++,i++ b、if i != left+1,说明 arr[left+1,...,i-1]也一定是pivot,此时left应该右扩left++,设为left',此时对新的left'和i进行交换后,此时arr[left'+1,...,i]也是=num的区间,此时i++。

综上两种情况,不论是a还是b,可以总结为 left++,swap(left, i),i++,包括了这两种情况。

def partition(arr, pivot):
    n = len(arr)
    left = -1
    right = n
    i = 0
    while i < right:
        if arr[i] < pivot:
            left += 1
            arr[left], arr[i] = arr[i], arr[left]
            i += 1
        elif arr[i] == pivot:
            i += 1
        else:
            right -= 1
            arr[right], arr[i] = arr[i], arr[right]
    return arr

快排

思路

主函数

  • 对arr[l...r]中以arr[r]为pivot,采用3等分partition,获取到=pivot的区间,[n,m]
  • 针对arr[l...n-1]和arr[m+1,r]继续进行3等分partition,直到l>=r位置

入口

  • 调用主函数,参数为 arr,0,len(arr)-1

优化

  • 对arr[r]进行随机取值

原因: 划分值越靠近中间,性能越好;越靠近两边,性能越差。随机选一个数进行划分的目的就是让好情况和坏情况都变成概率事件,把每一种情况都考虑,时间复杂度就是这种概率模型下的长期期望。

def partition(arr, l, r):
    """功能:将arr[r]放置到它的位置"""
    # 默认arr[r]是pivot
    less = l - 1
    more = r
    while l < more:
        if arr[l] < arr[r]:
            less += 1
            arr[less], arr[l] = arr[l], arr[less]
            l += 1
        elif arr[l] > arr[r]:
            more -= 1
            arr[more], arr[l] = arr[l], arr[more]
        else:
            l += 1

    # 将arr[r]和more交换,相当于=arr[r]的值区间从left+1...more结束
    arr[r], arr[more] = arr[more], arr[r]
    return [less+1, more]

import random
def quick_sort(arr, l, r):
    """功能:不断将每个元素放置到正确位置"""
    if l < r:
        # 随机取出一个位置,和 r位置替换
        n = l + int(random.random() * (r-l+1))
        arr[r], arr[n] = arr[n], arr[r]
        # 找到arr[r]的相等区间[e1, e2]
        e1, e2 = partition(arr, l, r)
        quick_sort(arr, l, e1-1)
        quick_sort(arr, e2+1, r)


def quickSort(arr):
    """快排主函数"""
    if arr is None or len(arr) < 2:
        return
    return quick_sort(arr, 0, len(arr)-1)