Reputation: 511
Lets say you have 2 vectors
passed to a function as lvalue references
. Later you realize, you can use recursion and pass slices of these vectors
using their iterators
.
Would it be an appropriate strategy if I go ahead and write some util
function to use these vecotrs
as rvalues
? Or I should avoid this in any way?
Simplified pattern:
Node* util(vector<int>&& a, vector<int>&& b) {
Node* root = new Node(a[0]);
root->left = util({ a.begin(), a.end() }, { b.begin(), b.end() });
root->right = util({ a.begin(), a.end() }, { b.begin(), b.end() });
return root;
}
Node* main(vector<int>& a, vector<int>& b) {
return util({ a.begin(), a.end() }, { b.begin(), b.end() });
}
Realistic example (LeetCode 105):
TreeNode* util(vector<int>&& preorder, vector<int>&& inorder) {
if (!preorder.empty()) {
TreeNode* root = new TreeNode(preorder[0]);
auto r = find(inorder.begin(), inorder.end(), preorder[0]);
root->left = util(
{ preorder.begin() + 1, preorder.begin() + 1 + distance(inorder.begin(), r) },
{ inorder.begin(), r });
root->right = util(
{ preorder.begin() + 1 + distance(inorder.begin(), r), preorder.end() },
{ r + 1, inorder.end() });
return root;
}
return nullptr;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return util({ preorder.begin(), preorder.end() },
{ inorder.begin(), inorder.end() });
}
Upvotes: 2
Views: 1348
Reputation: 25526
You are on the wrong way: {a.begin(), a.end()}
is equivalent to std::vector<int>(a.begin(), a.end())
, i. e. calls the constructor accepting two iterators, which copies all data in between these two iterators, i. e. the whole vector in your case (or sub-ranges of the vector in your more realistic example).
But you don't modify the vectors in any way, so no need for any copies.
If you just want to read the original vector, then you can work directly with iterators; you can pass these by value, unter the hoods, they are more or less just pointers into the vector's data anyway:
TreeNode* util
(
vector<int>::const_iterator preorderBegin, vector<int>::const_iterator preorderEnd,
vector<int>::const_iterator inorderBegin, vector<int>::const_iterator inorderEnd
)
{
// ...
++preorderBegin; // if we do so before the call, we have just one addition; compiler
// might optimize to the same code, but we don't rely on it this way
util(preorderBegin, preorderBegin + distance(), inorderBegin, r);
// ...
}
const_iterator
? Well, we want to accept const
vectors:
TreeNode* buildTree(vector<int> const& preorder, vector<int> const& inorder)
// ^ ^
// you don't modify the vectors, so accept them as constant
{
return util(preorder.begin(), preorder.end(), inorder.begin(), inorder.end());
// note the dropped braces...
}
Upvotes: 5