binary search tree assignment operator - c++

I am having a huge issue with my recursive function in my binary search tree. My project is due in a few hours and I cannot for the life of me get ahold of my instructor.
My function seems to only be traversing the left most branch of my tree.
Assignment Operator:
template<typename Type>
BST<Type>& BST<Type>::operator=(const BST& that)
{
if(this != &that)
{
this->clear();
Node *c = that.root;
preORet(c);
}
return *this;
}
Recursive Function Called:
template<typename Type>
void BST<Type>::preORet(Node *c)
{
this->insert(c->data);
if(c->left != nullptr)
preORet(c->left);
else if(c->right != nullptr)
preORet(c->right);
}
As an aside, I understand that a lot of this may look like seriously bastardized code, but this is how my instructor expects it to look.
Thank you in advance.

Your problem is right here:
if(c->left != nullptr)
preORet(c->left);
else if(c->right != nullptr)
preORet(c->right);
You don't want an else if. You want to traverse the right sub-tree regardless of whether the left sub-tree was a nullptr.

In addition to taking out the else in your preORet() function, as the others have pointed out, it's also worth noting that you will get a segmentation fault (error code 11) if you happen to pass in a null pointer through your parameter Node *c.
Here is a solution:
template<typename Type>
void BST<Type>::preORet(Node *c)
{
if (c != nullptr)
{
this->insert(c->data);
preORet(c->left);
preORet(c->right);
}
}
This way, every pointer get's checked to see if it is null before being used, including the left and right pointers. If it is null, it'll just fall through, out of scope.

Get rid of the else in preORet().

Get rid of that else.
As your design looks, at first glance, to be easily move or swap able, I would use the copy-swap idiom to generate a reasonably efficient, easy to write operator=.
But that is just me.

Related

The proper way to increment my binary tree

Here is the class I've created
#include <memory>
template <typename T>
class binary_tree {
private:
T t_data;
std::unique_ptr<binary_tree<T>> t_left, t_right;
class binary_tree_iterator { // -----------------------
private:
T data;
public:
binary_tree_iterator(T d) : data(d) {} // Iterator class
T& operator*() {return data;}
binary_tree_iterator& operator++() {} // <--------- ??????
};
// ------------------------
public:
binary_tree(T d) : t_data(d), t_left(nullptr), t_right(nullptr)
{}
void insert(T data) {
if(data <= t_data) {
if(t_left == nullptr) {
t_left = std::unique_ptr<binary_tree<T>>(new binary_tree<T>(data));
} else {
t_left->insert(data);
}
} else {
if(t_right == nullptr)
t_right = std::unique_ptr<binary_tree<T>>(new binary_tree<T>(data));
else
t_right->insert(data);
}
}
const T data() const {
return t_data;
}
const std::unique_ptr<binary_tree<T>>& left() const {
return t_left;
}
const std::unique_ptr<binary_tree<T>>& right() const {
return t_right;
}
binary_tree_iterator begin() {
if(t_left == nullptr) {
return binary_tree_iterator(t_data);
} else {
return t_left->begin();
}
}
binary_tree_iterator end() {
if(t_right == nullptr) {
return binary_tree_iterator(t_data);
} else {
return t_right->end();
}
}
};
I've declared my iterator class inside of my container class. This may have been a mistake but either way I'm not sure how to define my overloaded increment function. Once I've found begin() I've lost my way back. It seems like unique_ptr() is designed for one way pointing. Assuming I have to use unique_ptr in this fashion, is there some work around here? I've thought about giving each instance of binary_tree a head member that points back from whence it came, but each node should only be accessible from the node above it. I make some sort of index but that seems to completely defeat the purpose of this container type. I'm solving exercise so I'm restricted to using the unique_ptr.
You defined your iterator as containing the data value in your tree.
This is not what iterators are all about. Iterators do not contain the value they're referencing, but rather a reference (in the common meaning of the word, and not a C++ term) to it, typically a pointer.
Of course you can't figure out what to do with ++. For your iterator, it is natural to expect that the ++ operator will advance the iterator to the next node in your tree, but since the iterator does not contain a pointer to anything, you have nothing to advance there, and run into a mental block.
You will need to redesign your iterator so that it contains a pointer to your binary_tree; its * overload dereferences; and the ++ advances to the next element in your binary tree, which it will then be able to do, using its pointer.
At this point you will run into another mental block. Iterating through an entire binary tree requires, at some point, to back up to parent nodes in the tree. After all, after recursing into the left part of the binary tree, at some point, after iterating through the binary tree you will need to, somehow, in some way, wind up in the right part of the binary tree. However, as designed, your binary_tree has no means of navigating to any node's parent. That's another design flaw you will need to address, in some fashion.
It is possible, I suppose, to implement this entire backtracking in the iterator itself, having the iterator record each node its visited, so it can back up to it, when needed. But iterators are supposed to be lightweight objects, barely more than a pointer themselves, and not a full blown data structure that implements complicated operations.
In summary, you have several holes in the design of your binary tree that you will need to address, before you can implement an effective iterator for it.

