我有理解算法时间复杂度的问题。
让我们的第一个例子采用这种算法在二叉搜索树中搜索:
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
那么,如何计算这个时间复杂度呢?
让我们以这种递归算法为例:
int f(int a, int b)
{
if (a > 0)
return f(a − 1, b − 3);
else
return b;
}
所以,我认为这个算法的时间复杂度是O(a),因为结束条件只取决于a
参数。
如果我写下来:
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)
那么,我怎么知道这是线性时间复杂度?只是因为当a小于1时递归将结束?
最后:
答案 0 :(得分:1)
至于树搜索算法的复杂性 - 尝试考虑每次迭代时都会发生变化的事情。提示:考虑树中current_node的深度。
尝试使用归纳来证明这种特殊情况下的线性复杂性。你知道T(0,x)将以单个调用结束,这将是你的基础。尝试证明T(n,x)将执行n次递归调用。
答案 1 :(得分:1)
在二叉搜索树中查找值的最坏情况时间复杂度是多少?最糟糕的情况是你必须下降到最深的叶子。通常,n
个节点的二叉树可以具有深度O(n)
。 (想想一个案例,每个孩子都是一片叶子,而左边的孩子往下行。)但是,如果你保持一个平衡的二元搜索树,例如red-black tree,你的高度为{{1} }。这是红黑树中键查找操作的最坏情况运行时间。
您的函数O(log n)
定义为:
f
if f(a, b) = f(a − 1, b − 3)
a > 0
否则我们可以通过f(a, b) = b
上的归纳证明,评估a
f(a, b)
的任何非负值都需要a
a
次f
次呼叫。在基本情况下,a == 0
只调用f
一次。对于正数a
,假设f(a - 1, b)
被称为a - 1
次。然后,评估f(a, b)
需要a - 1 + 1
= a
次来电f
。 (顺便说一句,我们可以观察到f(a, b) = b - 3*a
并实现恒定时间。)
每个递归算法都可以转换为迭代算法,该算法模拟执行递归函数调用的堆栈。观察计算机执行迭代以实现递归程序。更深刻的是,图灵机是迭代的。计算机科学的一个公理是可以用图灵机计算所有可以计算的东西。 lambda演算提供的计算能力不比图灵机那么大。
递归算法通常比迭代算法占用更多的时间和空间,因为它们需要为每个函数调用在堆栈上分配新的帧。
如果以每个函数调用处于尾部位置的方式编写递归函数,意味着调用不返回需要进一步计算的中间值,则它是尾递归函数。递归计算不依赖于递归调用的参数以外的任何值。因此,对函数的最终调用会立即生成最终结果,并且无需返回递归调用链。
编译器可以实现尾递归,以便重用当前帧,而不必在堆栈上分配新帧。例如,方案编译器需要这样做。由此产生的计算具有迭代的性能特征,但代码具有递归的表现优势。
答案 2 :(得分:1)
二叉树算法最多访问树中的每个节点一次;否则二叉树中有一个循环,这是一个矛盾。此外,在最坏的情况下,它至少访问每个节点一次。因此,对节点的访问次数是Theta(n),并且由于每次访问需要Theta(1)时间,因此最坏情况下的运行时间是Theta(n)。
我们&#34;知道的方式&#34;复发的解决方案是通过归纳证明。在您的情况下,基础是
任何b的T(0,b)= c
对于任何b,诱导接触是T(n,b)<= c(n + 1)。诱导步骤
T(n,b)&lt; = c + T(n-1,b-3)&lt; = c + cn = c(n + 1)。
由此得出T(n)= O(n)。
不,递归算法不一定慢,是的,循环可以被尾递归替换。