【问题标题】:Complexity of printing all root to leaf paths in binary tree在二叉树中打印所有根到叶路径的复杂性
【发布时间】:2021-09-29 07:36:14
【问题描述】:

https://www.techiedelight.com/print-all-paths-from-root-to-leaf-nodes-binary-tree/ 中,下面提供了为每个叶节点打印根到叶的代码。

他们说算法是 O(n),但我认为它应该是 O(n log n),其中 n 是节点数。标准 DFS 通常为 O(n + E),但打印路径似乎会添加 log n。假设 h 是完美二叉树的高度。最后一层有 n/2 个节点,因此我们需要打印 n/2 个路径。每条路径都有 h + 1(为了数学简单,我们只是说它是 h)节点。所以我们需要在打印所有路径时最终打印 h * n/2 个节点。我们知道 h = log2(n)。所以 h * n/2 = O(n log n)?

他们的回答是错的,还是我这里的分析有问题?

#include <iostream>
#include <vector>
using namespace std;
 
// Data structure to store a binary tree node
struct Node
{
    int data;
    Node *left, *right;
 
    Node(int data)
    {
        this->data = data;
        this->left = this->right = nullptr;
    }
};
 
// Function to check if a given node is a leaf node or not
bool isLeaf(Node* node) {
    return (node->left == nullptr && node->right == nullptr);
}
 
// Recursive function to find paths from the root node to every leaf node
void printRootToleafPaths(Node* node, vector<int> &path)
{
    // base case
    if (node == nullptr) {
        return;
    }
 
    // include the current node to the path
    path.push_back(node->data);
 
    // if a leaf node is found, print the path
    if (isLeaf(node))
    {
        for (int data: path) {
            cout << data << " ";
        }
        cout << endl;
    }
 
    // recur for the left and right subtree
    printRootToleafPaths(node->left, path);
    printRootToleafPaths(node->right, path);
 
    // backtrack: remove the current node after the left, and right subtree are done
    path.pop_back();
}
 
// The main function to print paths from the root node to every leaf node
void printRootToleafPaths(Node* node)
{
    // vector to store root-to-leaf path
    vector<int> path;
 
    printRootToleafPaths(node, path);
}
 
int main()
{
    /* Construct the following tree
              1
            /   \
           /     \
          2       3
         / \     / \
        4   5   6   7
               /     \
              8       9
    */
 
    Node* root = new Node(1);
    root->left = new Node(2);
    root->right = new Node(3);
    root->left->left = new Node(4);
    root->left->right = new Node(5);
    root->right->left = new Node(6);
    root->right->right = new Node(7);
    root->right->left->left = new Node(8);
    root->right->right->right = new Node(9);
 
    // print all root-to-leaf paths
    printRootToleafPaths(root);
 
    return 0;
}

【问题讨论】:

    标签: c++ algorithm time-complexity


    【解决方案1】:

    算法遍历 O(n) 个节点。对于平衡树,它的总打印量是 O(n lg n),对于任意树,它的总打印量是 O(n^2)。

    这取决于哪些操作有成本。

    例如,存储或递增 n 位数字或指针通常被视为 O(1) 操作。在任何物理计算机中,如果您有 2^100 个节点,您将需要 lg(2^100) 位指针(或节点名称),这将需要比 64 或 32 位节点名称更多的时间来复制。从某种意义上说,复制一个指针需要 O(lg n) 时间!

    但我们不在乎。我们隐含地设定了操作的价格,并根据这些操作给出 O 符号成本。

    在这里,他们将打印整个路径计算为 O(1) 操作,并计算节点遍历,以获得 O(n) 成本,这似乎是合理的。也许他们甚至注意到了,就像您注意到 32 位或 64 位指针所暗示的最大节点数一样。他们没有告诉你他们是如何定价的。

    在标准库算法的规范中也会发生同样的事情;它保证了谓词的最大调用次数。

    【讨论】:

      【解决方案2】:

      平衡二叉树的复杂度是O(n log n),但对于任意二叉树,最坏的情况是O(n2)。

      考虑一棵由以下组成的树:

      1. 链表中的 n/2 个节点在其 rightChild 指针上;和
      2. 最后,n/2 个节点排列成具有 n/4 个叶子的树。

      由于n/4个叶子的深度都超过n/2个节点,所有路径中的节点总数超过n2/8个,即O(n 2)

      【讨论】:

        【解决方案3】:

        寻找路径的时间复杂度是 O(n),它遍历所有节点一次。

        “打印一条路径”的时间复杂度为 O(log n)。 要打印所有路径(n/2 叶),需要 O(n log n)

        然后你需要比较节点遍历成本和打印路径成本。 我相信在大多数现代操作系统中,打印成本远高于节点遍历成本。

        所以实际的时间复杂度是 O(n log n) (对于 print )。

        我假设该网站可能会忽略打印成本,因此它声称时间复杂度为 O(n)。

        【讨论】:

        • 但是我们需要打印O(n)个路径,所以打印所有路径应该是O(n log n)?
        • @roulette01 啊,你是对的。打印所有路径应该是 O(n log n)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-29
        • 1970-01-01
        • 1970-01-01
        • 2018-03-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多