Sorting vector of smart pointers: mysterious crash

I am trying to sort a vector of smart pointers to a class. I use a struct as the third parameter to std::sort with operator():
struct PhraseSmartPtrParseCreationComparer
{
bool operator()(const std::shared_ptr<Phrase>& a, const std::shared_ptr<Phrase>& b)
{
if (a.get() == nullptr)
return b.get() != nullptr;
if (b.get() == nullptr)
return a.get() != nullptr;
return *a < *b;
}
};
Once in a while, I get a segmentation fault where one of the pointers inside the comparing method points to an invalid structure; always one. The interesting bit is, just before the sort, all the objects are intact; I also tried modifying the function to remove the reference bit: const std::shared_ptr<Phrase> a, and it crashed elsewhere.
The call is nothing fancy:
std::sort(_detectedPhrases.begin(), _detectedPhrases.end(), PhraseSmartPtrParseCreationComparer());
Is there something I'm missing or I should be looking elsewhere?
I can't believe how fast it was resolved. Here is the explanation - thank you for your guidance and cues, #BoBTFish and #Jabberwocky.
Indeed, the reason was that the sorter was having it both ways. The result was not symmetric. That is, the same two items, when swapped, could sometimes produce the same answer. Unfortunately, because of the business logic, it was actually valid. It's basically a tree, and one of the comparison components was whether one item is allowed to be a parent of the other - and a situation when both could theoretically be parents of the other is valid. So I did not change that. What I did (and hopefully it's not bad taste) was to add this workaround (never mind the nullptr checks, they are not relevant):
struct PhraseSmartPtrParseCreationComparer
{
bool operator()(const std::shared_ptr<Phrase>& a, const std::shared_ptr<Phrase>& b)
{
return *a < *b && !(*b < *a);
}
};

delete right child makes parent left pointer point to nullptr

thank you for checking my question, I currently have a really basic question on operator "delete", it seems it can automatically change the pointer value to nullptr. Let me give you a example for this:
template <typename T>
void Tree<T>::remove(const unsigned& index, TreeNode<T>*& tree)
{
if(tree == nullptr)
{
std::cerr << "remove: can't find target" << std::endl;
}
else if(index < tree->index)
{
remove(index, tree->left);
}
else if(index > tree->index)
{
remove(index, tree->right);
}
else if(index == tree->index)
{
if(tree->degree() == 2)
{
tree->index = findMin(tree->right)->index;
tree->value = findMin(tree->right)->value;
remove(tree->index, tree->right);
}
else
{
auto oldNode = tree;
tree = (tree->left != nullptr) ? tree->left: tree->right;
delete oldNode;
// oldNode = nullptr;
}
}
}
The code above is a classic searching tree remove algorithm. If the current tree only has two nodes which is root (with key equals to 3 for example) and right child (with key equals to 4 for example), so when I remove node 4, it will call remove twice and go to this line:
delete oldNode;
And this line will delete "oldNode", which should be 4 right now. As far as my knowledge, the delete operator will just free the memory address(the address is same to the value of oldNode), which means it tells OS this address is available again. So I suppose when I print out the value of root's right pointer(root->right), I should get a address. Actually when I print out I get 0. So my question when root->right changed?
Hope I explain my question clearly. That may be a stupid question, let me known if I make any confusion.
I think what you are seeing is that the use of a pointer after the delete is undefined behavior (until c++14).
For c++14: indirection through a pointer that became invalid in this manner and passing it to a deallocation function (double-delete) is undefined behavior. Any other use is implementation-defined.
Undefined behavior basically allows the implementation to do whatever it wants with the pointer after delete (even change its value).
It looks like your implementation sets the value of the pointer to nullptr in delete.
I found the answer, but before answer, let me clarify my question. The question is asking when a child is deleted, when and who set its parent right pointer to nullptr. The answer is this function has pointer reference as argument, when call this function, the passed parameter itself will be set to nullptr. For example, when pass in root->right, the root->right can be directly set to nullptr.

