算法学习 快速排序

算法概述:

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

是分而治之的典型用例,由于使用了递归,所以对于小规模的数据,速度可能还不如快速排序,所以就需要一个标志位,来判定是否适用快速排序。


具体步骤(例子):

1.选取一个主元,作为分左右两半边的标准,设置左右指针(只是代表位置的移动)

 

2.左边指针i对应的值应小于主元,若该值大于主元,则该指针不动,把指针切换到右边的j指针。右边的指针同理

把左指针i对应的值(8)和主元(6)比较,8>6,i不动

切换到j指针,对应的值(7)大于6,左移,2<6。所以j不动,此时i和j都停住了,交换值。

交换完成后,左指针切换到i+1(i已排好序),右指针切换到j-1。继续该步骤

3.直到下图,j<i,说明两边都已经排好序,只需要把主元的值和i的值交换即可(j在i的左边,说明i这个位置的元素已经大于主元,所以交换后也符合主元左小右大的规律)。至此该主元的位置确认完成,也是最终的位置(这是为什么快拍效率高的原因,一次排序,作为主元的元素的最终位置已经确认(插入排序等,在插入一次后,不一定是元素的最终位置)。

快速排序的重点:

元素相同怎么办;

如何选主元;

在何时用快速排序,选择标志位;

接下来解决这三个问题

1.元素相同怎么办?

(1)不管他,指针继续前进

(2)就算元素相同也要交换

对于以上两种情况,在极端情况下看两种如何表现,再进行选择。

例子:元素全部相同的数组。

(1)假设选取末位作为主元,则每次都不交换,则需要和冒泡排序一样,一个接一个遍历完整个数组,时间复杂度降低为O(N^2)。

(2)假设选区末尾作为主元,每次都交换,则每次指针都会交叉到中间,主元会到中间去。起到了平分的作用,从而时间复杂度位O(NLogn)。缺点就是每次都需要交换。

2.如何选主元?

(1)直接选队首或对尾巴

(2)random函数随机选

(3)选区首末中三个的中间值

直接选首末是不合理的

random函数会有额外开销,不合适。

经过他人实验,也是历代总结,人们发现方法(3)选的主元是最快的。就是数组的第一个,最后一个,中间三个值的比较。

3.在何时用快速排序,选择标志位?

由于快速排序使用了递归,而递归在数据小的时候(例:100)的时候,效率还不如简单排序,所以应该该设置一个标志位,当数据量小于该标志位时,应该采用简单排序而非快速排序。


代码实现

主元的选取

 /*
    主元的选取采用中位数法
    选去左中右中的中位数作为主元
     */
    public static int Median3(int[] arr,int left,int right){
        int center=(left+right)/2;
        if (arr[left]>arr[center]){
            swap(arr[left],arr[center]);
        }
        if (arr[center]>arr[right]){
            swap(arr[center],arr[right]);
        }
        if (arr[left]>arr[center]){
            swap(arr[left],arr[center]);
        }
        //此时这三个已经排好了序
        //将pivot初始化到右边倒数第二位
        //倒数第一位已经比他大了
        swap(arr[center],arr[right-1]);
        //只需要考虑arr[left+1],arr[right-2]
        return arr[right-1];
    }

具体算法

 public static void quickSort3(int []arr,int left,int right,int CutOff){
        int pivot;
        int i;
        int j;//最右边的位置拿来放pivot

        //小于CutOff的值才进行快速排序
        if (CutOff<=right-left){
            pivot=Median3(arr,left,right);
            i=left;
            j=right-1;
           // boolean flag=true;
            while (true){
                while (arr[++i]<pivot){}
                while (arr[--j]>pivot){}
                //退出循环说明左边的大于pivot,右边的小于pivot
                //交换这两个元素位置
                if (i<j){
                    swap(arr[i],arr[j]);
                }else {
                    //说明指针交叉了,退出这一个循环
                    break;
                }
            }
            //把右边的i指针和pivot位置交换,从此pivot到达了最后的位置
            swap(arr[i],arr[right-1]);
            //由此,一个元素的最终位置确定了,开始左右两边的递归
            quickSort3(arr,left,i-1,CutOff);
            quickSort3(arr,i+1,right,CutOff);

        }else {
            insertSort(arr,arr.length);
        }
    }

附上插入排序

 public static void insertSort(int [] arr,int length){
        for (int i = 1; i < length ; i++) {
            int temp = arr[i];
            int j;
            for (j = i-1; j > 0 && temp < arr[j]; j--) {
                arr[j+1] = arr[j];//前值后移
            }
            arr[j+1]=temp;
        }
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值