排序1-算法性能分析指标,冒泡,选择,插入排序算法(包括希尔排序)

排序算法在计算机软件方向属于必修的基础点。这篇开始我会尽我所能写下关于排序的总结。

如何分析排序算法的性能?

排序算法的执行效率

1.时间复杂度分析,包括最好 最坏 平均时间复杂度分析。关于这一点详细内容我在之前的一篇博客中做过描述白话代码中的复杂度分析
这里要说明一点的是以往我们分析时间复杂度 都是从宏观上进行分析,很少考虑到时间复杂度的系数、常数、低阶等因素。但是在实际开发中,我们排序的数据规模有时候很小时,也许时间复杂度为O( n2)的排序算法优于其他算法 而更被适用。
2.程序执行的比较和移动次数
基于比较的排序算法的执行过程,会涉及两种操作,一种是元素比较大小,另一种是元素交换或移动。所以,如果我们在分析排序算法的的执行效率的时候,应该把比较次数和交换(或移动)次数也考虑进去。

排序算法的内存消耗

内存消耗可以通过空间复杂度来进行很衡量。关于排序算法的内存消耗,我们引入一个概念叫原地排序,他特指空间复杂度为O(1)的排序算法。

排序算法的稳定性

稳定性:指的是元数据中两个数值相等的元素在经过有效排序后元素之间的相对位置没有发生改变。例如:6,8,2,5,8,10在经过升序排列之后2,5,6,8,8,10 若排序算法使得这两个8的相对位置没有发生改变,则说明它是稳定性算法。反之,则为不稳定算法。

时间复杂度为O(n2)的排序算法

冒泡排序

冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。
冒泡排序比较基础,在日常中我们应用的较多。实现起来也比较简单

public static int[] maopaoSort(int[] a) {
        int temp;
        for (int i = 0; i < a.length; i++) {
            for (int j = i + 1; j < a.length; j++) {
                if (a[i] > a[j]) {
                    temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                }
            }
        }
        return a;
    }

冒泡排序的总结:

 * 冒泡排序算法(数组中的元素两两进行比较 排序)
 * 冒泡排序是一种原地排序算法(冒泡的过程只涉及相邻数据的交换操作,只需要常量级的临时空间)
 * 冒泡排序是一种稳定算法(当有相邻的两个元素大小相等的时候,我们不做交换,相同大小的数据在排序前后不会改变顺序)
 * 冒泡排序的时间复杂度分析:
	* 最好情况是排序的数据已经有序 只需要及逆行一次遍历 无任何交换操作 时间复杂度为O(n)
 	* 最坏情况是数据刚好倒序,n次遍历 n次冒泡操作,时间复杂度为O(n^2)
 	* 平均时间复杂度是O(n^2)

插入排序

插入排序是一个动态排序的过程,即动态的有序集合(例如:数组)中添加数据,通过这种方法一直保持集合中的数据有序性。
具体而言:首先,我们将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。
直接插入排序的算法实现(核心就是 未排序区间的元素,在已排序区间中找到合适位置进行插入,知道保证所有数据有序):

public static int[] insertSort(int[] a) {
        int temp;       //temp起到哨兵作用
        int j;
        for (int i = 1; i < a.length; i++) {
            if (a[i] < a[i - 1]) {    //a[i]小于前驱元素 则将a[i]插入前面有序的表中
                temp = a[i];
                for (j = i - 1; temp < a[j]; j--) {
                    a[j + 1] = a[j];    //向后移动
                }
                a[j + 1] = temp;
            }
        }
        return a;
    }

在直接插入排序的基础上进行优化,有折半插入排序
折半插入排序(折半查找找出元素的待插入位置,然后在统一的移动待插入位置之后的所有元素)
折半插入排序 只是减少了比较元素的次数,约为O(nlogn),元素移动的次数没有改变,他依赖于待排序表的初始状态
折半插入排序的时间复杂度是O(n2)

public static int[] halfInsertSort(int[] a) {
        int i, j, low, mid, high, temp;
        for (i = 1; i < a.length; i++) {
            temp = a[i];
            low = 0;
            high = i - 1;
            while (low <= high) {           //折半查找(默认递增有序)
                mid = (low + high) / 2;       //取中间点
                if (a[mid] > temp) high = mid - 1;
                else low = mid + 1;
            }
            for (j = i - 1; j >= high + 1; j--) {
                a[j + 1] = a[j];        //统一向后移动元素,空出插入位置
            }
            a[high + 1] = temp;
        }
        return a;
    }

理解直接插入排序和折半插入排序之后,理解希尔排序就会变得简单了。
希尔排序(基本思想:先将待排数据分割成若干个特殊子表 ,分别进行直接插入排序。当整个数据基本有序时,再对全体及逆行一次直接插入排序)
排序过程:这里我们取d1=n/2;di+1=[di/2] 知道最后一个增量为1
当n在某个特定范围内,希尔排序的时间复杂度为O(n1.3),最坏情况下 时间复杂度时O(n2)

public static int[] ShellSort(int[] a) {
        int n = a.length;
//        System.out.println("n= "+n);
        int dk, i, j, temp;
        for (dk = n / 2; dk >= 1; dk = dk / 2) {      //步长变化
            for (i = dk + 1; i < n; i++) {
                if (a[i] < a[i - dk]) {       //须将a[i]插入有序数组中
                    temp = a[i];
                    for (j = i - dk; j > 0 && temp < a[j]; j -= dk) {
                        a[j + dk] = a[j];       //记录后移,查找插入的位置
                    }
                    a[j + dk] = temp;
                }
            }
        }
        return a;
    }

插入排序的总结:

 * 插入排序是一种原地排序算法(空间复杂度时O(1))
 * 冒泡排序是一种稳定算法(对于值相同的元素,我们可以选择将后面出现的元素,插入到前面出后面出现的元素的后面,这样就可以保持原有的前后顺序不变)
 * 冒泡排序的时间复杂度分析:    	
 	* 平均时间复杂度是O(n^2)

选择排序

选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。
实现代码如下:

public static int[] choiceSort(int[] a) {
        int i, j, t=0,min;
        for( i=0;i<a.length;i++){
            min=a[i];
            for(j=i+1;j<a.length;j++){
                if(min>a[j]){
                    min=a[j];
                    t=j;
                }
            }
            if(min!=a[i]){
                a[t]=a[i];
                a[i]=min;
            }
        }
        return a;
    }

选择排序的总结:

 * 选择排序是一种原地排序算法(空间复杂度是O(1))
 * 选择排序是一种不稳定算法(选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这破坏了算法稳定性)
 * 选择排序的时间复杂度分析:    	
 	* 平均时间复杂度是O(n^2)

猜你喜欢

转载自blog.csdn.net/weixin_38073885/article/details/84403888