快速排序算法及其时间复杂度分析

本文详细介绍了快速排序算法,包括其工作原理(选择基准值、分区操作和递归排序),以及在不同情况下的时间复杂度分析,指出通过优化策略可提高平均性能。
摘要由CSDN通过智能技术生成

综述

快速排序算法是一种高效的排序算法,它采用了分治法的思想来进行排序。在快速排序中,我们选取一个元素作为基准值,通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

示例

1. 初始状态

假设我们有一个无序的数组 [5, 3, 8, 6, 9, 2, 7]

2. 选择基准值

在这个例子中,我们选择数组的第一个元素 5 作为基准值。

3. 划分数组

将数组中比基准值小的元素放在它的左边,比基准值大的元素放在它的右边。这一步也称为分区操作。

分区后的数组为:[2, 3, 5, 6, 9, 8, 7]

4. 递归排序子数组

递归地对基准值左边的子数组 [2, 3] 和右边的子数组 [6, 9, 8, 7] 进行快速排序。即重复前面123的步骤

5. 合并有序子数组

当递归到子数组长度为10时,停止递归。然后,将两个有序子数组合并成一个有序数组。

合并后的数组为:[2, 3, 5, 6, 7, 8, 9]

代码

以下是代码示例

python代码

def quick_sort(arr):
    """
    这段代码实现的是经典快速排序算法,其基本思想是选择一个基准元素,
    将列表分成两个子数组,小于等于基准的元素放在左边,大于基准的元素放在右边,
    然后递归地对左右两个子列表进行排序。
    :param arr: 原排序前无序列表
    :return: 新排序后有序列表
    """
    # 递归终止条件:列表长度小于或等于1,直接返回列表
    if len(arr) <= 1:
        return arr
    # 选择基准元素,这里选择中间元素
    pivot = arr[len(arr) // 2]
    # 创建三个列表,分别存储小于、等于、大于基准的元素
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    # 递归调用快速排序,将小于和大于基准的元素分别排序,再拼接中间元素,返回结果
    return quick_sort(left) + middle + quick_sort(right)


if __name__ == '__main__':
    arr = [5, 3, 8, 6, 9, 2, 7]
    print(quick_sort(arr))

java代码

public class QuickSort {
    public static void main(String[] args) {
        int[] arr = new int[]{5, 3, 8, 6, 9, 2, 7};
        quickSort(arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(arr));
    }

    /***
     * 这段代码实现的是经典快速排序算法,其基本思想是选择一个基准元素,
     * 将数组分成两个子数组,小于等于基准的元素放在左边,大于基准的元素放在右边,
     * 然后递归地对左右两个子数组进行排序。
     * 其中partition函数用于将小于等于基准的元素放在左边,大于基准的元素放在右边,并返回基准的索引。
     * @param arr 原排序前无序列表
     * @param left arr的起始索引
     * @param right arr的结束索引
     */
    private static void quickSort(int[] arr, int left, int right) {
        // 递归终止条件:左指针大于等于右指针,返回
        if (left >= right) {
            return;
        }
        // 选择基准元素,这里选择最右边的元素
        int pivotIndex = partition(arr, left, right);
        // 递归调用快速排序,对基准左边和右边的子数组分别进行排序
        quickSort(arr, left, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, right);
    }

    private static int partition(int[] arr, int left, int right) {
        // 选择基准元素,这里选择最右边的元素
        int pivot = arr[right];
        int i = left - 1;
        // 遍历数组,将小于等于基准的元素放在左边,大于基准的元素放在右边
        for (int j = left; j < right; j++) {
            if (arr[j] <= pivot) {
                i++;
                // 交换元素位置
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        // 将基准放在正确的位置上
        int temp = arr[i + 1];
        arr[i + 1] = arr[right];
        arr[right] = temp;
        // 返回基准的索引
        return i + 1;
    }
}

时间复杂度分析

下面分别从最好、平均和最坏情况来介绍快速排序的时间复杂度。

1. 最好情况- O(nlogn)

当每次划分都能将数组分成两个等长的子数组时,快速排序的时间复杂度为 O(nlogn)。这是因为每次划分都能将数组分成两个等长的子数组,所以递归树的深度为 logn,而每个节点都需要进行一次比较和一次交换操作,所以总的时间复杂度为 O(nlogn)

2. 最坏情况- O(n^2)

当每次划分都不能将数组分成两个等长的子数组时,快速排序的时间复杂度为 O(n^2)。这是因为每次划分只能将数组分成一个比原数组更小的子数组和一个比原数组更大的子数组,所以递归树的深度为 n,每个节点都需要进行一次比较和一次交换操作,所以总的时间复杂度为 O(n^2)

3. 平均情况- O(nlogn)

快速排序的平均时间复杂度为 O(nlogn)。这是因为每次划分都能将数组分成两个等长的子数组的概率是存在的,所以快速排序的平均时间复杂度为 O(nlogn)

总结

快速排序的时间复杂度取决于基准值的选择,最好情况是 O(nlogn),最坏情况是 O(n^2)。然而,在实际应用中,通过随机化选择基准值或使用其他技巧,可以使得快速排序在大多数情况下表现良好。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值