冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序(Java实现,复杂性分析,稳定性分析)

1 冒泡排序

在这里插入图片描述

1.1 代码

public class Bubble {
    // 对数组a中的元素进行排序
    public static void sort(Comparable[] a) {
        for(int i=a.length-1; i>0; i--) {
            for(int j=0; j<i; j++) {
                if (greater(a[j], a[j+1])) {
                    exch(a, j, j+1);
                }
            }
        }
    }

    // 比较v元素是否大于w元素
    private static boolean greater(Comparable v, Comparable w) {
        return v.compareTo(w) > 0;
    }

    // 数组元素i和j交换位置
    private static void exch(Comparable[] a, int i, int j) {
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}

1.2 复杂性及稳定性分析

  • 时间复杂度:O(n^2);
  • 空间复杂度:O(1);
  • 稳定性:稳定;

2 选择排序

在这里插入图片描述

2.1 代码

public class Selection {
    // 对数组a中的元素进行排序
    public static void sort(Comparable[] a){
        for(int i=0; i<a.length-1; i++) {
        	//假定本次遍历,最小值所在索引是i
            int minIndex = i;
            for(int j=i+1; j<a.length; j++) {
                if(greater(a[minIndex],a[j])) {
                	//更换最小值所在索引
                    minIndex = j;
                }
            }
            //交换索引i和索引minIndex处的值
            exch(a, i, minIndex);
        }
    }

    // 比较v元素是否大于w元素
    private static boolean greater(Comparable v, Comparable w) {
        return v.compareTo(w) > 0;
    }

    // 交换数组a中,索引i和索引j处的值
    private static void exch(Comparable[] a, int i, int j) {
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}

2.2 复杂性及稳定性分析

  • 时间复杂度:O(n^2);
  • 空间复杂度:O(1);
  • 稳定性:不稳定;

3 插入排序

在这里插入图片描述

3.1 代码

public class Insertion {
    // 对数组a中的元素进行排序
    public static void sort(Comparable[] a) {
        for(int i=1; i<a.length; i++) {
            // 当前元素为a[i],依次和i前面的元素比较,找到一个小于a[i]的元素
            for(int j=i; j>0; j--) {
                if (greater(a[j-1], a[j])) {
                    //交换元素
                    exch(a, j, j-1);
                }else {
                    break;
                }
            }
        }
    }
    
    // 比较v元素是否大于w元素
    private static boolean greater(Comparable v, Comparable w) {
        return v.compareTo(w) > 0;
    }

    // 交换数组a中,索引i和索引j处的值
    private static void exch(Comparable[] a, int i, int j) {
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}

3.2 复杂性及稳定性分析

  • 时间复杂度:O(n^2);
  • 空间复杂度:O(1);
  • 稳定性分析:稳定;

4 希尔排序

在这里插入图片描述

4.1 代码

public class Shell {

    public static void sort(Comparable[] a) {
        int N = a.length;
        // 确定增长量h的最大值
        int h = 1;
        while(h < N / 2) {
            h = h * 2 + 1;
        }

        // 当增长量h小于1,排序结束
        while(h >= 1) {
            // 找到待插入的元素
            for(int i=h; i<N; i++) {
                // a[i]就是待插入的元素
                // 把a[i]插入到a[i-h],a[i-2h],a[i-3h]....序列中
                for(int j=i; j>=h; j-=h) {
                    // a[j]就是待插入的元素,依次和a[j-h],a[j-2h],a[j-3h]进行比较,如果a[j]小,那么交换位置
                    if (greater(a[j-h], a[j])) {
                        exch(a, j, j-h);
                    } else {
                        break;
                    }
                }
            }
            h /= 2;
        }
    }
    // 比较v元素是否大于w元素
    private static boolean greater(Comparable v, Comparable w) {
        return v.compareTo(w) > 0;
    }

    // 交换数组a中,索引i和索引j处的值
    private static void exch(Comparable[] a, int i, int j) {
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}

4.2 复杂性及稳定性分析

  • 时间复杂度:最好情况O(n),最坏情况O(n ^ 2),平均O(n ^ 1.3);
  • 空间复杂度:O(1);
  • 稳定性:不稳定;
  • 经过测试,希尔排序性能远高于插入排序;

5 归并排序

在这里插入图片描述

5.1 代码

public class Merge {
    //定义一个辅助数组
    private static Comparable[] assist;

    //对数组a中的元素进行排序
    public static void sort(Comparable[] a) {
        assist = new Comparable[a.length];
        int lo = 0;
        int hi = a.length-1;
        sort(a,lo,hi);
    }

    //对数组a从lo到hi的元素进行排序
    private static void sort(Comparable[] a,int lo, int hi) {
        if (lo>=hi) {
            return;
        }

        int mid = (lo + hi) / 2;
        //对lo到mid之间的元素进行排序
        sort(a, lo, mid);
        //对mid+1到hi之间的元素进行排序
        sort(a, mid+1, hi);
        //对lo到mid这组数组和mid+1到hi这组数据进行归并
        merge(a, lo, mid, hi);
    }

