计算图中路径的递归函数的复杂性

Posted

技术标签:

【中文标题】计算图中路径的递归函数的复杂性【英文标题】:Complexity of a recursive function that counts paths in graph 【发布时间】:2015-02-19 21:10:24 【问题描述】:

我找到了一个有向图的函数,它对于其中的顶点“u”和“v”,它计算从“u”到“v”的所有可能步行,步行上正好有 k 个边。 代码和算法来自here。所以,

// C++ program to count walks from u to v with exactly k edges
#include <iostream>
using namespace std;

// Number of vertices in the graph
#define V 4

// A naive recursive function to count walks from u to v with k edges
int countwalks(int graph[][V], int u, int v, int k)

   // Base cases
   if (k == 0 && u == v)      return 1;
   if (k == 1 && graph[u][v]) return 1;
   if (k <= 0)                return 0;

   // Initialize result
   int count = 0;

   // Go to all adjacents of u and recur
   for (int i = 0; i < V; i++)
       if (graph[u][i])  // Check if is adjacent of u
           count += countwalks(graph, i, v, k-1);

   return count;

我试图找出并证明这个算法的复杂性。根据帖子:

"上述函数的最坏情况时间复杂度为O(V^k) 其中V 是给定图中的顶点数。我们可以简单分析 通过绘制递归树来计算时间复杂度。最坏的情况发生在 完整的图表。在最坏的情况下,递归树的每个内部节点 正好有 n 个孩子。”

但是,我找不到导致我可以分析的树的递归,以证明该算法是O(V^k)。另外,我认为最好的情况是Theta(1)。真的吗?一般情况下呢?

【问题讨论】:

P(k+1) = VP(k)。我错过了什么吗?是的,最好的情况是 Θ(1)。而且我不知道如何谈论平均情况(具有 V(V-1)/2 边的随机图?)。 【参考方案1】:

对于完整的图,每个节点都连接到其他节点,因此您的 for 循环将进行 |V| 递归调用。这将在每次递归调用时发生,直到 k 变为 1,所以总共有 O(|V|^k) 递归调用。

你可以这样表达:

T(V, k) = |V|*T(V, k - 1)
        = |V|*|V|*T(V, k - 2)
        = |V|^2*|V|*T(V, k - 3)
        = ...

总是T(V, _),因为一个节点可以被多次访问。

当前三个 if 条件之一在第一次调用期间触发时,最好的情况确实是 O(1)

我不确定的平均情况,但我认为它应该仍然很糟糕。考虑一个链表图和一个巨大的k:您将多次遍历相同的边以使k 为0 或1。随着您添加更多路径,这种情况会逐渐变得更糟。

【讨论】:

【参考方案2】:

一般的“平均情况”分析在这样的问题中有点不适,因为您可以选择如何定义“随机”图。一种方法是说每个可能的边都以某个概率 p 出现,0

【讨论】:

以上是关于计算图中路径的递归函数的复杂性的主要内容,如果未能解决你的问题,请参考以下文章

计算递归函数的时间复杂度

递归函数c ++的复杂性

计算给定递归函数的精确运行时间(时间复杂度)

请问递归算法的时间复杂度如何计算呢?

LeetCode112.路径总和(C++描述,递归解法)

LeetCode112.路径总和(C++描述,递归解法)