算法概述和时间复杂度

什么是算法

算法是用于解决特定问题的一系列的执行步骤,使用不同算法,解决同一个问题,效率可能相差非常大

比如:求第 n 个斐波那契数(fibonacci number)

/**
     * 斐波那契数列 Fibonacci sequence
     * 斐波那契数列(Fibonacci sequence),又称黄金分割数列、
     * 因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,
     * 指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……
     * 在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)
     */
    public static int fibonacciSequence_01(int i){
        if(i<=1){
            return i;
        }
        return fibonacciSequence_01(i-1)+fibonacciSequence_01(i-2);
    }

    public static int fibonacciSequence_02(int n){
        if(n<=1){
            return n;
        }
        int first=0;
        int second=1;
        for (int i=0;i<n-1;i++){
            int sum=first+second;
            first=second;
            second=sum;
        }
        return second;
    }

如何评判一个算法的好坏?

如果单从执行效率上进行评估,可能会想到这么一种方案 : 比较不同算法对同一组输入的执行处理时间 .这种方案也叫做:事后统计法

此方案有比较明显的缺点 1.执行时间严重依赖硬件以及运行时各种不确定的环境因素 2.必须编写相应的测算代码 3.测试数据的选择比较难保证公正性

还可以从以下维度来评估算法的优劣

  • 正确性、可读性、健壮性(对不合理输入的反应能力和处理能力)
  • 时间复杂度(time complexity):估算程序指令的执行次数(执行时间)
  • 空间复杂度(space complexity):估算所需占用的存储空间

大O表示法(Big O)

一般用大O表示法来描述复杂度,它表示的是数据规模 n 对应的复杂度,此方法忽略常数、系数、和低阶

◼ 注意:大O表示法仅仅是一种粗略的分析模型,是一种估算,能帮助我们短时间内了解一个算法的执行效率

对数阶的细节

log2n = log29 ∗ log9n 

所以 log2n 、log9n 统称为 logn

常见的复杂度

下图来自维基百科

常数级别O(1)

O(1):算法复杂度和问题规模无关。换句话说,哪怕你拿出几个PB的数据,我也能一步到位找到答案。

理论上哈希表就是O(1)。因为哈希表是通过哈希函数来映射的,所以拿到一个关键字,用哈希函数转换一下,就可以直接从表中取出对应的值。和现存数据有多少毫无关系,故而每次执行该操作只需要恒定的时间

(当然,实际操作中存在冲突和冲突解决的机制,不能保证每次取值的时间是完全一样的)

对数级别O(logN)

 O(logN):算法复杂度和问题规模是对数关系。换句话说,数据量大幅增加时,消耗时间/空间只有少量增加(比如,当数据量从2增加到2^64时,消耗时间/空间只增加64倍,常见于二分法

int number = 1; // 语句执行一次
while (number < n) { // 语句执行logn次
  // 这里的2是log的底数
  // 底数在大O符号中是省去的
  number *= 2; // 语句执行logn次
}

线性级别O(N)

O(n):算法复杂度和问题规模是线性关系。换句话说,随着样本数量的增加,复杂度也随之线性增加

int i =0; // 语句执行一次
while (i < n) { // 语句执行n次
  print(i); //语句执行n次
  i++; // 语句执行n次
}

线性对数级别O(NlogN)

public static void test6(int n) {
        // log5(n)
        // O(logn)
        while ((n = n / 5) > 0) {
            System.out.println("test");
        }
    }

平方级别O(N^2)

 O(n^2)计算的复杂度随着样本个数的平方数增长。这个例子在算法里面,就是那一群比较挫的排序,比如冒泡等等

for (int i = 0; i < n; i++) { // 语句执行n次
  for (int j = 0; j < n; j++) { // 语句执行n^2次
     print('I am here!'); // 语句执行n^2
  }
}

指数级别O(2^N)

如果一个算法的运行时间是指数级的(exponential),一般它很难在实践中使用

斐波那契数的例子的第一个算法就是O(2^N)

 有时候算法之间的差距,往往比硬件方面的差距还要大

算法的优化方向

1.用尽量少的存储空间 

2.用尽量少的执行步骤(执行时间) 

3.根据情况,可以 

  • 空间换时间 
  • 时间换空间
原文地址:https://www.cnblogs.com/lusaisai/p/13663904.html