    //对数组中,从lo到mid为一组,从mid+1到hi为一组,对这两组数据进行归并
    private static void merge(Comparable[] a, int lo, int mid, int hi) {
        int i = lo;//定义一个指针,指向assist数组中开始填充数据的索引
        int p1 = lo;//定义一个指针,指向第一组数据的第一个元素
        int p2 = mid+1;//定义一个指针,指向第二组数据的第一个元素

        //lo到mid这组数据和mid+1到hi这组数据归并到辅助数组assist对应的索引处
        //比较左边小组和右边小组中的元素大小,如果右边比左边小,把右边数据填充到assist数组中,否则填充左边
        while (p1<=mid && p2<=hi) {
            if (greater(a[p1], a[p2])) {
                assist[i++] = a[p2++];
            } else {
                assist[i++] = a[p1++];
            }
        }

        //上面的循环结束后,如果退出循环的条件是p1<=mid,则证明左边小组中的数据已经归并完毕;如果退出循环的条件是p2<=hi,则证明右边小组的数据已经填充完毕
        //所以需要把未填充完毕的数据继续填充到assist中,下面两个循环,会执行其中一个
        while (p1<=mid) {
            assist[i++] = a[p1++];
        }
        while (p2<=hi) {
            assist[i++] = a[p2++];
        }
        //到目前为止,assist数组中从lo到hi的元素都是有序的,再把数据拷贝到a数组中对应的索引处
        for (int index=lo;index<=hi;index++) {
            a[index] = assist[index];
        }
    }

    // 比较v元素是否大于w元素
    private static boolean greater(Comparable v, Comparable w) {
        return v.compareTo(w) > 0;
    }
}

5.2 复杂性及稳定性分析

  • 时间复杂度:O(nlogn);
  • 空间复杂度:O(n);
  • 稳定性:稳定;
  • 与希尔排序在处理大批量数据时性能差别不大;

6 快速排序

在这里插入图片描述

6.1 代码

public class Quick {
    public static void sort(Comparable[] a) {
        int lo = 0;
        int hi = a.length - 1;
        quicksort(a, lo, hi);
    }

    private static void quicksort(Comparable[] a, int lo, int hi) {
        if (lo >= hi) return;
        Comparable key = a[lo]; // 把最左边的元素当作基准值
        int left = lo; // 定义一个左指针,初始指向最左边元素
        int right = hi; // 定义一个右指针,初始指向最右边元素
        // 进行切分
        while (left < right){
            // 先从右往左扫描,找到一个比基准值小的元素
            while(left < right && greaterOrequal(a[right],key)){
                right--;
            }

            // 再从左往右扫描,找一个比基准值大的元素
            while (left < right && greaterOrequal(key,a[left])){
                left++;
            }

            //交换left和right索引处的元素
            exch(a, left, right);
        }
        // 交换最后left索引处和基准值所在索引处的值,交换right也可以,因为left和right最终会相等
        exch(a, lo, left);
        // 继续迭代
        quicksort(a, lo, left - 1);
        quicksort(a, left + 1, hi);
    }

    // 比较v元素是否大于等于w元素
    private static boolean greaterOrequal(Comparable v, Comparable w) {
        return v.compareTo(w) >= 0;
    }

    // i索引和j索引处的元素互换
    private static void exch(Comparable[] a, int i, int j) {
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}

6.2 复杂性及稳定性分析

  • 时间复杂度:最好情况O(nlogn),最坏情况O(n^2),平均O(nlogn);
  • 空间复杂度:O(logn);
  • 稳定性:不稳定;

7 测试

public class Test {
    public static void main(String[] args) throws Exception {
        Integer[] arr = {8,6,1,2,7,40,8,56,21,88};
//        Bubble.sort(arr);
//        Selection.sort(arr);
//        Insertion.sort(arr);
//        Shell.sort(arr);
//        Merge.sort(arr);
        Quick.sort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
[1, 2, 6, 7, 8, 8, 21, 40, 56, 88]

8 排序的稳定性

8.1 概念

  • 数组arr有若干元素,其中A等于B,并且A在B前,如果使用某种排序算法,能保证A依然在B前,那么该排序算法就是稳定的。

在这里插入图片描述

8.2 意义

  1. 如果一组数据只需要一次排序,稳定性没有意义;
  2. 一组数据需要多次排序,稳定性有意义;
  3. 例如对一组商品对象排序,第1次排按价格低到高,第二次按销量低到高。如果第二次排序稳定,那么相同销量的商品依然保持价格低到高的顺序展现,只有销量不同的对象才需要重新排序。如此既保持了第一次排序的意义,又减少了系统开销;

8.3 排序算法的稳定性分析

8.3.1 冒泡排序

只有当arr[i]>arr[i+1]的时候,才会交换元素位置,相等的时候不交换。

所以冒泡排序是稳定的。

8.3.2 选择排序

选择排序是给每个位置选择当前元素最小的。例如有数据{5(1),8,5(2),2,9},第一遍选择到的最小元素为2,所以5(1)会和2进行位置交换,此时5(1)到了5(2)的后面,破坏了稳定性。

所以选择排序是不稳定的。

8.3.3 插入排序

插入排序是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果不比它小则直接插入在其后面,否则一直往前找直到找到它插入的位置。

如果遇到一个和插入元素相等的,那么把要插入的元素放在相等元素的后面,相等元素的前后顺序没有改变。

所以插入排序是稳定的。

8.3.4 希尔排序

希尔排序是按照不同步长对元素进行插入排序,虽然一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱。

所以希尔排序是不稳定的。

8.3.5 归并排序

归并排序在归并过程中,只有当前面的元素比后面的元素大时才会交换位置,如果元素相等则先加入前面的元素,不会破坏稳定性。

所以归并排序是稳定的。

8.3.6 快速排序

快速排序需要一个基准值,在基准值右侧找一个比基准值小的元素,在基准值左侧找一个比基准值大的元素,然后交换这两个元素,会破坏稳定性。

所以快速排序是不稳定的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hellosc01

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

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

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

打赏作者

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

抵扣说明:

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

余额充值