简体   繁体   English

迭代和递归算法的时间复杂度

[英]Time complexity of iterative and recursive algorithms

I have problem understanding time complexity of algorithms. 我在理解算法的时间复杂性时遇到问题。

Let's for first example take this algorithm for searching in binary search tree: 让我们以第一个示例为例,采用此算法在二进制搜索树中进行搜索:

def search_iteratively(key, node): 
     current_node = node
     while current_node is not None:
         if key == current_node.key:
             return current_node
         elif key < current_node.key:
             current_node = current_node.left
         else:  # key > current_node.key:
             current_node = current_node.right
     return None

So, how to calculate this time complexity? 那么,该如何计算时间复杂度呢?

Let's take for example this recursive algorithm: 让我们以这个递归算法为例:

int f(int a, int b) 
{ 
    if (a > 0)
        return f(a − 1, b − 3); 
    else 
        return b;
}

So, I presume that time complexity of this algorithm is O(a), because end condition depends only on a parameter. 所以,我相信这个算法的那段时间复杂度为O(一),因为最终状态仅取决于a参数。

If I write this down: 如果我写下来:

T(a, b) = O(1) where a <= 0
T(a, b) = T(a-1, b-3) where a > 0

T(a, b) = 
T(a-1, b-3) = 
T(a-1, b-3) + T(a-2, b-6) = 
T(a-1, b-3) + T(a-2, b-6) + T(a-3, b-9)

So, how do I know that this is linear time complexity? 那么,我怎么知道这是线性时间复杂度? Just because the recursion will end when a is less than 1? 仅仅因为递归将在a小于1时结束?

And for the end: 最后:

  • is it true that we can convert every recursive algorithm into iterative? 我们可以将每个递归算法都转换为迭代器吗?
  • is it the speed of recursive algorithms normally slover than than iterative? 递归算法的速度通常比迭代速度慢吗?
  • can we substitute tail recursion with loop? 我们可以用循环代替尾递归吗?

As for the complexity of the tree search algorithm - try to think of something that changes on each iteration. 至于树型搜索算法的复杂性,请尝试考虑每次迭代都会更改的内容。 TIP: think of the depth of current_node in the tree. 提示:考虑树中current_node的深度。

Try to use induction to prove linear complexity in this particular case. 在这种特殊情况下,尝试使用归纳法证明线性复杂度。 You know that T(0, x) will end with a single call and this would be your base. 您知道T(0,x)将以一个调用结束,这将成为您的基础。 Try proving that T(n, x) will perform n recursive calls. 尝试证明T(n,x)将执行n个递归调用。

  • There is a theorem that every iterative algorithm can be converted to recursion and vise versa 有一个定理,每个迭代算法都可以转换为递归,反之亦然
  • If you implement the same algorithm without any optimization recursively and iteratively recursion will be slower because there are function calls - a new frame will have to be allocated on the stack and then it will have to be popped 如果您在没有任何递归优化的情况下实现相同的算法,并且由于存在函数调用,则递归递归将较慢-必须在堆栈上分配一个新帧,然后将其弹出
  • It is relatively easy to substitute tail recursion with loop in most cases 在大多数情况下,用循环替换尾部递归相对容易

What is the worst-case time complexity of finding a value in a binary search tree? 在二进制搜索树中找到值的最坏情况下的时间复杂度是多少? The worst case is when you have to descend to the deepest leaf. 最坏的情况是您必须下降到最深的叶子。 In general, a binary tree of n nodes can have depth O(n) . 通常, n节点的二叉树可以具有深度O(n) (Think of a case where every right child is a leaf while the left children descend ever downward.) However, if you maintain a balanced binary search tree such as a red-black tree , you are guaranteed a height of O(log n) . (考虑到每个右孩子都是叶子而左孩子向下下降的情况。)但是,如果维护平衡的二叉搜索树(例如红黑树) ,则可以保证高度为O(log n) That is the worst-case running time of the key-find operation in a red-black tree. 这是在红黑树中进行键查找操作的最坏情况下的运行时间。

Your function f is defined as: 您的函数f定义为:

  • f(a, b) = f(a − 1, b − 3) if a > 0 如果a > 0 f(a, b) = f(a − 1, b − 3)
  • f(a, b) = b otherwise f(a, b) = b否则

