快速排序算法的分析

1.快速排序算法的描述

1.1快速排序的方法是每一次把数组分成两个部分,其中大于K(从数组里面选出来的标准元素,一般是第一个)的在前面,小于K的在后面。然后对两个数组做同样的事情,直到这两个数组只有一个元素那么排序完成

因此快速排序可以分成两个部分,一个是分治,就是不断递归,减小问题规模;另一个就是数据的移动(挖坑填补)

代码描述,在这里做分治的主要操作

 /**
     * @description 快速排序,分治部分
     *
     */
    public static void quickSort(int[] list,int lo,int hi){

        if(lo>=hi)return;
        int index=pair(list,lo,hi);
        quickSort(list,lo,index-1);
        quickSort(list,index+1,hi);

    }

1.2如何进行切分(数据移动)

第一次取下标为0的元素作为k,两个指针,i指向第一个元素,j指向最后一个元素

假设k被从数组中挖了出来,然后需要一个数填补空位,那么j从后往前移动,找到一个比k小的,然后和k交换位置;接下来就i向前移动,找到比k大的,然后再和k交换位置,这样就完成了一个循环

如此往复,知道i和j相遇活着一方走到尽头,那么切分就结束了

2.代码呈现

需要说明的地方:

  • 使用了一个随机数组生成器,然后使用了断言来判断是否排序成功。这一步值得注意的地方有两个,一个是数组的赋值用等于是引用传递,这里需要调用对象的clone()方法来实现值传递,另一个是为了开启断言编译的时候需要加上-ea这个选项,在idea里面的操作步骤为,Run->Edit Configurations->VM options  加上 -ea,数组的比较需要使用Arrays.equals()
  • 整个快速排序分成两个方面,一个是递归分成小问题,一个是分割,用来调整数据位置
  • 在分割的时候,直接遍历查找i,j然后直接把i,j进行交换。最后跳出循环的时候,交换j和第一个位置,因为j所指向的都是小于或等于第一个的,j要从hi+1开始因为--操作会先执行,而i可以从lo开始因为第一个没有必要检测。
import java.util.Arrays;

/**
 * Created by kisstheraik on 16/8/13.
 * Description 快速排序
 */
public class QuickSort{

    public static void main(String[] args){

        int[] l=getList(6);
        int[] t=l.clone();

        System.out.println("原数组:");
        for (int tmp:l)
            System.out.print(tmp + " ");

        Arrays.sort(t);
        quickSort(l, 0, l.length-1);

        //用来判断排序有没有成功
        assert Arrays.equals(l,t):"排序没有成功";

        System.out.println("\n排序后:");
        for (int tmp:l)
            System.out.print(tmp + " ");


    }
    /**
     * @description 快速排序,分治部分
     *
     */
    public static void quickSort(int[] list,int lo,int hi){

        if(lo>=hi)return;
        int index=pair(list,lo,hi);
        quickSort(list,lo,index-1);
        quickSort(list,index+1,hi);

    }

    /**
     *
     * @param list  int[] 要排序的数组
     * @param lo    最低位置
     * @param high  最高位置
     * @return      标准元素的下标
     * @description 快速排序,分割部分
     */
    public static int pair(int [] list,int lo,int high){

        int first=list[lo];

        int i,j;

        i=lo;
        j=high+1;

        while (true){

            while (list[--j]>first)if(j==lo)break;
            while(list[++i]<first)if(i==high)break;

            if(i>=j)break;
            int tmp=list[i];
            list[i]=list[j];
            list[j]=tmp;

        }
        list[lo]=list[j];
        list[j]=first;
        return j;
    }

    /**
     *
     * @param length int 数组的长度
     * @return int[] 随机数组
     */
    public static int[] getList(int length){

        int[] list=new int[length];

        for(int i=0;i<length;i++)
            list[i]=(int)(Math.random()*100);
        return list;
    }
}
<?php
/**
 * Created by PhpStorm.
 * User: kisstheraik
 * Date: 16/8/13
 * Time: 下午11:10
 */

