时间复杂度及空间复杂度

一、时间复杂度

1.简介

算法的时间复杂度计算的不是算法运行了多少时间,而是算法运行的大概的次数,因为运行时间时间是计算不出来的,因为不同硬件配置的计算机运行速度是不同的。

2.经典案例

2.1冒泡排序

public static void main(String[] args) {
        int[] array = new int[]{10, 5, 6, 1, 2 ,3, 8, 7, 9, 4};
        //int[] array = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        System.out.println(Arrays.toString(array));
        for (int i=0; i<array.length-1; i++){
            int exchange = 0;
            for (int j=0; j<array.length-1-i; j++){
                if (array[j] > array[j+1]){
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                    exchange = 1;
                }
            }
            if (exchange == 0){
                break;
            }
        }
        System.out.println(Arrays.toString(array));
    }

冒泡排序的算法时间复杂度为
O ( N 2 ) O(N^2) O(N2),我们先不看代码,先从冒泡排序的思想来想一下,以从大到小排序为例,每次比较都是相邻的进行比较,如果第一个大于第二个,则交换位置,然后第二个和第三个再进行比较,第一轮比较下来,比较了 n-1 次,由于第一轮比较的时候,已经将最大的数冒到了最后一位,所以第二轮比较的时候,比较的元素个数为 n-2 个(因为最大的数即最后一个元素不用比较了),同理,第三轮为 n-3, 第四轮为 n-4,一直到1。所以可以看出冒泡排序的时间复杂度为一个等差数列,即 1+2+3+4+…+(n-1),由等差数列得出
n + ( n ( n − 1 ) ) / 2 = n 2 n+(n(n-1))/2=n^2 n+(n(n1))/2=n2。因为时间复杂度是一个估算值,当n值很大时,各种常数项,系数,低阶项与高阶项相比,均可忽略不计,因此取值 n 2 n^2 n2,当然这是最坏的情况,因为时间复杂度就是要取最后的情况,最好的情况就是这个数组原本就有序,那么他的时间复杂度为 O ( N ) O(N) O(N)

2.2二分查找

public static void main(String[] args) {
        int[] array = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int findTemp = 11;
        int start = 0;
        int end = array.length - 1;
        while (start <= end){
            int middle = (start + end) >> 1;
            if (findTemp == array[middle]){
                System.out.println(middle);
                return;
            }else if (findTemp > array[middle]){
                start = middle + 1;
            }else {
                end = middle - 1;
            }
        }
        System.out.println(findTemp + " not find.");
    }

二分查找的前提条件是要查找的数列是有序的,冒泡排序可以参考上面的示例。至于二分查找的时间复杂度,肯定不是 O ( N ) O(N) O(N),因为如果是 O ( N ) O(N) O(N),为什么不直接写一个循环查找呢,所以说二分查找的时间复杂度肯定小于 O ( N ) O(N) O(N),即 O ( l o g 2 N ) O(log_2N) O(log2N),以2为底N的对数,也就是 2 x = N 2^x=N 2x=N, x就是要查询的次数,二分查找的核心就是每次查找的范围缩小一半,举个例子,把中国人口的身份证号排个序,然后从中间找你的身份证号,如果运气好,第一次就找到了,运气不好的话,是不是要找13亿次,而如果用二分查找,只需要找31次就可以了,因为2的30次方=10亿左右,那么31次方就是20亿,远大于13亿,因此13亿次和31次进行比较,差距显而易见。但实际上这个算法不怎么样,因为使用该算法的前提是中国人的身份证号是有序的。

3.使用斐波那契计算阶乘

public static void main(String[] args) {
        System.out.println(factorial(10));
    }

    private static int factorial(int n) {
        return n < 2 ? n : factorial(n - 1) * n;
    }

上述阶乘的时间复杂度为 O ( N ) O(N) O(N),因为如果计算 N,那么就要计算 1 到 N-1,如果计算 N-1,就要计算 1 到 N-2,那么递归算法的时间复杂度如何计算呢,递归算法时间复杂度=递归次数*每次递归函数的次数,以上述阶乘为例,递归次数为 N,每次递归函数的次数为 1,所以时间复杂度为 O(N), 如果内层是一个 N 次的循环,那么时间复杂度就为 O ( N 2 ) O(N^2) O(N2)

4.求第N项斐波那契数列的值

public static void main(String[] args) {
        System.out.println(fib(6));
    }

    private static int fib(int n) {
        return n < 2 ? n : fib(n - 1) + fib(n - 2);
    }

斐波那契的时间复杂度为 2 0 + 2 1 + 2 2 + 2 3 + . . . + 2 ( n − 1 ) 2^0+2^1+2^2+2^3+...+2^(n-1) 20+21+22+23+...+2(n1)在这里插入图片描述
因此第一层是 2 0 2^0 20,所以最后一层是 2 ( n − 1 ) 2^(n-1) 2(n1),由等比数列公式可得,斐波那契时间复杂度为 2 N − 1 = O ( 2 N ) 2^N-1=O(2^N) 2N1=O(2N),当n=50时,几乎已经运算不出来了,所以要进行优化,上述是计算某一位的值,可以优化为

private static long fib_one(int i) {
        long a = 0;
        long b = 1;
        long n = 0;
        if (i == 0){
            return a;
        }
        for (int j=2; j<=i; j++){
            n = a + b;
            a = b;
            b = n;
        }
        return n;
    }

可以看出,这个时间复杂度仅仅为 O(N),因为每一个后一项的值等于前两项的和,因为我们只需进行位置互换及迭代即可,同理我们可以求出一定范围内所有的值

public static void main(String[] args) {
        System.out.println(fib_all(50));
    }

    private static String fib_all(int i) {
        long[] fibArray = new long[i + 1];
        fibArray[0] = 0;
        fibArray[1] = 1;
        if (i == 0){
            return Arrays.toString(fibArray);
        }
        for (int j=2; j<=i; j++){
            fibArray[j] = fibArray[j - 1] + fibArray[j - 2];
        }
        return Arrays.toString(fibArray);
    }

这个算法的时间复杂度也为O(N),所以可以很快的计算出前50,乃至更大位数的斐波那契数列,从 O ( 2 N ) O(2^N) O(2N),到 O ( N ) O(N) O(N),差距显而易见。

二、空间复杂度

空间复杂度:不计算空间,计算的是定义变量的大概个数。
比如说定义了一个int型的变量,占据4个字节,相对于现在几个G的内存,完全可以忽略掉,所以我们应该更加关注时间复杂度,而不是空间复杂度,如果可以用空间换时间,就去换,比如上述的斐波那契数列,我们就是通过空间去换时间,多定义了几个变量,而程序的运行速度得到了质的飞跃。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值