我理解,有算法的地方,就有时间复杂度。
如果语言中有算法,则一定有时间复杂度。
甚至一条机器指令,也有时间复杂度,比如加法指令时间复杂度是常数,而除法指令则与除数和被除数有关。
像Java这种内建支持String类型的语言,所有的String操作都有其时间复杂度。因为即使表面上看起来好像只有一条语句(s += "a";),也有其实现方式,也就是算法,所以必然有时间复杂度。也就是说:当s为很短的字符串时所花的时间,与s为很长时所花的时间肯定是不一样的。
如果不理解这一点,就会写出小数据量下可以工作,大数据量下停止响应的代码。
比如method1(),当循环次数达到10^5次时,需要十几秒钟的时间,勉强可以忍受;达到10^6次时,我以最大的耐心都等不到结果(超过10分钟的等待)。
而method2(),在我的机器上,即使执行10^7次,也才仅仅消耗328ms,都还在可接受的程度范围(再尝试10^8导致OutOfMemory异常了,因此放弃)。
我理解,这就是时间复杂度的威力,也是为什么我们要分析时间复杂度的原因。
下面是我的测试代码:
public class Main { public static void main(String...args) { System.out.format("method2\n"); // 循环次数不能再多,否则OutOfMemory异常: for (long i = 10; i<=10000000; i*=10) { System.out.format("%1$ 10d%2$ 10d\n", i, method2(i)); } // 循环次数不能再多,否则要等待很久才能得到结果: System.out.format("method1\n"); for (long i = 10; i<=1000000; i*=10) { System.out.format("%1$ 10d%2$ 10d\n", i, method1(i)); } } public static long method1(long count) { long t0 = System.currentTimeMillis(); String s = ""; for (long i = 0; i < count; i++) { s += "a"; } return (System.currentTimeMillis()-t0); } public static long method2(long count) { long t0 = System.currentTimeMillis(); StringBuilder sb = new StringBuilder(); for (long i = 0; i < count; i++) { sb.append("a"); } return (System.currentTimeMillis()-t0); } }
测试结果汇总:
N method1 ms method2( ms)
10 0 0
100 0 0
1000 0 0
10000 141 0
100000 14140 0
1000000 2117750 31
10000000 n/a 328
此结果支持下列说法:
method1与N不是线性关系,时间复杂度肯定不是O(N)
method2与N基本是线性关系,时间复杂度为O(N)