排序算法-交换排序-快速排序

排序算法-交换排序-快速排序

算法思想

快速排序是对冒泡排序的一种改进,其基本思想是基于分治算法的。
在待排序表 L [ 0... n ] L[0...n] L[0...n]中任取一个元素pivot作为基准,通过一趟排序将待排序表划分为独立的两个部分 L [ 0... k − 1 ] L[0...k-1] L[0...k1] L [ k + 1... n ] L[k+1...n] L[k+1...n],使得 L [ 0... k − 1 ] L[0...k-1] L[0...k1]中的元素小于pivot, L [ k + 1... n ] L[k+1...n] L[k+1...n]中的元素大于或等于pivot,则pivot放在了最终的位置 L ( k ) L(k) L(k)上,这个过程称为一趟快速排序。

每一次排序都会使得确定一个中间位置的元素,即本次排序的枢轴元素被放到了最终的位置上。

而后分别递归地对两个子表重复上述过程,直至每部分内只有一个元素或者元素为空为止,即所有元素放在了其最终位置上。

每一趟排序的代码如下,起到划分的作用。
public static int Partition(int[] nums, int low, int high){
        // 以子表的第一个元素为枢轴进行划分,划分完即完成一次排序过程。
        int pivot = nums[low];  // 设定枢轴值
        while(low<high){        // 循环结束条件
            while(low<high && nums[high]>=pivot) --high;
            nums[low] = nums[high];  //将比枢轴值小的元素移动到左端
            while(low<high && nums[low]<=pivot) ++low;
            nums[high] = nums[low];  //将比枢轴值大的元素移动到右端
        }
        nums[low] = pivot;  // 枢轴元素存放到最终位置
        return low;         // 返回枢轴元素的下标
    }

在这里插入图片描述
最终图中小于下标小于low的元素的值小于等于25(pivot),下标大于low的元素值大于等于25.

经过一次partition之后,枢轴值已经在最终的位置上了,所以可以对枢轴值左边的子表和右边的子表进行同样的操作,达到排序的目的。代码如下:

public static void quicksort(int[] nums, int left, int right){
        if(left<right){ //递归结束的条件
            // partition将nums以枢轴划分为两个表,枢轴右边的元素大于枢轴,左边的元素小于枢轴。
            int pivotpos = Partition(nums, left, right);
            quicksort(nums, left, pivotpos-1); // 对左表进行递归排序
            quicksort(nums, pivotpos+1, right); // 对右表进行递归排序
        }
    }
完整代码:
public class QuickSort {
    public static void main(String[] args) {
        int[] nums = {3,8,4,5,6,8,9,0};
        quicksort(nums,0,nums.length-1);
        for(int num:nums){
            System.out.print(num);
        }
    }
    public static void quicksort(int[] nums, int left, int right){
        if(left<right){ //递归结束的条件
            // partition将nums以枢轴划分为两个表,枢轴右边的元素大于枢轴,左边的元素小于枢轴。
            int pivotpos = Partition(nums, left, right);
            quicksort(nums, left, pivotpos-1); // 对左表进行递归排序
            quicksort(nums, pivotpos+1, right); // 对右表进行递归排序
        }
    }
    public static int Partition(int[] nums, int low, int high){
        // 以子表的第一个元素为枢轴进行划分,划分完即完成一次排序过程。
        int pivot = nums[low];  // 设定枢轴值
        while(low<high){        // 循环结束条件
            while(low<high && nums[high]>=pivot) --high;
            nums[low] = nums[high];  //将比枢轴值小的元素移动到左端
            while(low<high && nums[low]<=pivot) ++low;
            nums[high] = nums[low];  //将比枢轴值大的元素移动到右端
        }
        nums[low] = pivot;  // 枢轴元素存放到最终位置
        return low;         // 返回枢轴元素的下标
    }
}
复杂度分析:

空间效率:由于快速排序是递归的,需要借助一个递归工作栈来保存每一层递归调用的必要信息,其容量应与递归调用的最大深度一致。最好情况下是 ⌈ l o g 2 ( n + 1 ) ⌉ \lceil log_{2}(n+1) \rceil log2(n+1)(或 ⌊ l o g 2 n ⌋ + 1 \lfloor log_{2}n\rfloor +1 log2n+1),也就是有n个节点二叉树的最小高度;最坏情况下,因为要进行n-1次递归调用,所以栈的深度为 O ( n ) O(n) O(n),也就是有n个节点的二叉树的最大高度;平均情况下,栈的深度为 O ( l o g 2 n ) O(log_{2}n) O(log2n).

时间效率
快速排序的运行时间与划分是否对称有关,而后者又与具体使用的划分算法有关,即取决于枢轴值的选取。每次partition的时间复杂度为 O ( n ) O(n) O(n),整个排序算法的时间复杂度为 O ( n ∗ 递 归 深 度 ) O(n*递归深度) O(n)。最坏情况发生在两个区域分别包含n-1和0个元素时,这种最大程度的不对称性发生在每一层递归上,即对应于初始排序基本有序或者基本逆序时,递归深度为n数量级,就得到最坏情况下的时间复杂度为 O ( n 2 ) O(n^{2}) O(n2).最理想的情况下,也即Partition()可能做到的最平衡的划分中,得到的两个子问题的大小都不可能大于n/2,递归深度为logn数量级,此时,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn).

快速排序平均情况下运行时间与其最佳情况下运行时间很接近,而不是接近其最坏情况下的运行时间。快速排序是内部排序算法中平均性能最优的排序算法。

稳定性:不稳定。在划分算法中,若右端区间存在两个关键字相同,且小于枢轴值的元素时,在交换到左端区间后,他们的位置会发生变化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山风wind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值