算法与数据结构(1)—— 选择、插入排序 及 优化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Jae_Wang/article/details/80545305

一、前言

算法和语言无关、学习路径从线性(排序),树形结构,图形结构慢慢学~

1. 算法思想:

在强调数据结构的重要性后,其中算法思想也是不容忽略:

  • 分治算法:归并排序、快速排序……
  • 贪心算法:最小生成树……
  • 动态规划:最短路径……
  • 递归搜索:树形结构……

以上所举的例子可以看出数据结构和算法之间的互相依托程度,例如在学习归并、快速排序时,实则也在探究分治算法……

每个细分领域都是算法,例如以下例子:

  • 图形学
  • 机器学习
  • 人工智能
  • 数据挖掘
  • 操作系统
  • 网络安全
  • 高性能计算

二、选择排序(默认都是从小到大排序)

  • 在数组中找最小的数字与数组中第一个位置的交换
  • 此时数组第一个位子是最小的,那么接着从寻找次小的,与第二个位置交换、以此类推
public static void sort(int[] arr){

        int len = arr.length;
        for(int i = 0; i < len; i ++){
            int minIndex = i;
            for(int j = i + 1; j < len; j ++){
                if(arr[minIndex] > arr[j])
                    minIndex = j;
            }
            swap(arr, minIndex, i);
        }
}

三、优化

1. 使用泛型来编写

    对于上述代码,只能适用于int类型,而不是能用于double,自定义类型排序。

public static void sort(Comparable[] arr){
	int len = arr.length;
	for(int i = 0; i < len; i ++){
		int minIndex = i;
		for(int j = i + 1; j < len; j ++){
		if(arr[j].compareTo(arr[minIndex]) < 0)
                    minIndex = j;
		}
		swap(arr, minIndex, i);
	}
}

2. 在每一个轮中,可以同时找出当前未处理元素的最大值和最小值

略~~~  快了不少~ 将近40% ,一趟能找两个了,除了一些细小的开销~

3.编写随机生成算法与测试算法性能

  •     测试的数据不能太少,可以直接写一个满足随机生成算法测试用例的条件。
  •     编写一个赋值函数来测试不同算法之间的性能差异,测试时间运行的长短,通过反射来获取相应的排序算法。

随机生成算法

//生成有n个元素的随机数组,每个元素的随机范围为[rangL, rangR]
    public static Integer[] generateRandomArray(int n, int rangL, int rangR){

        assert rangL <= rangR;

        Integer[] arr = new Integer[n];
        for(int i = 0; i < n; i ++){
            arr[i] = new Integer((int)(Math.random() * (rangR- rangL + 1) + rangL));
        }
        return arr;
    }

测试性能算法

public static void testSort(String sortClassName, Comparable[] arr){
        try {
            //通过sortClassName获得排序函数的class对象
            Class sortClass = Class.forName(sortClassName);

            //通过排序函数Class对象获得排序方法
            Method sortMethod = sortClass.getMethod("sort", new Class[]{Comparable[].class});

            //排序参数只有一个,就是可比较的额数组
            Object[] params = new Object[]{arr};

            long startTime = System.currentTimeMillis();
            sortMethod.invoke(null, params);
            long endTime = System.currentTimeMillis();

            assert isSorted(arr);

            System.out.println(sortClass.getSimpleName() + ":" + (endTime - startTime) + "ms");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

测试:

 public static void main(String[] args) {

        int N = 20000;
        Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
        SortTestHelper.testSort("wang.sort.SelectionSort03.SelectionSort", arr);
        return;
    }


四、插入排序

  • 保持第一个位置不动,后面位置上的元素比前面小,则交换,换一轮,则第一个位置上的元素就是最小的了
  • 继续保持第二个位置上元素不动,接着比较其后面的元素

1. 代码实现注意:

  • 插入排序外层循环从下标1开始,因为是从1开始往前比较,一定要保证下标越界问题
  • 每次都是往前找,所以j--,而不是j++
public static void sort(Comparable[] arr) {

        int n = arr.length;
        for (int i = 1; i < n; i++) {

            //寻找arr[i]的最佳插入位置
            for(int j = i; j > 0 && arr[j].compareTo(arr[j-1]) < 0; j --){
                //要是不熟悉泛型的话,直接写arr[i] < arr[j-1]
                swap(arr, j, j-1);
            }

           /*  寻找元素arr[i]合适的插入位置   写法二
            for( int j = i ; j > 0 ; j-- )
                if( arr[j] < arr[j-1] )
                    swap( arr, j, j-1 );
                else
                    break;
            */
        }
    }

2. 对比与代码优化

2.1选择排序和插入排序的根本区别:

         插入排序的内循环在满足条件的情况下是可以提前结束的!而选择排序必须遍历每一次循环。所以插入排序理论上比选择排序更快一些。(比如一个值已经比插入点要大了,而它之后的点还比这个值大,那么就不用遍历前面而直接结束了,而选择排序里的for循环必须是len长度,要整个遍历一遍才行~)

2.2 代码优化
    (1)减少多次交换swap操作

    在实际测试中,插入排序比选择排序稍微慢,原因是插入在执行内循环每次都执行了swap操作,这样会更耗时,其实涉及了三次赋值,更别说数组中索引值访问等消耗时间。

优化: 避免在内循环中使用swap,只进行一次赋值操作,在内循环结束后再进行一次赋值操作。逻辑并没有变,只是将之前的一次次交换修改成赋值操作、性能得到提高。

public static void sort(Comparable[] arr){
        
        int n = arr.length;
        for(int i = 1; i < n; i ++){
            Comparable e = arr[i];
            int j = i;
            for( ; j > 0 && arr[j-1].compareTo(e) > 0; j --){
                arr[j] = arr[j-1];
            }
            arr[j] = e;
        }
    }

所以一般来说,插入排序的优异性是因为在内循环中可以提前结束,所以在部分有序数组中进行排序效果会更好~

在20000个数据,依次是无序,稍微有序,几乎有序的情况下测试、提升到80000个数据继续测试、


可以发现,无序时,查找排序和优化后的插入排序差不多,而在几乎有序的情况下,插入排序的高效性。

展开阅读全文

没有更多推荐了,返回首页