//分治
function quickSork(&$array,$lo,$hi){

    if($lo>=$hi)return;

    $index=par($array,$lo,$hi);

    quickSork($array,$lo,$index-1);
    quickSork($array,$index+1,$hi);
}

//分割
function par(&$array,$lo,$hi){

    $first=$array[$lo];
    $i=$lo;
    $j=$hi+1;

    while(true){
        while($array[--$j]>$first)if($j==$lo)break;
        while($array[++$i]<$first)if($i==$hi)break;
        if($i>=$j)break;

        $tmp=$array[$j];
        $array[$j]=$array[$i];
        $array[$i]=$tmp;
    }
    $array[$lo]=$array[$j];
    $array[$j]=$first;

    return $j;

}

$array=[23,43,13,6,1,7,89];
quickSork($array,0,6);

foreach($array as $k=>$v)
    echo $v." ";

3.优点和缺点的分析

由于内循环的比较次数较少,因此效率高,最高的时候是每次都能把数组分成均等的两个,但是最坏的情况就是k为最小的或者最大的元素,这时候性能达到n^2

4.复杂度的计算

主定理: T [n] = aT[n/b] + f (n)

其中 a >= 1 and b > 1 是常量 并且 f (n) 是一个渐近正函数, 为了使用这个主定理,您需要考虑下列三种情况:

上面的定理用于分析递归问题的复杂度 a为子问题的个数,n/b为子问题的规模,快速排序里面最好的时候f(n)=O(n),满足第二个,T[n]=O(nlogn),因此最好的情况是nlogn.

最坏的情况是O(n^2),具体的证明,参考《算法导论》

5.如何进行优化

5.1小的数组的时候切换到插入排序

插入排序:

插入排序的核心思想是。把一个数插入一个已经排好的序列里面,一直和前一个元素比较,不符合大小就交换,直到插入了相应的位置

import java.util.Arrays;

/**
 * Created by kisstheraik on 16/8/13.
 */
public class InsertSort {

    public static void main(String[] args){

        int[] l=getList(10);
        int[] t=l.clone();

        System.out.println("原数组:");
        for (int tmp:l)
            System.out.print(tmp + " ");

        Arrays.sort(t);
        insertSort(l);
        //用来判断排序有没有成功
        assert Arrays.equals(l,t):"排序没有成功";

        System.out.println("\n排序后:");
        for (int tmp:l)
            System.out.print(tmp + " ");

    }
    public static void insertSort(int []list){
        int length=list.length;
        //对数组进行插入排序


        for(int i=1;i<length;i++){
            //把list[i]插入到0-i这个数组里面
            int j=i;
            while(j>0&&list[j]<list[--j]){

                    //交换位置 j 和j-1
                    int t=list[j];
                    list[j]=list[j+1];
                    list[j+1]=t;

            }

        }
    }
    /**
     *
     * @param length int 数组的长度
     * @return int[] 随机数组
     */
    public static int[] getList(int length){

        int[] list=new int[length];

        for(int i=0;i<length;i++)
            list[i]=(int)(Math.random()*100);
        return list;
    }
}

同时使用快速排序和插入排序:这里一定注意需要return不能丢

/**
     * @description 快速排序,分治部分
     *
     */
    public static void quickSort(int[] list,int lo,int hi){

        if((lo+5)>=hi){
            InsertSort.insertSort(list,lo,hi);
            return;
        }
        //if(lo>=hi)return;

        int index=pair(list,lo,hi);
        quickSort(list,lo,index-1);
        quickSort(list,index+1,hi);

    }

两个版本(有插入排序和没有插入排序)的结果分析

1亿数据排序的结果

没有插入排序20072ms16711ms17047ms15084ms
有插入排序,最小为515706ms15612ms17790ms14362ms

 

可以看出增加的插入排序还是加快了排序速度

5.2使用经过筛选的k,k可以取为3,影响效率的主要因素就是根据k进行数组分组的过程

//比较复杂留到以后分析

转载于:https://my.oschina.net/lovezfy/blog/732291

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值