We can prove by induction on a that evaluating f(a, b) for any non-negative value of a requires a calls to f . 我们可以通过归纳法证明a ,在评价f(a, b)对任何非负值a需要a到呼叫f In the base case, with a == 0 , f is called just once. 在基本情况下,如果a == 0 ,则f仅被调用一次。 For positive a , assume that f(a - 1, b) is called a - 1 times. 对于正a ,假设f(a - 1, b)被称为a - 1次。 Then evaluating f(a, b) requires a - 1 + 1 = a calls to f . 然后评估f(a, b)需要a - 1 + 1 = af a调用。 (By the way, we can observe that f(a, b) = b - 3*a and arrive at a constant-time implementation.) (顺便说一下,我们可以观察到f(a, b) = b - 3*a并得出一个恒定时间的实现。)

Every recursive algorithm can be converted into an iterative algorithm that simulates a stack on which recursive function calls are executed. 每个递归算法都可以转换为迭代算法,该算法模拟在其上执行递归函数调用的堆栈。 Observe that the computer performs iteration to implement your recursive program. 观察计算机执行迭代以实现您的递归程序。 More profoundly, Turing machines are iterative. 更深刻地讲,图灵机是迭代的。 It is an axiom of computer science that everything that can be computed can be computed with a Turing machine. 计算机科学的一个公理是,可以使用图灵机来计算所有可以计算的东西。 The lambda calculus offers no greater computational power than the Turing machine. 拉姆达演算没有比图灵机提供更大的计算能力。

Recursive algorithms generally take more time and space than iterative algorithms because they require allocating a new frame on the stack for each function call. 递归算法通常比迭代算法占用更多的时间和空间,因为它们需要为每个函数调用在堆栈上分配新的帧。

If a recursive function is written in such a way that every function call is in tail position, meaning that the call does not return an intermediate value requiring further computation, it is a tail-recursive function. 如果以每个函数调用都位于尾部位置的方式编写递归函数,这意味着该调用不会返回需要进一步计算的中间值,则它是尾部递归函数。 The recursive computation does not depend on any values other than the arguments to the recursive call. 递归计算不依赖于递归调用的参数以外的任何值。 Thus, the final call to the function immediately produces the final result and there is no need to go back up the chain of recursive calls. 因此,对函数的最终调用会立即产生最终结果,而无需返回递归调用链。

A compiler can implement tail recursion in such a way that the current frame is reused instead of having to allocate a new frame on the stack. 编译器可以以重新使用当前帧的方式实现尾递归,而不必在堆栈上分配新的帧。 Scheme compilers, for example, are required to do this. 例如,需要方案编译器来执行此操作。 The resulting computation has the performance characteristics of iteration, yet the code has the expressive advantages of recursion. 所得的计算具有迭代的性能特征,但是代码具有递归的表达优势。

The binary tree algorithm visits each node in the tree at most once; 二叉树算法最多访问树中的每个节点一次; otherwise there is a loop in the binary tree, a contradiction. 否则,二叉树中会有一个循环,这是一个矛盾。 Furthermore in the worst case it visits every node at least once. 此外,在最坏的情况下,它至少会访问每个节点一次。 Therefore the number of visits to nodes is Theta(n) and, since each visit requires Theta(1) time, the worst-case running time is Theta(n). 因此,对节点的访问次数为Theta(n),并且由于每次访问都需要Theta(1)时间,因此最坏的运行时间为Theta(n)。

The way we "know" the solution to a recurrence is by inductive proof. 我们“知道”递归解决方案的方式是归纳证明。 In your case, the basis is 您的情况是

T(0,b) = c for any b 任何b的T(0,b)= c

The induction hypthesis is T(n,b) <= c(n+1) for any b. 对于任何b,归纳假设为T(n,b)<= c(n + 1)。

The induction step is 归纳步骤是

T(n,b) <= c + T(n-1,b-3) <= c + cn = c(n+1). T(n,b)<= c + T(n-1,b-3)<= c + cn = c(n + 1)。

It follows that T(n) = O(n). 因此,T(n)= O(n)。

No, a recursive algorithm is not necessarily slower, ane yes, a loop can be replaced by tail recursion. 不,递归算法不一定较慢,是的,可以用尾递归代替循环。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM