快速排序算法及分析

快速排序的思路是依据一个“中值”数据项来把数据表分成两半:小于中值的一半和大于中值的一半,然后每部分分别进行快速排序(递归)

如果希望这两半拥有相等数量的数据项,则应该找到数据表的“中位数”

但找中位数需要计算开销,要想没有开销,只能随意找一个数来充当“中值”

 快速排序的递归算法“递归三要素如下”

  • 基本结束条件:数据表只有一个数据项,因此不需要排序
  • 缩小规模:根据“中值”,将数据表分为两半,最好情况是相等规模的两半
  • 调用自身:将两半分别调用自身排序(排序的基本操作在分裂过程中)

分裂数据表的目标:找到“中值”的位置 

分裂数据的手段

设置左右标(left / right)

右标向左移动​​​​​,左标向右移动   

  •   右标一直向左移动,碰到比中值小的数就停止 
  •   将右标做指位置的数值覆盖掉左标所指向位置的数值
  •   左标一直向右移动,碰到比中值打的数就停止
  •   将左标做指位置的数值覆盖掉右标所指向位置的数值

继续移动,直到左标等于右标,停止移动

这时左右标所指的位置就是“中值”应处的位置,将中值覆盖到此处

分裂完成,此时的数据表左半部分比中值小,右半部分比中值大

 快速排序:图示


 快速排序:代码


package com.zyj.test;

/**
 * 快速排序实现和分析
 * @author 张永俊
 */
import java.util.Arrays;

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

    public static void quick(int[] arr, int left, int right) {

        /*排序之前首先判断高低位指针是否重合,如果重合就代表只有一个数值,不用继续执行*/
        if (left < right) {

            /*记录每次递归的中值(又称中间值)*/
            int baseNumber = arr[left];

            /*定义高位指针和低位指针,分别指向*/
            int low = left;
            int high = right;

            /*
            * 指针的遍历必须从右边开始,如果从左边开始遍历,会因为没有记录值而丢失数据
            * */

            /*循环判断,直达两指针位置重合*/
            while (low < high) {

                /*一旦低位指针找到比中值小的数值,循环停止*/
                while (arr[high] >= baseNumber && low < high) {
                    high--;
                }

                /*将低位指针所指向的数值覆盖到高位指针所指向的位置*/
                arr[low] = arr[high];

                /*一旦高位指针找到比中值大的数值,循环停止*/
                while (arr[low] <= baseNumber && low < high) {
                    low++;
                }

                /*将高位指针所指向的数值覆盖到低位指针所指向的位置*/
                arr[high] = arr[low];
            }

            /*最后将中值保存到两指针重合的位置*/
            arr[low] = baseNumber;

            /*递归排序*/
            quick(arr, left, low - 1);
            quick(arr, low + 1, right);
        }
    }
}

输出结果:

 

排序算法:算法分析


快速排序过程分为两部分:分裂移动

如果分裂总能把数据表分为相等两部分,那么就是O(log n)的复杂度;

而移动需要将每项都与中值进行对比,还是O(n)

综合起来就是O(nlog n);

而且,算法运行过程中不需要而外的存储空间

但是,如果不那么幸运的话,中值所在的分裂点过于偏离中部,造成左右两部分数量不平衡

极端情况,有一部分始终没有数据,这样时间复杂度就退化到O(n^2)

还要加上递归调用的开销(比冒泡排序还要糟糕)

可以适当改进下中值的选取方法,让中值更具代表性

比如“三点取样”,从数据表的头、尾、中间选出中值

但是会产生额外计算开销,且仍然不能排除极端情况

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值