As part of LeetCode, I wrote a simple recursive function to traverse a binary tree and output all the possible paths. (It's not necessary to think about this to answer my question. I'm just providing sample code in order to demonstrate a concrete example.)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> paths;
if( root != nullptr ) {
string str;
binaryTreePathsHelper(root, paths, str);
}
return paths;
}
void binaryTreePathsHelper(TreeNode* node, vector<string>& paths, string str) {
str += std::to_string(node->val);
if( node->left == nullptr &&
node->right == nullptr ) {
paths.push_back(str);
} else {
str += "->";
}
if( node->left != nullptr ) {
binaryTreePathsHelper(node->left, paths, str);
}
if( node->right != nullptr ) {
binaryTreePathsHelper(node->right, paths, str);
}
}
};
LeetCode was saying my solution was rather slow, compared to other submissions:
After a good deal of experimentation, the biggest improvement to my code seemed to be replacing all
foo != nullptr
with simply
foo
e.g.
void binaryTreePathsHelper(TreeNode* node, vector<string>& paths, string str) {
str += std::to_string(node->val);
if( !node->left && !node->right ) {
paths.push_back(str);
} else {
str += "->";
}
if( node->left ) {
binaryTreePathsHelper(node->left, paths, str);
}
if( node->right ) {
binaryTreePathsHelper(node->right, paths, str);
}
}
which instantly brings me to the top:
According to the answers at Performance wise, is it faster to use 'nullptr' or just '0'?, it seems it shouldn't make (this much of) a difference. What am I missing?
Assuming that main.cpp
:
#include <vector>
#include <string>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root);
void binaryTreePathsHelper(TreeNode* node, vector<string>& paths, string str);
};
vector<string> Solution::binaryTreePaths(TreeNode* root) {
vector<string> paths;
if( root != nullptr ) {
string str;
binaryTreePathsHelper(root, paths, str);
}
return paths;
}
void Solution::binaryTreePathsHelper(TreeNode* node, vector<string>& paths, string str) {
str += std::to_string(node->val);
if( node->left == nullptr &&
node->right == nullptr ) {
paths.push_back(str);
} else {
str += "->";
}
if( node->left != nullptr ) {
binaryTreePathsHelper(node->left, paths, str);
}
if( node->right != nullptr ) {
binaryTreePathsHelper(node->right, paths, str);
}
};
While main2.cpp
:
#include <vector>
#include <string>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root);
void binaryTreePathsHelper(TreeNode* node, vector<string>& paths, string str);
};
vector<string> Solution::binaryTreePaths(TreeNode* root) {
vector<string> paths;
if( root ) {
string str;
binaryTreePathsHelper(root, paths, str);
}
return paths;
}
void Solution::binaryTreePathsHelper(TreeNode* node, vector<string>& paths, string str) {
str += std::to_string(node->val);
if( !(node->left) &&
!(node->right) ) {
paths.push_back(str);
} else {
str += "->";
}
if( node->left ) {
binaryTreePathsHelper(node->left, paths, str);
}
if( node->right ) {
binaryTreePathsHelper(node->right, paths, str);
}
};
Let's generate asm code and compare results:
$ gcc -S main.cpp -o main.S -std=c++11
$ gcc -S main2.cpp -o main2.S -std=c++11
$ diff main.S main2.S
1c1
< .file "main.cpp"
---
> .file "main2.cpp"
There is no change in generated asm code (even with -O3
). So no, there is no performance hit possible. At least not with the use of g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
and clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
.