我已经通过谷歌和Stack Overflow搜索,但无处我能够找到一个清楚和直接的解释如何计算时间复杂度。

我已经知道了什么?

比如下面这样简单的代码:

char h = 'y'; // This will be executed 1 time
int abc = 0; // This will be executed 1 time

比如下面这样的循环:

for (int i = 0; i < N; i++) {
    Console.Write('Hello, World!!');
}

int i = 0;这将只执行一次。

时间实际上是计算到i=0,而不是声明。

i < N;这将被执行N+1次 i++将执行N次

因此,这个循环所需的操作数为{1+(N+1)+N} = 2N+2。(但这仍然可能是错误的,因为我对自己的理解没有信心。)

好的,这些基本的小计算我知道,但在大多数情况下,我看到的时间复杂度是O(N) O(N²)O(log N) O(N !)等等。


当前回答

循环的几个例子。

O(n) time complexity of a loop is considered as O(n) if the loop variables is incremented / decremented by a constant amount. For example following functions have O(n) time complexity. // Here c is a positive integer constant for (int i = 1; i <= n; i += c) { // some O(1) expressions } for (int i = n; i > 0; i -= c) { // some O(1) expressions } O(nc) time complexity of nested loops is equal to the number of times the innermost statement is executed. For example, the following sample loops have O(n2) time complexity for (int i = 1; i <=n; i += c) { for (int j = 1; j <=n; j += c) { // some O(1) expressions } } for (int i = n; i > 0; i += c) { for (int j = i+1; j <=n; j += c) { // some O(1) expressions } For example, selection sort and insertion sort have O(n2) time complexity. O(log n) time complexity of a loop is considered as O(log n) if the loop variables is divided / multiplied by a constant amount. for (int i = 1; i <=n; i *= c) { // some O(1) expressions } for (int i = n; i > 0; i /= c) { // some O(1) expressions } For example, [binary search][3] has _O(log&nbsp;n)_ time complexity. O(log log n) time complexity of a loop is considered as O(log log n) if the loop variables is reduced / increased exponentially by a constant amount. // Here c is a constant greater than 1 for (int i = 2; i <=n; i = pow(i, c)) { // some O(1) expressions } //Here fun is sqrt or cuberoot or any other constant root for (int i = n; i > 0; i = fun(i)) { // some O(1) expressions }


时间复杂度分析的一个例子

int fun(int n)
{
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j < n; j += i)
        {
            // Some O(1) task
        }
    }
}

分析:

For i = 1, the inner loop is executed n times.
For i = 2, the inner loop is executed approximately n/2 times.
For i = 3, the inner loop is executed approximately n/3 times.
For i = 4, the inner loop is executed approximately n/4 times.
…………………………………………………….
For i = n, the inner loop is executed approximately n/n times.

因此上述算法的总时间复杂度为(n + n/2 + n/3 +…+ n/n),即为n *(1/1 + 1/2 + 1/3 +…+ 1/n)

级数(1/1 + 1/2 + 1/3 +…+ 1/n)的重要之处在于大约是O(log n),所以上面代码的时间复杂度是O(n·log n)。


引用:

1 2 3

其他回答

当你分析代码时,你必须一行一行地分析,计算每个操作/识别时间复杂度。最后,你必须把它加起来才能得到整体。

例如,你可以有一个线性复杂度的简单循环,但在同一个程序中,你可以有一个三次循环,它具有三次复杂度,所以你的程序将具有三次复杂度。函数的生长顺序在这里起作用。

让我们看看一个算法的时间复杂度有哪些可能,你可以看到我上面提到的增长顺序:

Constant time has an order of growth 1, for example: a = b + c. Logarithmic time has an order of growth log N. It usually occurs when you're dividing something in half (binary search, trees, and even loops), or multiplying something in same way. Linear. The order of growth is N, for example int p = 0; for (int i = 1; i < N; i++) p = p + 2; Linearithmic. The order of growth is n·log N. It usually occurs in divide-and-conquer algorithms. Cubic. The order of growth is N3. A classic example is a triple loop where you check all triplets: int x = 0; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) for (int k = 0; k < N; k++) x = x + 2 Exponential. The order of growth is 2N. It usually occurs when you do exhaustive search, for example, check subsets of some set.

