算法初体验

咱们在面向对象的演进过程一文中介绍了面向对象发展的几个阶段,其中第一个阶段远古时期的程序由数据结构和算法组成的。其中,数据结构表示数据的组织形式,基本的数据结构包括数组、链表、栈、队列、树、哈希表、图、堆等。而算法表示对数据结构中的数据进行处理的方式或过程,换句话说,就是解决问题的方法。它们俩之间的关系:数据结构为算法服务,不少算法依赖于特定的数据结构,但不是所有算法,算法能够和数据结构没有关系。本期咱们就来聊一聊算法。面试

学习算法的重要性

在介绍具体算法以前,我先谈一下我的对学习算法的初心。个人初心无非有两点:一,BAT等互联网公司招聘面试时要问算法知识,若是想要进入互联网公司,我就必须学好算法;二,经过学习算法提高我的开发的基本功,这样一来,对于不一样场景我就能够正确选择对应的数据结构和算法,使得程序更健壮,提升程序的运行效率。算法

应用领域

目前计算机各个细分领域涉及到不一样的算法。好比说搜索引擎,平时咱们使用google、百度等浏览器,只要咱们输入一个关键字,浏览器就会快速地返回相关的集合,这个集合的背后就隐藏着许多算法。若是没有这些算法,咱们是不可能这么快速地获得想要的结果。再好比说人工智能,经过计算模型算法实现人体识别、语音识别等各应用场景。数组

算法分析

上文咱们已经介绍到算法就是解决问题的方法,而对于同一个问题,可能存在不一样的解决方法。所以,为了衡量一个算法的优劣,提出了时间复杂度空间复杂度这两个概念。浏览器

时间复杂度微信

通常状况下,算法中基本操做重复执行的次数是问题规模n的某个函数f(n),算法的时间度量记为 T(n) = O(f(n)),它表示随问题规模n的增大,算法执行时间的增加率和f(n)的增加率相同,称做算法的渐近时间复杂度,简称时间复杂度。数据结构

空间复杂度数据结构和算法

空间复杂度是对一个算法在运行过程当中临时占用存储空间大小的量度,记作S(n)=O(f(n))。一个算法的优劣主要从算法的执行时间和所须要占用的存储空间两个方面衡量。函数

排序算法

根据时间复杂度咱们大致能够将排序算法分为两类,一类是以选择排序为表明的O(n^2)的算法,另外一类是以快速排序为表明的O(nlogn)的算法。看到这里咱们不由会问:既然有O(nlogn)的排序算法,那些O(n^2)的算法还有存在的必要吗?要回答这个问题,先来看下O(n^2)的排序算法的特色:首先,它相对是比较基础的,编码简单,易于实现,在一些特定场景下O(n^2)更适合 ,譬如在机器语言中O(n^2)更容易实现;其次,简单的排序算法思路衍生出复杂的排序算法,好比说希尔排序是对插入排序的优化;最后,对于一些简单的算法,因为它们自己的一些性质,能够被用做改进更复杂排序算法的子过程当中。基于此,本文O(n^2)排序算法中两个表明性的算法即选择算法和插入算法。
image.png学习

选择排序

思想:在整个待排序数组里找到最小的值,而后和待排序中的第一个元素进行交换,接着在剩下的元素里找到最小的元素,接着将它和待排序中的第一个元素进行交换,以此类推。为了加深你们的理解,举个具体例子,对八、六、二、三、一、五、七、4进行升序排序。
image.png优化

选择排序的Java语言实现以下:

/**
     * 思路:每次从待选数组中选择一个最小元素,而后和对应位置交换位置
     * @param arr
     * @param n
     */
    public void sort(int[] arr, int n) {
        for(int i=0;i<n;i++) {
            // 1. 寻找[i,n)区间里的最小元素
            int minIndex = i;
            for(int j=i+1;j<n;j++ ) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }
            // 2. 交换位置
            this.swap(arr,i,minIndex);

        }
    }

插入排序

思路:插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。固然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,若是比它大则直接插入在其后面,不然一直往前找直到找到它该插入的位置。若是遇见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。因此,相等元素的先后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,因此插入排序是稳定的。

image.png
插入排序的Java语言实现以下:

public void sort(Comparable[] arr){
        int n = arr.length;
        for (int i = 0; i < n; i++) {
            // 寻找元素arr[i]合适的插入位置
            for( int j = i; j > 0 && arr[j].compareTo(arr[j-1]) < 0 ; j--)
                swap(arr, j, j-1);
        }
    }

经过比较选择排序和插入排序的代码实现,咱们能够发现一旦有部分排序好以后,新插入一个数若是比排好序最大值还要大,则不用再和其余数字比较,减小了比较次数。可是,咱们应该注意到插入排序在每次遍历的时候都须要进行交换操做,这个交换操做包含三次赋值操做,致使插入排序的时间要比选择排序的时间更长。针对这个问题,咱们的先辈们想到了一个方法:先将待比较元素复制一份,而后依次和有序数组中的元素进行比较,若是比有序数组中的元素小,则将有序数组中的元素覆盖待比较元素,以此类推。以下图所示,首先咱们将元素6复制一份,接着验证元素6是否应当放在当前位置,经过比较6和它以前的元素大小,发现元素8应该放在元素6的位置上,所以将元素8覆盖元素6,而后咱们考查元素6是否应该放在前一个元素位置上,此时,因为元素8在第0个位置上咱们就不用比较直接覆盖。它的Java代码实现以下:

image.png

for (int i = 0; i < n; i++) {
            // 寻找元素arr[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;
        }

这样一来,内循环只须要进行一次赋值操做,效率获得了大大优化,不只超过了选择排序,并且在待排序数组是有序的状况下,时间复杂度能够达到O(n)


image

欢迎关注微信公众号:木可大大,全部文章都将同步在公众号上。

相关文章
相关标签/搜索