关于时间复杂度空间复杂度的理解

对于非科班出身的人来说,在学习算法的时候经常会遇到的问题就是关于对事件空间复杂度的 理解。

「大 O 表示法」的准确的数学描述方式非常枯燥,我在这里就不贴出来凑字数了,其实大 O 表示法的意思挺简单的,就是表示:随着输入的值变化,程序运行所需要的时间与输入值的变化关系。

我们先看第一个代码,这是一个函数,输入一个数组,输出这个数组里元数的和。

int count(int a[], int n) {
    int result = 0;
    for (int i = 0; i < n; ++i) {
        result += a[i];
    }
    return result;
}
这里的时间复杂度是O(n),即随着n的增长,O(n)也随之线性增长,我们用O(n)来表示这种线性时间复杂度
接下来我们来看第二行代码
int binary_search(int A[], int key, int imin, int imax)
{
    if (imax < imin) {
        return KEY_NOT_FOUND;
    } else {
        int imid = midpoint(imin, imax);
        if (A[imid] > key)
            return binary_search(A, key, imin, imid - 1);
        else if (A[imid] < key)
            return binary_search(A, key, imid + 1, imax);
        else
            return imid;
    }
}

对于这个程序来说,如果它处理 N 个元素求和所花的时间是 T,那么它处理 N 2 个元素的和所花的时间是多少呢?是 T 2 吗?

如果头脑算不清楚,我们可以拿实际的数字来实验,二分查找每次(几乎)可以去掉一半的候选数字。所以假如 N = 1024,那么它最多要找多少次呢?答案是 10 次,因为 2^10 = 1024,每次去掉一半,10 次之后就只剩下唯一一个元素了。

好,这个时候,如果元素的个数翻一倍,变成 2048 个,那么它最多要找多少次呢?相信大家都能算出来吧?答案是 11 次,因为 2 ^ 11 = 2048。

所以在这个例子中,输入的元素个数虽然翻倍,但是程序运行所花的时间却只增加了 1,我们把这种时间复杂度要叫「对数」时间复杂度,用 O(logN) 来表示。

除了刚刚讲的「线性」时间复杂度和「对数」时间复杂度。我们还有以下这次常见的时间复度数。

「常数」时间复杂度,例如返回一个有序数组中的最小数,这个数因为始终在第一个位置,所以就不会受到数组大小的影响,无论数组多大,我们都可以在一个固定的时间返回结果。

「线性对数」时间复杂度,即 O(N*logN),这个复杂度比较常见,因为常见的高效的排序算法,都是这个时间复杂度,比如快速排序,堆排序,归并排序等。

之前我很多次在网上看到说把1000万次运算当做实际的1s来运算。我的计算机主频是2.5GHZ,所以每秒可以执行25亿次汇编指令,加入每次循环里面的代码可以产生250条汇编指令,才能够得到1s1千万次循环的结果,不是很确定,有机会可以yongxcode试一下

总结一下学习时间复杂度的知识对于我们的工作有什么用:

  1. 对于不同的数据规模,能够决策采用不同的解决方案。

  2. 了解什么情况下用暴力解法就能够解决问题,避免写复杂的代码。

  3. 在写代码之前,就能够预估程序的运行时间,从而可以知道是否能够满足产品需求。

  4. 在程序出现性能瓶颈时,能够有解决方案而不是抓瞎。


参考 https://www.cnblogs.com/mafeng/p/6831731.html

猜你喜欢

转载自www.cnblogs.com/textile/p/10090702.html