循环的几个例子。

O(n) time complexity of a loop is considered as O(n) if the loop variables is incremented / decremented by a constant amount. For example following functions have O(n) time complexity. // Here c is a positive integer constant for (int i = 1; i <= n; i += c) { // some O(1) expressions } for (int i = n; i > 0; i -= c) { // some O(1) expressions } O(nc) time complexity of nested loops is equal to the number of times the innermost statement is executed. For example, the following sample loops have O(n2) time complexity for (int i = 1; i <=n; i += c) { for (int j = 1; j <=n; j += c) { // some O(1) expressions } } for (int i = n; i > 0; i += c) { for (int j = i+1; j <=n; j += c) { // some O(1) expressions } For example, selection sort and insertion sort have O(n2) time complexity. O(log n) time complexity of a loop is considered as O(log n) if the loop variables is divided / multiplied by a constant amount. for (int i = 1; i <=n; i *= c) { // some O(1) expressions } for (int i = n; i > 0; i /= c) { // some O(1) expressions } For example, [binary search][3] has _O(log&nbsp;n)_ time complexity. O(log log n) time complexity of a loop is considered as O(log log n) if the loop variables is reduced / increased exponentially by a constant amount. // Here c is a constant greater than 1 for (int i = 2; i <=n; i = pow(i, c)) { // some O(1) expressions } //Here fun is sqrt or cuberoot or any other constant root for (int i = n; i > 0; i = fun(i)) { // some O(1) expressions }


时间复杂度分析的一个例子

int fun(int n)
{
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j < n; j += i)
        {
            // Some O(1) task
        }
    }
}

分析:

For i = 1, the inner loop is executed n times.
For i = 2, the inner loop is executed approximately n/2 times.
For i = 3, the inner loop is executed approximately n/3 times.
For i = 4, the inner loop is executed approximately n/4 times.
…………………………………………………….
For i = n, the inner loop is executed approximately n/n times.

因此上述算法的总时间复杂度为(n + n/2 + n/3 +…+ n/n),即为n *(1/1 + 1/2 + 1/3 +…+ 1/n)

级数(1/1 + 1/2 + 1/3 +…+ 1/n)的重要之处在于大约是O(log n),所以上面代码的时间复杂度是O(n·log n)。


引用:

1 2 3

从这里开始-算法的时间复杂度介绍

1. 简介

在计算机科学中,算法的时间复杂度将算法运行所需的时间量化为表示输入的字符串长度的函数。

2. 大O符号

算法的时间复杂度通常用大O符号表示,其中不包括系数和低阶项。当以这种方式表示时,时间复杂度被称为渐近描述,即当输入大小趋于无穷大时。

例如,如果一个算法对所有大小为n的输入所需要的时间最多为5n3 + 3n,则渐近时间复杂度为O(n3)。稍后再详细介绍。

再举几个例子:

1 = O(n) n = O(n2) log(n) = O(n) 2 n + 1 = O(n)

3.O(1)常数时间:

不管输入大小如何,如果一个算法需要相同的时间,我们就说它在常数时间内运行。

例子:

数组:访问任意元素 固定大小的堆栈:push和pop方法 固定大小的队列:入队列和出队列方法

4. O(n)线性时间

如果一个算法的时间执行与输入大小成正比,即时间随着输入大小的增加而线性增长,则该算法被称为在线性时间内运行。

考虑下面的例子。下面我线性搜索一个元素,它的时间复杂度是O(n)。

int find = 66;
var numbers = new int[] { 33, 435, 36, 37, 43, 45, 66, 656, 2232 };
for (int i = 0; i < numbers.Length - 1; i++)
{
    if(find == numbers[i])
    {
        return;
    }
}

更多的例子:

数组:线性搜索,遍历,查找最小值等 ArrayList:包含方法 Queue:包含方法

5. O(log n)对数时间:

如果一个算法的执行时间与输入大小的对数成正比,则该算法在对数时间内运行。

示例:二分搜索

