Binary Tree insertion:
#include "stdafx.h"
#include <iostream>
using namespace std;
struct TreeNode {
int value;
TreeNode* left;
TreeNode* right;
};
struct TreeType {
TreeNode* root;
void insert(TreeNode* tree, int item);
void insertItem(int value) {
insert(root, value);
}
};
void TreeType::insert(TreeNode* tree, int number) {
if (tree == NULL) {
tree = new TreeNode;
tree->left = NULL;
tree->right = NULL;
tree->value = number;
cout << "DONE";
} else if (number < tree->value) {
insert(tree->left, number);
} else {
insert(tree->right, number);
}
}
int main() {
TreeType* MyTree = new TreeType;
MyTree->insertItem(8);
return 0;
}
I am currently learning Data structures in C++ and this is the code that make insertion in binary trees.
After it is compiled, everything looks fine, however when i try to execute this program, it crashed.
Can anybody tell me where I am wrong?
In your tree constructor, you need to initialize the root pointer to NULL. It's not guaranteed to be initialized as NULL.
When you compile in linux, you can use gdb to show where the source of the segfault is coming from.
Some other notes:
You should assign the value back to root after you've allocated the new node. You're not doing that because you're missing one of the fundamentals of c++. That is, it's based on c. And the thing about c is that is strictly a "by-value" function/method calling paradigm. So all parameters in a function call are by value. When you pass in the memory address for root, you're actually copying the value of the pointer. Then, you're only updating the local value. You need to assign it back to root. If you'd like to learn that concept front to back, I highly recommend watching Jerry Cain's Programming Paradigms course at Stanford.
In your main function, you should try to keep the symbol names as lower_case instead of CamelCase. This helps differentiate variables from types (types should stay CamelCase).
In your TreeType::insert method, you should call the variable tree_node instead of tree. Doing so helps reflect the correct type and avoids confusion.
Whenever possible, try you use the this->root and this->insert notation. Not only will it correctly resolve if you accidentally create a locally scoped root variable, but it's also clearer to the reader where the data or method is defined. Great coding is about communication. It may only take 100-500ms less for the reader to understand where the symbol points to; however, the tiny savings that you can accumulate in avoiding ambiguities add up into a much clearer piece of software. Your future self (and your colleagues) will thank you. See http://msmvps.com/blogs/jon_skeet/archive/2013/09/21/career-and-skills-advice.aspx
Lastly, I can't overstate enough how important learning from the source is. If you're learning c or c++ for the first time, read http://www.amazon.com/The-Programming-Language-4th-Edition/dp/0321563840 and http://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628. It will save you hours, upon hours, upon hours. After having learned from the source, programming is also A LOT more enjoyable because you understand a good portion of the concepts. And, the truth is, things are more fun when you have a decent level of competence in them.
Try something like this-
struct Node {
int Data;
Node* Left;
Node* Right;
};
Node* Find( Node* node, int value )
{
if( node == NULL )
return NULL;
if( node->Data == value )
return node;
if( node->Data > value )
return Find( node->Left, value );
else
return Find( node->Right, value );
};
void Insert( Node* node, int value )
{
if( node == NULL ) {
node = new Node( value );
return;
}
if( node->Data > value )
Insert( node->Left, value );
else
Insert( node->Right, value );
};
In your TreeType constructor I think you should explicitly make root point to NULL.
TreeType::TreeType() {
root = NULL;
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I am currently learning c++ and in order to learn it I though I would implement a simple binary search tree class in order to get a hang of the concepts in c++. When implementing an add function I am receiving an interesting error in which it seems that the program does not recognize the node as null then crashes immediately after because the node should be null.
//Here is my add/insert function I created.
void BinarySearchTree::insert(double x){
if(root == NULL){
root = (struct TreeNode*) malloc(sizeof(struct TreeNode));
root->val = x;
return;
}
bool inserted = false;
struct TreeNode* curr = root;
while(curr != NULL && !inserted){
if(curr->val == x){
return;
}
if(x > curr->val){
if(curr->right == NULL){
curr->right = (struct TreeNode*) malloc(sizeof(struct TreeNode));
curr->right->val = x;
inserted = true;
} else {
curr = curr->right;
}
} else {
if(curr->left == NULL){
curr->left = (struct TreeNode*) malloc(sizeof(struct TreeNode));
curr->left->val = x;
inserted = true;
} else {
curr = curr->left;
}
}
}
}
//This is the TreeNode struct and the BinarSearchTree class in my header file if it helps
struct TreeNode{
double val;
struct TreeNode *right;
struct TreeNode *left;
};
class BinarySearchTree{
private:
struct TreeNode *root;
public:
void insert(double x);
};
Grand that you are learning C++!
Have no fear: you only need to wade a little deeper in, to have it all come right. The comments concur that there is a lot yet to learn, but trust us: it will be well worth the effort.
As some comments have indicated, the major issue in this example is that you have not properly initialized the variables you are using in your decision making process, which means that they contain garbage -- any old value whatsoever -- and not the zeroes that you intend them to. Initialization is just as vital in C; C++ only holds your hand more, making sure that you do -- iff you let it.
Indeed, later on you may look into the specialized pointer types that the standard library provides, but at first you may wish to just focus on learning the C++ way of doing things. You may use good 'ol malloc for allocation, but when you do you are actually just making unnecessary work for your self, and at the same time exposing your code to precisely the kind of problem you have encountered. C++ offers you operator new, which will do all the heavy lifting for you, especially when combined with a proper constructor function.
The simple C++ statement:
new TreeNode;
... does more than it appears to. First it allocates the memory required for the object -- most implementations using malloc under the hood. Then it calls the constructor function you have defined for the class, to initialize the data in the structure.
So first, in TreeNode you need something along these lines:
struct TreeNode {
double val;
TreeNode *right;
TreeNode *left;
// default constructor --'ctor'
TreeNode():right(null), left(null) {}
// useful ctor for your particular situation
TreeNode( double val ):TreeNode(), val(val) {}
};
And then, within your ::insert function:
// replace these lines...
if(root == NULL){
root = (struct TreeNode*) malloc(sizeof(struct TreeNode));
root->val = x;
return;
}
// with something like these...
if( !root ) {
root = new TreeNode; // root->val is garbage, for now
return;
}
// and replace the two branch creation sections...
if(curr->right == NULL){
curr->right = (struct TreeNode*) malloc(sizeof(struct TreeNode));
curr->right->val = x;
inserted = true;
}
// ... with something like this
if( !curr->right ) {
curr->right = new TreeNode( x );
break; // you are only setting 'inserted' here to end your loop
}
Constructors -- and the destructors that are their complements -- are powerful tools, and there are many nuances about them for you to discover.
Then, you can get down to the nitty-gritty of streamlining and optimizing your code.
First post but I have been a lurker for some time now :)
First, I am attempting to execute this skeleton code in Visual Studio 2015 and it should work as is according to my teacher so I am not sure what else could be wrong client side that may be causing that.
Finally, the overall issue here is I am not sure how to complete the remain commands. I understand the basic concepts of how the pointer to pointers work as well as the linked lists but not completely. My first issue is not helping either.
Any help would be greatly appreciated.
Thanks,
Several
#include <stdio.h>
#include <stdlib.h>
typedef struct _node {
int value;
struct _node *next;
} node;
void append(node**, int);
void prepend(node**, int);
void lprint(node*);
void destroy(node*);
int main(int argc, char **argv)
{
node *head = NULL;
append(&head, 1);
append(&head, 2);
append(&head, 3);
prepend(&head, 0);
prepend(&head, -1);
prepend(&head, -2);
lprint(head);
destroy(head);
return 0;
}
void append(node **head, int value)
{
if (*head == NULL)
{
*head = (node*)calloc(0, sizeof(node));
(**head).value = value;
}
else
{
node *temp;
for (temp = *head; (*temp).next != NULL; temp = temp->next);
(*temp).next = (node*)calloc(0, sizeof(node));
(*temp).(*next).value = value;
}
}
void prepend(node **head, int value)
{
}
void lprint(node *head)
{
node *temp;
for (temp = head; temp != NULL; temp = temp->next)
{
printf("%d ", temp->value);
}
printf("\n");
}
void destroy(node *head)
{
}
I was able to compile and run your code after changing this line:
(*temp).(*next).value = value;
to this:
(*temp).next->value = value;
When I ran it, it printed out:
1 2 3
... which is what I would expect, given that prepend() isn't implemented.
I could write an implementation of prepend() and post it here, but I don't want to risk doing your homework for you; instead I'll just describe how prepend() ought to work.
For the case where head is NULL, prepend() can do the exact same thing that append() does: allocate a node and set head to point to that node.
For the other case, where head is non-NULL (because the list is non-empty), it's pretty easy too -- even easier than the append() implementation. All you need to do is allocate the new node, set the new node's next pointer to point to the existing head node (*head), and then set the head pointer (*head) to point to the new node.
The destroy() function can work with a loop very similar to the one in your lprint() function, with one caveat -- you have to grab the next pointer out of a given node (and store it into a local variable) before you free() the node, because if you free the node first and then try to read the next-pointer from the already-freed node, you are reading already-freed memory which is a no-no (undefined behavior) and will cause bad things (tm) to happen.
I started writing a binary tree and then came up with this example and I'm not sure what's going on. So here's the code:
#include<iostream>
using namespace std;
struct Node
{
Node *left, *right;
int key;
Node()
{
left = NULL;
right = NULL;
key = 0;
}
Node(int key)
{
left = NULL;
right = NULL;
key = key;
}
};
struct Tree
{
Node* root;
void Add(int k)
{
Node* t;
t->key = k;
root->left = t;
}
Tree(int key)
{
this->root = new Node(key);
}
};
int main()
{
Tree* tree = new Tree(5);
tree->Add(4);
cout<<tree->root->left->key;
return 0;
}
Add function Add in Tree is whats confuses me. So, there is a pointer to Node object, but new keyword is not used and it appears to me that anyway there is memory allocated in the heap because I can reach the object. Shouldn't go out of scope and be destroyed? And why I can reach that object and print out its key?
Probably that memory belongs to your program and nothing bad seems to happen because you are using so little memory. If you use more memory, some object will own that unallocated space and expect it to remain unmodified. Then this code will start giving you problems.
You are "dereferencing an uninitilized pointer". There are questions relating to this here and here, for instance. Your compiler may blow up if you do this, or it may not: the behaviour is undefined. Anything might happen, including the appearance that things are working.
Use new, like you should.
This line …
Node* t;
… is like:
Node* t = random_address;
It means that the next line …
t->key = k;
… is able to corrupt interesting memory locations.
The code is invalid. In this function
void Add(int k)
{
Node* t;
t->key = k;
root->left = t;
}
local variable t is not initialized and has indeterminate value. So the execution of the statement
t->key = k;
results in undefined behaviour.
You correctly pointed to that there must be used operator new. For example
Node* t = new Node( k );
Nevertheless even in this case the function is invalid because it has to check whether the new key is less of greater than the key of root. Depending on the condition there should be either
root->left = t;
or
root->right = t;
I recently asked a question about the proper way to go about creating a class in C++11. I practiced by building a Tree class, and I received some wonderful advice. However, I'm having a little trouble understanding why my code is not working.
In particular, I'm having trouble understanding why my insert method is not working correctly.
template<typename T>
class Tree {
private:
struct Node {
T data;
Node* p_left;
Node* p_right;
};
Node* newNode(T data) { return new Node {data, nullptr, nullptr}; }
Node* root_;
//Other functions, etc... (copy constructor and copy assignment operator)
public:
void insert(T const data) {
Node*& root = root_;
while (root != nullptr) {
root = (data <= root->data ? root->p_left : root->p_right);
}
root = newNode(data);
}
Tree(): root_(nullptr) {}
//Other constructors, functions, etc...
};
If I create a new Tree object, and then populate that object with some data, the object only retains the last piece of inserted data. I know I'm messing up somewhere because of my pointer reference, but I can't figure out where. Any tips in the right direction would be much appreciated.
Two issues... first, on the very first insert into the Tree, you are not updating the root:
if (root == nullptr) { return newNode(data); }
In fact, you're returning the new node, even though the insert function returns void. If you remove that line entirely, the code should work. If root starts out as nullptr, the while loop will be skipped and root will be updated to a new node.
Second issue is that you're using a reference to a Node pointer, which means that you're moving your Tree's root_ every time you create a new node. That's not necessarily a good idea. Personally, I'd write it like this:
void insert(T const data) {
Node** proot = &root_;
while (*proot != nullptr) {
proot = (data <= (*proot)->data ? (*proot)->p_left : (*proot)->p_right);
}
*proot = newNode(data);
}
I'm trying to create a function that inserts a key struct into a tree. The function sets the root correctly, but does not set the branches when called again with another key. Here is the code:
tree.h:
class tree{
key *tree_root;
public:
tree();
//Constructor
void treedestroy(key *root);
//Tree destructor helper
~tree();
//Destructor
void insert(key* root, key *newkey, int disc);
};
insert function from tree class:
void tree::insert(key *root, key *newkey, int disc){
if (root == NULL){
root = newkey;
return;
}
if (newkey->cord[disc] <= root->cord[disc])
insert(root->left, newkey, (disc+1)%4);
else if (newkey->cord[disc] > root->cord[disc])
insert(root->right, newkey, (disc+1)%4);
}
I'm a little inexperienced with C++ pointers and was wondering how I could fix this code so that it will fill the tree correctly?
I'm not entirely sure about your approach here, but to help get you on your feet, it would help to use a function signature of:
void insert(key*& root, key *newkey, int disc);
This passes the root pointer by reference, which means that changes made inside the function will "stick" to the variable you passed in.
Your function as it stands modifies a function-local variable, without those changes propagating.
This article is a balanced and quick read on passing by reference (I can't say if it's the best - it was just the first decent one I found)
If on the first call newkey is null, root will stay null. Make sure method call is correct.
I would put an else rather than an else if. If it is a binary tree it is either equal, greater, or less than.
Does it get into Insert_helper or not? Why did you not include it, seems important? I would guess it is getting at least that far.
root = newKey;
This do not modify actual root. It just modify function argument which is a copy of pointer you specify when call intsert function.
Correct version would looks something like this:
private:
void tree::insert_helper( key **root, key *newkey, int disc ) {
if ( (*root) == NULL ) {
*root = key;
} else if (newkey->cord[disc] <= root->cord[disc]) {
insert_helper( &((*root)->left), newkey, (disc+1)%4 );
} else {
insert_helper( &((*root)->right), newkey, (disc+1)%4);
}
}
public:
void tree::insert( key *newKey, int disc ) {
insert_helper( &tree_root, newkey, disc );
}
Also you have to be sure that 'key' constructol set NULL for left and right. And tree constructor should set NULL for tree_root