大话数据结构系列之排序的初体验

排序的定义

假设含有 n 个记录的序列为 {r1, r2, r3 … rn },其相应的关键字分别为{k1, k2, k3 … kn},需确定 1, 2,… 的一种排列 p1, p2, …, pn, 使其相应的关键字满足 kp1 <= kp2 <= … <= kpn(非递减或非递增)关系,即使得序列成为一个按关键字有序的序列{ rp1, rp2, … , rpn },这样的操作叫作排序。

排序的稳定性:

假设 Ki = Kj (1 <= i <= n, 1 <= j <= n, i 不等于 j),且在排序前的序列中 ri (即 i < j)。如果排序后 ri 仍领先于 rj,则称所用的排序方法是稳定的;反之,若可能使得排序后的序列中 rj 领先于 ri,则称所用的排序方法是不稳定的。

稳定性的意义

1、如果只是简单的进行数字的排序,那么稳定性将毫无意义。
2、如果排序的内容仅仅是一个复杂对象的某一个数字属性,那么稳定性依旧将毫无意义。
3、如果要排序的内容是一个复杂对象的多个数字属性,但是其原本的初始顺序毫无意义,那么稳定性依旧将毫无意义。
4、除非要排序的内容是一个复杂对象的多个数字属性,且其原本的初始顺序存在意义,那么我们需要在二次排序的基础上保持原有排序的意义,才需要使用到稳定性的算法,例如要排序的内容是一组原本按照价格高低排序的对象,如今需要按照销量高低排序,使用稳定性算法,可以使得想同销量的对象依旧保持着价格高低的排序展现,只有销量不同的才会重新排序。(当然,如果需求不需要保持初始的排序意义,那么使用稳定性算法依旧将毫无意义)。

内排序

内排序是在排序整个过程中,待排序的所有记录全部被放置在内存中。

外排序

外排序是由于排序的记录个数太多,不能同时放置在内存,整个排序过程需要在内外存之间多次交换数据才能进行。

内排序的性能影响因素:
1、时间性能:时间复杂度
2、辅助空间:辅助空间是除了存放排序所占用的存储空间之外,执行算法所需要的其他存储空间
3、算法的复杂性:指算法本身的复杂程度,所需要的步骤是否过多?随之而来的指令的增多

冒泡排序(Bubble Sort)

一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。

易混淆的基本交换排序:

//冒牌冒泡排序(交换排序)
    public static void bubbleSort0(int[] list){
        for(int i = 0; i < list.length; i++){
            for(int j = i+1; j < list.length; j++){
                if(list[i] > list[j])
                    swap(list,i,j);
            }
        }
    }

正宗冒泡排序:
冒泡名称的由来:较小的数字如同气泡般慢慢浮到上面

//正宗冒泡排序
    public static void bubbleSort1(int[] list){
        for(int i = 0; i < list.length; i++){
            for(int j = list.length - 2; j >= i; j--){
                if(list[j] > list[j+1])
                    swap(list,j,j+1);
            }
        }
    }

交换排序 VS 冒泡排序
后者算法在排序过程中会未后续的排序步骤减少压力(即减少交换次数)

优化后的冒泡排序:
针对已经排序好的序列,停止判断操作

//优化版冒泡排序
    public static void bubbleSort2(int[] list){
        //flag 用来标记
        boolean flag = true;
        //flag 为 true 时退出循环
        for(int i = 0; i < list.length && flag; i++){
            //初始化为false,如果内循环完成,flag的值不变,则说明后面的序列已经完成排序
            flag = false;
            for(int j = list.length - 2; j > i; j--){
                if(list[j] > list[j+1]){
                    swap(list,j,j+1);
                    flag = true;
                }
            }
        }
    }

整体代码:

public static void main(String[] args){
        //基础数据
        int[] sortList = {1,4,3,10,9,6,7,8,5,2,20,18,16,11,13,15,14,17,19,12};
        System.out.println(Arrays.toString(sortList));
        //bubbleSort0(sortList);
        //bubbleSort1(sortList);
        //bubbleSort2(sortList);
        //selectSort(sortList);
        System.out.println(Arrays.toString(sortList));
        int[] sortList2 = {0,1,4,3,10,9,6,7,8,5,2,20,18,16,11,13,15,14,17,19,12};
        System.out.println(Arrays.toString(sortList2));
        //insertSort(sortList2);
        System.out.println(Arrays.toString(sortList2));
    }
    
    //冒牌冒泡排序(交换排序)
    public static void bubbleSort0(int[] list){
        for(int i = 0; i < list.length; i++){
            for(int j = i+1; j < list.length; j++){
                if(list[i] > list[j])
                    swap(list,i,j);
            }
        }
    }
    
    //正宗冒泡排序
    public static void bubbleSort1(int[] list){
        for(int i = 0; i < list.length; i++){
            for(int j = list.length - 2; j >= i; j--){
                if(list[j] > list[j+1])
                    swap(list,j,j+1);
            }
        }
    }
    
    //优化版冒泡排序
    public static void bubbleSort2(int[] list){
        //flag 用来标记
        boolean flag = true;
        //flag 为 true 时退出循环
        for(int i = 0; i < list.length && flag; i++){
            //初始化为false,如果内循环完成,flag的值不变,则说明后面的序列已经完成排序
            flag = false;
            for(int j = list.length - 2; j > i; j--){
                if(list[j] > list[j+1]){
                    swap(list,j,j+1);
                    flag = true;
                }
            }
        }
    }

时间复杂度比较:
最好: O[n ]
最坏: O[n^2)

简单选择排序

思想:
在排序时找到适合的关键字再做交换,并且只移动一次就完成相应关键字的排序定位工作
生活类比:类似于买股票一样,抓住最关键点去买

//选择排序法
    public static void selectSort(int[] list){
        int min;
        for(int i = 0; i < list.length; i++){
            //将当前下标定义为最小值
            min = i;
            for(int j = i+1; j < list.length; j++){
                //把后续的值与当前最小值比较
                if(list[min] > list[j])
                    min = j;
            }
            //若 i 不等于 min,说明找到了最小值,进行交换
            if( i != min)
                swap(list,i,min);
        }
    }

时间复杂度比较:
最好: O[n ]
最坏: O[n2)

直接插入排序

思想:将一个记录插入到已经排好序的有序表中,从而得到一个新的,记录数增 1 的有序表
生活类比:类似于玩扑克牌或者打麻将,选出一张牌儿,直接插入
特点:需要占用一个辅助空间,就是头部的空间,并且不作为最后的排序结果

哨兵的思想 待补充在各种算法的体验,以及哨兵思想的由来

//插入排序法
    public static void insertSort(int[] list){
        int i,j;
        for( i = 2; i < list.length; i++){
            //需要将 i 序号的值插入有序子表
            if(list[i] < list[i-1]){
                list[0] = list[i];
                for( j = i-1; list[j] > list[0]; j--)
                     //大于哨兵的记录后移
                     list[j+1] = list[j];
                //插入到有序位置
                list[j+1] = list[0];
            }
        }
    }

时间复杂度比较:
最好: O[n ]
最坏: O[n2)

发布了239 篇原创文章 · 获赞 78 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/weixin_39966065/article/details/104159518