0
点赞
收藏
分享

微信扫一扫

十大排序算法(二)

目录

6.堆排序

7.快速排序

7.1快排1.0

7.2快排2.0

7.3快排3.0

7.4总结


6.堆排序

学习堆排序就不得不介绍一下二叉树的种类,二叉树分为完全二叉树、均衡二叉树、有序二叉树、满二叉树、完美二叉树。可以认为堆排序是利用完全二叉树的结构设计的排序算法。完全二叉树的序号与数组对应如下

 算法步骤

1. 先形成大根堆,根节点的值大于子节点;
2. 然后将序列中的第一个数与最后一个数交换,最后一个数会是最大值,排序序列长度减1,堆长减1;
3. 重复1,3直到排序序列为1。

大根堆形成思路:插入上升法,遍历每个元素与其父节点比较。时间复杂度O(nlog(n))。
heapify操作,借助原有大根堆的特性,顶端下降法,时间复杂度O(logn)。
算法总体时间复杂度O(nlog(n)),空间复杂度O(1)。不稳定。

```Java
public static int[] sortHeap(int[] a){
        int len=a.length;
        //得到大根堆
        MaxHeap(a,len);
        //交换一次,堆长度减1
        for(int i=len-1;i>0;i--){
            swap(a,0,i);
            len--;
            heapify(a,0,len);
        }
        return a;
    }
 //这里得通过顶端下降法,借助第一次生成大根堆的特性 复杂度log(n)
    private static void heapify(int[] a, int i, int len) {
        int left=2*i+1;
        int right=2*i+2;
        while (left<len){
            int largeIndex;
            //比较子节点的大小,右节点为空的时候,左节点最大
            if(right<len&&a[left]<a[right]){
                largeIndex=right;
            }else{
                largeIndex=left;
            }
            //与根节点比较
            if(a[i]<a[largeIndex]){
                swap(a,i,largeIndex);
                i=largeIndex;
            }else {
                break;
            }
            left=2*i+1;
            right=2*i+2;
        }
    }
    private static void swap(int[] a, int i, int j) {
        int temp=a[i];
        a[i]=a[j];
        a[j]=temp;
    }
    //插入上升法复杂度nlog(n)
    private static void MaxHeap(int[] a, int length) {
        for(int i=0;i<length;i++){
            int index=i;
            while (a[index]>a[(index-1)/2]){
                swap(a,index,(index-1)/2);
                index=(index-1)/2;
            }
        }
    }
```

7.快速排序

快速排序使用分治策略把一个串行分为两个子串行,本质上看来是在冒泡排序的基础上递归分治法,与冒泡相比交换的间隔变大了,速度就变快了。接下来我将介绍快排的三种版本,分别称为1.0版本、2.0版本和3.0版本。其中快排的核心是partition操作
partition:将一个序列划成3部分,小于等于某个数放在左边,大于放在右边,等于放在中间,这是著名的荷兰国旗问题。要求额外空间复杂度为O(1),时间复杂度为O(n)。
算法步骤:

1. 选择序列的最后一个数作为划分值,定义2个数作为小于区的左边界,大于区的右边界;
2. 从第一个开始数遍历,小于等于划分值与小于区下一个数交换,小于区右扩,大于划分值和大于区前一个数交换,大于区右扩。等于划分值,遍历下一个数;
3. 划分值与大于区的第一个数交换。

```Java
 public static int[] partition(int[]a,int L,int R){//L、R为0和length-1
        int less=L-1;//小于区右边界
        int more=R;//大于区左边界
        while(L<more){
            if(a[L]<a[R]){
                swap(a,++less,L++);
            }else if(a[L]>a[R]){
                swap(a,--more,L);
            }
            else if(a[L]==a[R]){
                L++;
            }
        }
        swap(a,more,R);
        //返回交换后划分值的位置以及小于区下一个数据的位置,中间区
        return new int[]{less++,more};
    }
```

7.1快排1.0

算法步骤:

1. 对无序序列进行partition操作;
2. 然后在小于等于区和大于区分别进行partition操作。

```Java
public static int[] sortQuick1(int[] a,int L,int R){
        if(L>=R){
            return a;
        }
        int[] b=partition(a, L, R);
        sortQuick1(a,L,b[1]-1);
        sortQuick1(a,b[1]+1,R);
        return a;

    }
```

7.2快排2.0

与快排1.0相比,算法步骤2变成在小于区和等于区进行partition操作。速度比1.0快。

```Java
 public static int[] sortQuick2(int[] a,int L,int R){
        if(L>=R){
            return a;
        }
        int[] b=partition(a, L, R);
        sortQuick2(a,L,b[0]-1);
        sortQuick2(a,b[1]+1,R);
        return a;
    }
```

7.3快排3.0

快排1.0和快排2.0最坏的时间复杂度达到了(划分值为最大值或最小值)O(n2),快排3.0随机划分值进行partition操作。

```Java
public static int[] sortQuick3(int[] a,int L,int R){
        if(L>=R){
            return a;
        }
        //随机产生划分值与最后一个数交换
        swap(a,L+(int)(Math.random()*(R-L+1)),R);
        int[] b=partition(a, L, R);
        sortQuick3(a,L,b[0]-1);
        sortQuick3(a,b[1]+1,R);
        return a;
    }
```

7.4总结

1. 快排1.0和快排2.0时间复杂度O(n2);
2. 快排3.0时间复杂度O(logn);
3. 空间复杂度都是O(logn);
4. 不稳定
5. 排序要想跑的快就用快排,平均复杂度低;
6. 要想空间少就用堆排;
7. 要想有稳定性就选择归并排序;
8.  基于比较的排序不能做到时间复杂度O(n);

时间复杂度空间复杂度稳定性
选择N21不稳定
冒泡N21稳定
归并NlogNN稳定
插入N21稳定
快排NlogNlogN不稳定
堆排NlogN1不稳定
举报

相关推荐

0 条评论