回想一下“二十个问题”游戏——任务是在一段时间内猜测一个隐藏数字的值。每次你猜的时候,都会被告知你猜的是太高还是太低。二十题游戏暗示了一种策略,使用你的猜测数字来减半间隔大小。这是一个被称为二分搜索的一般问题解决方法的例子。

6. O(n2)二次时间

如果一个算法的执行时间与输入大小的平方成正比,我们就说它在二次时间内运行。

例子:

冒泡排序 选择排序 插入排序

7. 一些有用的链接

大0误解 算法复杂度的确定 大O小抄

简单地说,时间复杂度是一种总结算法的操作数量或运行时间如何随着输入大小的增加而增长的方式。

就像生活中的大多数事情一样,鸡尾酒会可以帮助我们理解。

O(N)

当你到达派对时,你必须和每个人握手(每件事都要做一次操作)。随着出席人数N的增加,你与每个人握手的时间/工作时间也随着O(N)的增加而增加。

为什么是O(N)而不是cN?

与人握手所用的时间各不相同。你可以把它平均出来,用常数c来表示。但这里的基本操作——和每个人握手——总是与O(N)成正比,不管c是多少。当我们讨论是否应该参加鸡尾酒会时,我们通常更感兴趣的是我们必须与每个人见面,而不是那些会面的细节。

O (N ^ 2)

鸡尾酒会的主人想让你玩一个愚蠢的游戏,让每个人都遇见其他人。因此,你必须会见N-1个人,因为下一个人已经见过你,他们必须会见N-2个人,以此类推。这个级数的和是x^2/2+x/2。随着参与者人数的增加,x^2项很快就变大了,所以我们就把其他的都丢掉了。

O (N ^ 3)

你必须和其他人见面,在每次会议期间,你必须谈论房间里的其他人。

O(1)

主持人想宣布一件事。他们敲着酒杯大声说话。每个人都能听到。事实证明,不管有多少参与者,这个操作总是花费相同的时间。

O(对数 N)

主人已经把每个人按字母顺序排列在桌子上。丹在哪里?你推断他一定在亚当和曼迪之间(当然不是在曼迪和扎克之间!)。鉴于此,他在乔治和曼迪之间吗?不。他肯定在亚当和弗雷德之间,在辛迪和弗雷德之间。等等……我们可以有效地定位丹,只需要看一半的集合,然后再看一半的集合。最终,我们观察O(log_2 N)个个体。

O(nlogn)

您可以使用上面的算法找到在桌子旁的位置。如果有很多人来到这张桌子前,一次一个,所有人都这样做,这将花费O(nlog N)个时间。结果证明,这是对必须进行比较的项目集合进行排序所需的时间。

最好/最坏情况

你到达了派对,需要找到伊尼戈——需要多长时间?这取决于你什么时候到。如果每个人都在周围转悠,你就遇到了最坏的情况:将花费O(N)时间。然而,如果每个人都坐在桌子旁,只需要O(log N)时间。或者你可以利用主持人的酒杯呐喊能力,这样只需要O(1)时间。

假设主机不可用,我们可以说inigo查找算法的下界为O(log N),上界为O(N),这取决于您到达时聚会的状态。

空间与通信

同样的想法也可以应用于理解算法如何使用空间或通信。

Knuth写了一篇关于前者的好论文,题为《歌曲的复杂性》。

定理2:存在复杂度为O(1)的任意长歌曲。 证据:(由于凯西和阳光乐队)。考虑一下Sk用(15)定义的歌曲,但是用

V_k = 'That's the way,' U 'I like it, ' U
U   = 'uh huh,' 'uh huh'

对于所有的k。

O(n)是大O符号,用于表示算法的时间复杂度。当你把一个算法的执行次数加起来时,你会得到一个像2N+2这样的结果。在这个表达式中,N是主导项(N的值增加或减少对表达式影响最大的项)。O(N)是时间复杂度N是主要项。

例子

For i = 1 to n;
  j = 0;
while(j <= n);
  j = j + 1;

这里内层循环的总执行次数为n+1,外层循环的总执行次数为n(n+1)/2,因此整个算法的总执行次数为n+1 + n(n+1/2) = (n2 + 3n)/2。 这里n²是主导项所以这个算法的时间复杂度是O(n2)