给代码“染个色”:算法的时间密码

1,186 阅读3分钟

一看标题,算法还有啥时间密码? 那么有请主角登场 -- 时间复杂度。

前言

有人问了:什么是时间复杂度?知道它又有啥用?它还能影响我的算法不成?不知道大家在刷力扣,洛谷,牛客或者cf等算法平台时,有些题你的方法确确实实可以通过这道题,但是你的暴力美学最后成为冰冷的3个字符 -- TLE(Time Limit Exceeded),也就是我们常说的超时错误时间复杂度就是影响TLE的罪魁祸首。

一、先搞懂:时间复杂度为啥这么重要?

计算机每秒能执行的指令数是有限的(普通CPU约10^8次/秒)。当问题规模扩大(比如从10个数据变成10万个),若算法时间复杂度太高,需要执行的指令数会暴增,超过系统规定的时间上限,就会触发TLE。简单说,时间复杂度是“因”,TLE是“果” ,选对复杂度,才能避开超时陷阱。

二、两种方法做“两数之和”

大家来看看力扣第一题:

两数之和 - 力扣(LeetCode)

最容易想到的方法不就是暴力枚举嘛,2个for循环,加起来为结果不就直接就结束了吗?没错,我也是这样做的!

var twoSum = function (arr, res) {
    for (var i = 0; i < arr.length; i++) {
        var left = arr[i];
        for (var j = i + 1; j < arr.length; j++) {
            var right = arr[j];
            if (left + right == res) {
                return [i, j];
            }
        }
    }
};

image.png

但是你看看执行时间为 47ms,只击败了不到四分之一的coder。为啥会这样呢?

你有没有发现,这道题你用了2个循环,那么时间复杂度就是O(n^2),显然不是最优解,那我们马上想到了另外一种解法,我只遍历一遍数组命名为i,然后去找数组其他的元素有没有为res - i的值,那时间复杂度不就是O(n)了吗?

说干就干:

var twoSum = function (arr, res) {
    var diffs = {};
    var len = arr.length;
    for (var i = 0; i < len; i++) {
        if (diffs[res - arr[i]] !== undefined) {
            return [diffs[res - arr[i]], i];
        }
        diffs[arr[i]] = i;
    }
};

image.png

执行时间立刻变成了1ms,击败了超95%的coder!

所以你会发现,时间复杂度真的还蛮有趣的,不然被TLE那真是难受至极!

三、几种常见的时间复杂度

  1. 绿灯(O(1)):常数时间,永避TLE (最理想的状态)

  2. 黄灯(O(log n)):对数增长,TLE绝缘体

  3. 红灯(O(n)):线性增长,警惕TLE

  4. 警示橙(O(n²)):指数增长,TLE重灾区

总结:算法选得好,超时跑不了

说白了,时间复杂度就像你去食堂吃饭的“取餐方式”:

O(1)是VIP专属,不管多少人,你直接到窗口拿预定好的餐,秒走;

O(log n)是拿号后看电子屏叫号,人越多,按号段分流的优势越明显,不用傻等;

O(n)是排长队,前面有多少人,你就得等多少时间,人少还行,人多就磨叽;

O(n²)最坑,相当于你排一次队只能拿一双筷子,再排一次拿碗,再排一次拿菜,人稍微多点,直接饿到放弃——这就是为啥会触发TLE。

所以写代码别光图“能跑通”,先给算法“染个色”,选绿灯或黄灯,避开警示橙,你的程序才能跑得又快又稳,永远跟TLE说拜拜。