C++ int& operator[](string key) function using Linked List

For a comsci project I have been assigned recently, we have to implement our own version of the STL Map using a Linked List. In our operator[] overload function though we cannot figure out how to access and return the "int&".
This is what we have tried doing so far:
int& LLMap::operator[](string key){
//return this->myMap.searchforNodeAddress(key)->getPairValue();
return this->myMap.searchforNodeAddress(key)->getPairValueAddress();
}
And here are the implementations of the functions being called:
PairNode* PairLinkedList::searchforNodeAddress(string desiredKey){
PairNode* currNode = this->getPairHead();
while (currNode != NULL){
if (currNode->getPairKey() == desiredKey){
return currNode;
}
else{
currNode = currNode->getNext();
}
}
}
And
int PairNode::getPairValue(){
return this->value;
}
int* PairNode::getPairValueAddress(){
return &(this->value);
}
We have been trying to figure this out for quite some time now and are completely stumped, any pointers in the right direction or any assistance at all would be greatly appreciated. Thank you
You can either dereference the address...
return *myMap.searchforNodeAddress(key)->getPairValueAddress();
...or change getPairValue()...
int& PairNode::getPairValue() // now returns int&
Separately, the STL map (lowercase "m") is confined to history - you probably mean the C++ Standard Library std::map, and you can't strictly "implement" either with a linked list: they're necessarily balanced binary trees, given the Standard's performance guarantees. You can implement something with a std::map-like interface - minus those performance characteristics - over a linked list....
Also, prefer to pass string parameters that won't be modified inside a function using const std::string&: it avoids allocating extra memory and copying the text over, only to release it after the function exits.

C++ Segfault on clearing a binary search tree

I'm trying to eliminate any memory leaks in in my Binary Search tree that I'm making so I made a simple recursive delete method. This is the method that first causes
void BST::copy(const BST& other){
if (other.root != NULL){
Clear();
Which Clear(); will first thing call recursiveDelete(root);
void BST::recursiveDelete(BSTNode * head){
if (head == NULL)
return;
recursiveDelete(head->left);
recursiveDelete(head->right);
delete head;
}
This code will Segfault and I don't know why. When it calls the copy method it only has one node in it, so it says that head is not NULL as expected, but for some reason when it tries to reference head->left it segfaults.
Any help would be greatly appreciated. :)
EDIT: I didn't know this until one of the people who answered pointed it out, but in the copy constructor, one need to initialize everything to NULL first before attempting to copy. So my problem was solved (for the most part) when I changed my constructor to be the following
BST::BST(const BST & other) : root(NULL), size(0){
First comment: You should probably clear the object's data in the copy if the other object has data or not. If the other object is empty, then the copy should make this object empty too.
Second comment: I cannot tell from your question, but I expect that head->left or head->right was not set to NULL properly during construction. An invalid but not NULL value would explain getting past the if (head == NULL) check.
In BST::copy it seems odd that your check for NULL is not on the same object as Clear() is called on. There's not enough code given for me to say for sure, but shouldn't it be:
if (root != NULL) {
Clear();
}
Then you may also need to check other.root != NULL for subsequent code you haven't posted.
But as a general rule, Clear() should be made safe to call either way. You should simply check for NULL inside Clear().
Try after
delete head;
head = NULL;
and make sure left and right are set to NULL in the constructor.