[时间复杂度]-排序算法的时间复杂度

我们经常可以看到这样的描述:软件=数据结构+算法,可见算法基础对于一个程序员的重要性。算法中,有两个基本概念:时间复杂度和空间复杂度。

  • 时间复杂度:描述算法执行消耗的时间,时间越短,时间复杂度越低,算法越优秀;
  • 空间复杂度:描述算法执行所占用的内存空间,占用内存越少,空间复杂度越低,算法越优秀;

因此,时间复杂度和空间复杂度是评价一个算法性能的主要指标。那么如何计算一个算法的时间复杂度和空间复杂度呢?

简单理解,计算时间复杂度就是评估一个算法完成后执行了多少行代码,也就是算法所消耗的时间,但是该如何用数学的方式其表示出来呢?

假设现在有个一个算法需要执行n次运算,我们用T(n)表示,n即表示算法的规模(比如n=10时,循环输出10次Hello World;n=100时,循环输出100次Hello World)。如果假设存在一个函数f(n),表示随着n的变化,算法需要执行的次数,当T(n)=O(f(n)),那么O(f(n))即为算法的时间复杂度。

比如,一个普通的循环:

public int calc(int n) {
    int total = 0;
    for (int i = 0; i < n; i++) {
        total += i;
    }
    return total;
}

当n越大,循环的次数越多,那么耗费的时间也就越大,也就是f(n)随着n线性增长。那么该算法的执行时间T(n)=O(n),即时间复杂度为O(n)。

再比如,一个双层循环:

public int calc(int n) {
    int total = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            total += j;
        }
    }
    return total;
}

外层循环每循环一次,内层循环就循环了n次,那么代码总循环次数就是n*n。那么该算法的执行时间T(n)=O(n^2) ,即时间复杂度为O(n^2)。

一般来说,时间复杂度是用来评估随着n的增大,算法执行需要消耗时间的增速,而不是精确计算消耗的时间,事实上也无法精确计算。既然是评估,那么f(n)函数我们只需要保留对消耗时间影响最大的因子即可。

例如,有一个f(n)=2*n3函数,系数2对f(n)函数的增速影响就不大,所以该算法的时间复杂度T(n)=O(n3),即忽略常量系数对增速的影响。

再比如,在一个没有循环的代码块里,只有三行输出代码:

public void print() {
    System.out.println("Hello 河先森");
    System.out.println("Hello 河先森");
    System.out.println("Hello 河先森");
}

即T(n)=3,是一个常数,而常数对增速的影响是可以忽略的,那么该代码块的时间复杂度就是O(1)。

再来看一个例子,计算下面代码块的时间复杂度:

public void print(int n) {
    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            System.out.println("Hello 河先森");
        }
    }
}

当i=0时,内层循环了n次,当i=1时,内层循环了n-1次,以此内推,当i=n-1时,内层循环了1次。那么总的循环次数T(n)=n+(n-1)+(n-1)+...+1=n(n+1)/2=nn/2+n/2,忽略常数项和对增速影响不大的因子,只保留影响最大的因子,那么该算法的时间复杂度即为O(n2)。

常见的时间复杂度量级:

  • 常数阶O(1)
  • 对数阶O(logN)
  • 线性阶O(n)
  • 线性对数阶O(nlogN)
  • 平方阶O(n²)
  • 立方阶O(n³)
  • K次方阶O(n^k)
  • 指数阶(2^n)

以上算法时间复杂度中,随着n的增大,f(n)增速越快,说明算法的效率越低。即算法耗费的时间O(1) < O(logN) < O(n) < O(nlogN) < O(n²) < O(n³) < O(n^k) < O(2^n)。

如果想在一个数组中找到一个数,最简单的方法就是循环遍历:

public boolean search(int m) {
    int[] arr = new int[]{3, 5, 7, 1, 2, 6, 4, 9};
    for (int i = 0; i < arr.length; i++) {
        if (m == arr[i]) {
            return true;
        }
    }
    return false;
}

如果要查找的是数是数组的第一个数(本例子中的3),那么只需要一次循环就能找到,时间复杂度为O(1)。但是如果要查找的数在数组最后位置(本例中的9),那么必须要经过arr.length次循环,时间复杂度为O(n),n为数组的长度。那么这段代码代码块的时间复杂度到底是多少呢?

一般在没有特殊说明的情况下,时间复杂度都是指最坏的时间复杂度,因为没有比这更坏的情况了。所以这段代码块的时间复杂度为O(n)。

时间复杂度描述的是算法消耗时间趋势,同理,空间复杂度则是描述算法在运行时临时内存消耗的趋势,一般用 S(n) 来定义,记作S(n)=O(fn)。

至于评价算法的具体好坏,就要具体情况具体分析了。有时我们会拿时间换空间,有时会去拿空间换时间,有时则需要同时考虑时间和空间复杂度。

总之,具体问题具体分析,但是我们一定要理解时间复杂度和空间复杂度的分析过程。

重要的排序算法的时间复杂度:

重点记忆: 插入排序-堆排序-快速排序-归并排序 的 平均时间复杂度

 

 

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值