I am implementing a simple Tree class to use in a parser, where I need to traverse the structure of the parse tree and build it up incrementally.
This is a stripped-down version of the class that demonstrates the issue (executable in this repl.it session):
template <typename T>
class Tree {
T val;
std::deque<Tree<T>> children;
public:
Tree() {}
Tree(T value) : val(value) {}
Tree(T value, std::deque<Tree<T>> children) : val(value), children(children) {}
std::deque<Tree<T>> getChildren() { return this->children; }
void appendChild(T value) {
this->children.push_back(value);
// this->children.emplace_back(value);
std::cout << "Appended child to node with value " << this->val << ".\n";
printChildren();
}
void printChildren() {
std::cout << "children for " << this << "(" << this->val << ")"
<< ": { ";
for (auto &child : this->children) {
std::cout << &child << "(" << child.val << ") ";
}
std::cout << "}\n";
}
};
For each node, the children are stored in a std::deque so children can be added to either end. In testing my class, I am finding that I cannot rely on the structure produced by building up the tree incrementally as opposed to all-at-once with an initializer list.
Here's some code to exercise the class and show what's happening:
std::cout << "Constructing Tree\n\n";
Tree<int> t(1);
t.appendChild(2);
t.getChildren()[0].appendChild(3);
std::cout << "\n\nPrinting tree from main\n\n";
t.printChildren();
t.getChildren()[0].printChildren();
This has the following output:
Constructing Tree
Appended child to node with value 1.
children for 0x7ffe9fd41820(1): { 0xb69080(2) }
Appended child to node with value 2.
children for 0xb694a0(2): { 0xb696b0(3) }
Printing tree from main
children for 0x7ffe9fd41820(1): { 0xb69080(2) }
children for 0xb698c0(2): { }
As you can see, the addresses of the node with the value 2 are different each time they are printed out. When it's first appended to the 1 node, it has the address 0xb69080. After it gets its own children, it has the address 0xb694a0. Then, when it's accessed from the main function, it has the address 0xb698c0.
Additionally, it seems that when it gets moved it somehow loses its children. The last line should show that the 2 node has a single child with value 3.
What's going on here?
I suppose your problem is here
std::deque<Tree<T>> getChildren() { return this->children; }
getChildren() return a copy of children.
Try with
std::deque<Tree<T>> & getChildren() { return this->children; }
// .................^
to return a reference to the internal children, if you want modify it using the returned value.
I mean: if getChildren() return a copy of children, with
t.getChildren()[0].appendChild(3);
you append a child with value 3 to the first element of the copy of children returned by getChildren().
This copy isn't saved so it's a temporary value that is destroyed immediately after, losing the 3 child appended.
Related
I'm writing a linked list, and using my main function to test it. Here's my code:
#include <iostream>
using namespace std;
class LinkedList {
int value;
LinkedList* next;
public:
LinkedList(int valueIn, LinkedList* nextIn) {
value = valueIn;
next = nextIn;
}
LinkedList(int valueIn) {
value = valueIn;
}
int getValue() {
return value;
}
void addNode(LinkedList* node) {
next = node;
}
LinkedList& getNext() {
return *next;
}
};
int main() {
cout << "starting..." << std::endl;
LinkedList list1(1);
LinkedList list2(2, &list1);
cout << list1.getValue() << " --> " << list1.getNext().getValue() << std::endl;
return 0;
}
I expect the output to be 1 --> 2, but I get 1 -->. As far as I understand, getNext() should return a reference to another list (list2 in this case), but something seems to be going wrong. My debugging efforts show me that list2 does have the correct value 2 when it's initialized, but when it's referenced for the final output, it doesn't seem to have anything for value. I can't for the life of me figure out why this is. Could someone help me to understand?
You are insertin list1(which is actually a node) to the end of list2, not the other way around, yet you call getNext() on list1. You should change the code in main to the below:
int main() {
std::cout << "starting..." << std::endl;
LinkedList list1(1);
LinkedList list2(2, &list1);
std::cout << list2.getValue() << " --> " << list2.getNext().getValue() << std::endl;
return 0;
}
Please note that there are a couple of other things which would be better to change:
Create a list class and a Node class woud make things clearer
Initializing the pointer to be NULL(or nullptr from C++11) in the LinkedList(int valueIn) constructor
Return the pointer to the node in getNext() rather than copy the node
You are not getting a blank value. Actually your program is crashing when you are trying to call list1.getNext().getValue() as getNext() is returning reference to a NULL.
You are doing the opposite of what you want to do.
Your list2 is pointing to list1 and list1 is pointing to NULL.
You should change your code with this:
LinkedList list2(2);
LinkedList list1(1, &list2);
cout << list1.getValue() << " --> " << list1.getNext().getValue() << std::endl;
So I'm a little new to Binary search trees and I'm trying to make a binary tree where each node is a vector of strings. then each insertion takes a string and only considers the first letters of that string. Based off the first 2 letters it will either append that string to an existing node where all string share the same 2 first letters or create a new node which will hold a vector of strings with all the same 2 first letters. Weird I know. It wasn't my idea.
I've tried narrowing down where the issue is by displaying the root at every insertion. And the insertions all seem to be working fine, but as soon as I want to display the nodes in Inorder, the root just seems to disappear, BUT almost like it's invisible. It's very evident base on the output. My guess is that it's null but I'm not sure. Sorry if this isn't the best way to ask. this is my first question here.
here's my code:
#include <iostream>
#include <string>
#include <vector>
//#include "stringSlicer.h"
using namespace std;
class BST
{
vector<string> data;
BST *left, *right;
public:
// Default constructor.
BST();
// Parameterized constructor.
BST(string);
// Insert function.
BST* Insert(BST*, string);
// Inorder traversal.
void Inorder(BST*);
// PreOrder Traversal.
void PreOrder(BST*);
// PostOrder Traversal
void PostOrder(BST*);
// string slicer
string strSlice(string);
// print vector
void printVector(vector<string>);
};
// Default Constructor definition.
BST ::BST()
: data(0)
, left(NULL)
, right(NULL)
{
}
// Parameterized Constructor definition.
BST ::BST(string value)
{
if(data.empty()){
data.push_back(strSlice(value));
}
data.push_back(value);
left = right = NULL;
}
// String slicing function definition
string BST ::strSlice(string word){
string word2 = "";
word2 += word[0];
word2 += word[1];
return word2;
}
// print vector function definition
void BST ::printVector(vector<string> dataVector){
for(int i = 0; i < dataVector.size(); i ++){
cout << dataVector.at(i) << " ";
cout << "end of this node";
}
}
// Insert function definition.
BST* BST ::Insert(BST* root, string value)
{
if (!root)
{
// Insert the first node, if root is NULL.
return new BST(value);
}
// Insert data.
if (strSlice(value).compare(root->data.at(0)) > 0)
{
// Insert right node data, if the 'value'
// to be inserted is greater than 'root' node data.
cout << value << " is being put in the right node " << value << " > " << root->data.at(0) << endl;
// Process right nodes.
root->right = Insert(root->right, value);
} else if (strSlice(value).compare(root->data.at(0)) == 0) {
cout << value << " is being put in the same node " << value << " = " << root->data.at(0) << endl;
root->data.push_back(value);
}
else
{
// Insert left node data, if the 'value'
// to be inserted is greater than 'root' node data.
cout << value << " is being put in the left node " << value << " < " << root->data.at(0) << endl;
// Process left nodes.
root->left = Insert(root->left, value);
}
// Return 'root' node, after insertion.
cout << "after insert root is " << root << endl;
return root;
}
// Inorder traversal function.
// This gives data in sorted order.
void BST ::Inorder(BST* root)
{
cout << "root is " << endl;
if (!root) {
return;
}
Inorder(root->left);
printVector(data);
cout << endl;
Inorder(root->right);
}
int main() {
const int size = 5;
string array [size] = {"hi","hillo","bye","chao","elo"};
BST b, *root = NULL;
cout << "root is " << root << endl;
root = b.Insert(root, array[0]);
for (int i = 1; i < size; i ++){
b.Insert(root, array[i]);
}
b.Inorder(root);
return 0;
}
here was the output:
root is 0
hillo is being put in the same node hillo = hi
after insert root is 0xeb7f10
bye is being put in the left node bye < hi
after insert root is 0xeb7f10
chao is being put in the left node chao < hi
chao is being put in the right node chao > by
after insert root is 0xeb7f30
after insert root is 0xeb7f10
elo is being put in the left node elo < hi
elo is being put in the right node elo > by
elo is being put in the right node elo > ch
after insert root is 0xeb7f88
after insert root is 0xeb7f30
after insert root is 0xeb7f10
root is
root is
root is
root is
root is
root is
root is
root is
root is
Problem:
Your nodes are not NULL, but you're not printing the data of any of them. With the statement printVector(data); you're printing just the data of the object b.
Solution:
Change printVector(data); to printVector(root->data);.
Additional information:
using namespace std; is considered a bad practice (More info here).
Instead of creating an object b just to use the methods of the class BST, make the methods static and pass the node as an argument. It's cleaner and will help to avoid confusions as this case.
Personally I would recommend you to use nullptr instead of NULL in C++.
Even if root is NULL, the Inorder method will execute "cout << "root is " << endl;" before returning and therefore outputting unnecesary lines.
You should use delete to free the data you store with new.
I recently run into this issues when implementing something. I have a custom Tree like struct which contains a value and a vector of children. When inserting child nodes, I expect them to come in a random order and I need to keep track of the last element inserted for some future operations. It turns out that if I save a pointer to the vector of the last node, after sorting the vector the pointer is still valid, but it now points to a completely different vector. Here is a minimal example:
#include <iostream>
#include <vector>
#include <algorithm>
struct Node {
int value;
std::vector<Node> nxt;
bool operator<(const Node& other) {
return value < other.value;
}
/* Having this custom swap function doesn't make a difference
*friend void swap(Node& lhs, Node& rhs) {
* std::swap(lhs.value, rhs.value);
* lhs.nxt.swap(rhs.nxt);
*}
*/
};
int main() {
Node node1;
node1.value = 1;
Node node2;
node2.value = 2;
Node node3;
node3.value = 3;
Node node4;
node4.value = 4;
std::vector<Node> container;
container.push_back(node2);
container.push_back(node1);
container.push_back(node4);
container.push_back(node3);
std::vector<Node>* node3_vec = &container.back().nxt;
node3_vec->push_back(node1);
std::cout << "Address of the vector: " << node3_vec << std::endl;
std::cout << "Size of the vector: " << node3_vec->size() << std::endl;
std::sort(container.begin(), container.end());
std::cout << "Address of the vector post sort: " << node3_vec << std::endl;
std::cout << "Size of the vector post sort: " << node3_vec->size() << std::endl;
//Inside the container
std::cout << "Value of the node inside the container: " << container[2].value << std::endl;
std::cout << "Address of the vector: " << &container[2].nxt << std::endl;
std::cout << "Size of the vector: " << container[2].nxt.size() << std::endl;
return 0;
}
I tried playing around with some custom std::swap implementations but I can't seem to change that behaviour. How can I make it so that after sorting, the pointer to the vector points to the same vector? At the moment, I perform an extra search after the sort to find the desired element.
Also could someone point me to some documentation that explains this behaviour?
After sort, the raw pointer node3_vec will still point the last Node in container. After the sort this will be a copy of node4 where a copy of node3 used to be before the sort.
Whenever you save the pointer, you're saving the address in memory of where the next vector is.
If you sort the container, the elements will move around and a different one will end up at that address.
Just like if you remember your friend lives at house number 4 on a street. Then you ask everybody on the street to move house so that they are in some order.
It's quite likely someone else will live at house number 4 that isn't your friend!
I'm not sure there's any documentation on this because it is expected!
I've been trying to write my own implementation of linked list, but the code segfaults when I try to access an the third element or anything after it. Adding elements doesn't segfault, but accessing does. I can't find the pointer error in my get() function.
Each node in the list stores data (of Template t) and a pointer leading to the next node. I have two functions for everything- one for the first element, and one for any subsequent elements. The get() function for the subsequent elements always segfaults. I have some debug messages in the function that spit out results I can't explain. For example, if I run a get() request for the second element, an then the third, the code doesn't segfault, but it does return clearly incorrect results. Debug messages I placed indicate the segfault occurs when the second element calls the function to check the third element, if it occurs at all. Try the code with and without the line cout << newList.get(2) << endl; and you'll get very different results.
One possible cause is the pointer storage- I have the get() function output the pointer of each element (except the first) as it cycles through, and compare them to the pointers outputted by the add() function, and and pointers for element 0 and 1 match, but 2 and beyond do not match, and I can't seem to figure out why that would be.
#include <iostream>
using namespace std;
template <class T> class myLinkedList{
T data;
myLinkedList<T> *next = NULL;
public:
myLinkedList(T input){
data = input;
}
void add(T input){
if(next == NULL){
myLinkedList<T> newItem(input);
next = &newItem;
cout << "adding to list, data is " << input << ", pointer is " << next << endl;
}else{
myLinkedList<T> nextEntry = *next;
nextEntry.add(input);
}
}
T getData(){
return data;
}
//the start of the get function, only used by the first entry in the list
T get(int entry){
int currentPosition = 0;
if(entry == currentPosition){
return getData();
}else{
//defrefrence the pointer anc check the next entry
myLinkedList<T> nextEntry = *next;
return nextEntry.get(entry, ++currentPosition);
}
}
private:
//this vesion is the hidden, private vesion only used by nodes other than the first one
//used to keep track of position in the list
T get(int entry, int currentPosition){
//cout << currentPosition << endl;
if(entry == currentPosition){
return data;
}else{
//derefrence the pointer and check the next entry
cout << next << endl;
myLinkedList<T> nextEntry = *next;
currentPosition++;
T output = nextEntry.get(entry, currentPosition);
return output;
}
}
};
int main(){
myLinkedList<int> newList(3);
newList.add(4);
newList.add(5);
newList.add(7);
newList.add(9);
cout << newList.get(2) << endl;
cout << newList.get(3) << endl;
return 0;
}
Results are clearly erroneous- program should spit oout two macthing sets of pointers, as well as the numbers 5 and 7 ( the list elements)
One of your main problems is here:
if(next == NULL){
myLinkedList<T> newItem(input); // <<<<<<<<<<<<<
next = &newItem;
cout << "adding to list, data is " << input << ", pointer is " << next << endl;
}
you allocate an item on stack inside the if scope. Then you make next to point to this item. But... lifetime of the item is bounded by this scope. As son as you exit the scope, this item does not exist any longer. You need to allocate it dynamically by 'new' or other methods.
I had a breakthrough! Following Serge's solution was helpful, but one more change was needed- rather than create a function reference in the else block of my add function,
eg
myLinkedList<T> nextEntry = *next;
nextEntry.add(input)
i needed to use the pointer directly, as in
next->add(input)
I didn't know my pointer/object syntax
Since void doesn't return anything, I don't know how to get a proper base case for a void function like the one I am trying to get.
struct TreeNode {
char value;
TreeNode *sibling;
TreeNode *child;
};
void serialize(std::ostream &out, TreeNode *root)
{
// If the root is nullptr, print "None"
if (root == nullptr)
out << "None" << "\n";
// Write out root's value
out << root->value << "\n";
// if there is no child
// write out "False"
// else
// write out "True"
// recursively call serialize on that child
if (root->child == nullptr)
out << false << "\n";
else
{
out << true << "\n";
serialize(out, root->child);
}
// recursively call serialize on the sibling
serialize(out, root->sibling);
}
Would it help if I rewrite serialize as a TreeNode type function instead, what would be my base case if I did that?
Note: this is one function from a project to create a tree-node data structure in c++.
In this code you are trying to call the serialize function recursively, but there is no termination condition specified. therefore as a result of which each time the recursive function-call the stack memory is occupied, eventually leading to stack overflow. Add the termination point like the return statement, it should work fine.
if (root == nullptr)
out << "None" << "\n";
return;