3
votes

Path Sum
Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum.
For example: sum = 11.

    5 
   / \ 
  4   8
 /   / \ 
2  -2   1 

The answer is :

[
  [5, 4, 2], 
  [5, 8, -2]
]

Personally I think, the time complexity = O(2^n), n is the number of nodes of the given binary tree.


Thank you Vikram Bhat and David Grayson, the tight time complexity = O(nlogn), n is the number of nodes in the given binary tree.
  • Algorithm checks each node once, which causes O(n)
  • "vector one_result(subList);" will copy entire path from subList to one_result, each time, which causes O(logn), because the height is O(logn).

So finally, the time complexity = O(n * logn) =O(nlogn).


The idea of this solution is DFS[C++].
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
#include <vector>
using namespace std;

class Solution {
public:
    vector<vector<int> > pathSum(TreeNode *root, int sum) {
        vector<vector<int>> list;

        // Input validation.
        if (root == NULL) return list;

        vector<int> subList;
        int tmp_sum = 0;

        helper(root, sum, tmp_sum, list, subList);

        return list;
    }

    void helper(TreeNode *root, int sum, int tmp_sum, 
                vector<vector<int>> &list, vector<int> &subList) {

        // Base case.
        if (root == NULL) return;
        if (root->left == NULL && root->right == NULL) {
            // Have a try.
            tmp_sum += root->val;
            subList.push_back(root->val);

            if (tmp_sum == sum) {
                vector<int> one_result(subList);
                list.push_back(one_result);
            }

            // Roll back.
            tmp_sum -= root->val;
            subList.pop_back();

            return;
        }

        // Have a try.
        tmp_sum += root->val;
        subList.push_back(root->val);

        // Do recursion.
        helper(root->left, sum, tmp_sum, list, subList);
        helper(root->right, sum, tmp_sum, list, subList);

        // Roll back.
        tmp_sum -= root->val;
        subList.pop_back();
    }
};
4
This question appears to be off-topic because it is about algorithm theory and not programming.Kerrek SB
I am sorry, but I think the algorithm theory is a part of programming.Zhaonan
@KerrekSB: you do realize we have an algorithm tag here for a reason?lpapp

4 Answers

5
votes

Though it seems that time complexity is O(N) but if you need to print all paths then it is O(N*logN). Suppose that u have a complete binary tree then the total paths will be N/2 and each path will have logN nodes so total of O(N*logN) in worst case.

3
votes

Your algorithm looks correct, and the complexity should be O(n) because your helper function will run once for each node, and n is the number of nodes.

Update: Actually, it would be O(N*log(N)) because each time the helper function runs, it might print a path to the console consisting of O(log(N)) nodes, and it will run O(N) times.

1
votes
TIME COMPLEXITY

The time complexity of the algorithm is O(N^2), where ‘N’ is the total number of nodes in the tree. This is due to the fact that we traverse each node once (which will take O(N)), and for every leaf node we might have to store its path which will take O(N).

We can calculate a tighter time complexity of O(NlogN) from the space complexity discussion below.

SPACE COMPLEXITY

If we ignore the space required for all paths list, the space complexity of the above algorithm will be O(N) in the worst case. This space will be used to store the recursion stack. The worst-case will happen when the given tree is a linked list (i.e., every node has only one child).

How can we estimate the space used for the all paths list? Take the example of the following balanced tree:

          1
      /      \
     2        3
   /   \    /   \
  4     5  6     7

Here we have seven nodes (i.e., N = 7). Since, for binary trees, there exists only one path to reach any leaf node, we can easily say that total root-to-leaf paths in a binary tree can’t be more than the number of leaves. As we know that there can’t be more than N/2 leaves in a binary tree, therefore the maximum number of elements in all paths list will be O(N/2) = O(N). Now, each of these paths can have many nodes in them. For a balanced binary tree (like above), each leaf node will be at maximum depth. As we know that the depth (or height) of a balanced binary tree is O(logN) we can say that, at the most, each path can have logN nodes in it. This means that the total size of the all paths list will be O(N*logN). If the tree is not balanced, we will still have the same worst-case space complexity.

From the above discussion, we can conclude that the overall space complexity of our algorithm is O(N*logN).

Also from the above discussion, since for each leaf node, in the worst case, we have to copy log(N) nodes to store its path, therefore the time complexity of our algorithm will also be O(N*logN).

1
votes

The worst case time complexity is not O(nlogn), but O(n^2).

  • to visit every node, we need O(n) time

  • to generate all paths, we have to add the nodes to the path for every valid path.
    So the time taken is sum of len(path). To estimate an upper bound of the sum: the number of paths is bounded by n, the length of path is also bounded by n, so O(n^2) is an upper bound. Both worst case can be reached at the same time if the top half of the tree is a linear tree, and the bottom half is a complete binary tree, like this:

    1
     1
      1
       1
        1
      1   1
     1 1 1 1
    

number of paths is n/4, and length of each path is n/2 + log(n/2) ~ n/2