[Quick Sort And Quick Selection Algorithm]快速排序和快速选择算法

209 阅读3分钟

快速排序简介

快速排序是一种高效的排序算法,平均时间复杂度为 O(nlog⁡n),最坏情况下为 O(n^2)。它通过选定一个基准(pivot),将数组划分为小于和大于基准的部分,然后递归地对这两部分排序。其优势在于分治策略和对大部分情况下的高效性能。

实现方法概览

  1. 基础快速排序:最右元素为基准。
  2. 双指针法快速排序:双指针交替缩进。
  3. 快速选择(Quickselect) :用于查找数组第 k 大元素。

注:图片来自Krahets 原链接🔗 image.png


在这篇博客中,我们将详细探讨快速排序的三种常用实现方法,包括基础递归实现、双指针分区方法以及随机基准的快速选择(Quickselect)算法。这些方法的核心思想是分治法,即通过选取基准将数组分为小于和大于基准的两个部分,再对这两部分递归进行排序。我们将通过每种实现的代码及详细说明,帮助您更好地理解快速排序的工作原理。


方法一:基础快速排序

def quick_sort(arr, left, right):
    if left < right:
        pivot_index = partition(arr, left, right)
        quick_sort(arr, left, pivot_index - 1)
        quick_sort(arr, pivot_index + 1, right)

def partition(arr, left, right):
    pivot = arr[right]  # 选择最右边元素为基准
    i = left - 1  # 初始化 i 为小于基准的边界
    for j in range(left, right):
        if arr[j] < pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]  # 将小于基准的元素移到左侧
    arr[i + 1], arr[right] = arr[right], arr[i + 1]  # 基准放到正确位置
    return i + 1  # 返回基准的索引

详细步骤

  1. 选择基准:将最右侧元素选为基准。
  2. 分区:通过 i 指针将数组划分为小于和大于基准的部分,j 遍历数组,将小于基准的元素移到左边。
  3. 递归:递归对左右子数组排序,直到所有子数组有序。

方法二:双指针法快速排序

def quick_sort(arr, left, right):
    if left < right:
        pivot_index = partition(arr, left, right)
        quick_sort(arr, left, pivot_index - 1)
        quick_sort(arr, pivot_index + 1, right)

def partition(arr, left, right):
    pivot = arr[left]  # 选择最左边元素为基准
    l, r = left, right
    while l < r:
        while l < r and arr[r] >= pivot:  # 从右找到小于基准的元素
            r -= 1
        arr[l] = arr[r]
        while l < r and arr[l] <= pivot:  # 从左找到大于基准的元素
            l += 1
        arr[r] = arr[l]
    arr[l] = pivot  # 基准放到正确位置
    return l  # 返回基准的索引

详细步骤

  1. 选择基准:选择数组的最左侧元素。
  2. 分区:通过双指针移动,l 指向大于基准的第一个元素,r 指向小于基准的第一个元素,然后交换二者。
  3. 递归:递归对左右子数组进行排序。

方法三:快速选择(Quickselect)

快速选择是一种基于快速排序的算法,用于查找数组的第 k 大元素。其原理类似于快速排序,通过基准分区,但只递归处理包含第 k 大元素的那部分子数组。

import random

class Solution:
    def findKthLargest(self, nums, k):
        def quick_select(nums, k):
            pivot = random.choice(nums)  # 随机选择基准
            big, equal, small = [], [], []
            for num in nums:
                if num > pivot:
                    big.append(num)
                elif num < pivot:
                    small.append(num)
                else:
                    equal.append(num)
            if k <= len(big):
                return quick_select(big, k)
            if len(nums) - len(small) < k:
                return quick_select(small, k - len(nums) + len(small))
            return pivot
        return quick_select(nums, k)

详细步骤

  1. 选择基准:随机选择一个基准元素。

  2. 分区:根据基准,将数组分为三部分:big(大于基准)、equal(等于基准)、small(小于基准)。

  3. 递归

    • 若第 k 大元素在 big 中,则递归查找 big
    • 若第 k 大元素在 small 中,则递归查找 small
    • 若在 equal 中,直接返回基准值。

引用

Krahets 215. 数组中的第 K 个最大元素(分治,清晰图解)