I have the following function for deleting the min element:
int BinaryTree::delete_min_helper(TreeNode *node){
while(node->left != NULL){
node = node->left;
}
if(node == root){
return 1;
}
delete node;
node = NULL;
return 0;
}
I always pass a pointer to the root node to this function. I've tried this and a few variations of it, but it always seems to delete the the wrong thing or not delete anything at all. Any ideas why?
Here is a compilable example:
#ifndef _TREE_H_
#define _TREE_H_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct TreeNode {
int val;
char *str;
TreeNode *left;
TreeNode *right;
};
class BinaryTree {
public:
BinaryTree();
~BinaryTree();
int insert_node(unsigned int val, char *str);
TreeNode *find_min();
int delete_min();
void print();
private:
int insert_node_helper(TreeNode *&node, unsigned int val, char *str);
int delete_min_helper(TreeNode *node);
void print_helper(TreeNode *node);
TreeNode *root;
};
#endif
BinaryTree::BinaryTree(){
this->root = NULL;
}
BinaryTree::~BinaryTree(){
}
int BinaryTree::insert_node(unsigned int val, char *str){
return insert_node_helper(this->root, val, str);
}
int BinaryTree::insert_node_helper(TreeNode *&node, unsigned int val, char *str){
if(node == NULL){
node = new TreeNode;
node->val = val;
node->str = strdup(str);
node->left = NULL;
node->right = NULL;
if(node != NULL){
return 0;
}else{
puts("inserted null node");
return 1;
}
}else if(val <= node->val){
return insert_node_helper(node->left, val, str);
}else if(val > node->val){
return insert_node_helper(node->right, val, str);
}
return 1;
}
void BinaryTree::print(){
print_helper(this->root);
}
void BinaryTree::print_helper(TreeNode *node){
if(node != NULL){
print_helper(node->right);
printf("%d occurrences of \"%s\"\n", node->val, node->str);
print_helper(node->left);
}
}
TreeNode *BinaryTree::find_min(){
TreeNode *temp = this->root;
while(temp->left != NULL){
temp = temp->left;
}
return temp;
}
int BinaryTree::delete_min(){
return delete_min_helper(root);
}
int BinaryTree::delete_min_helper(TreeNode *node){
while(node->left != NULL){
node = node->left;
}
if(node == root){
puts("attempted to delete root");
return 1;
}
delete node;
node = NULL;
return 0;
}
#include <time.h>
int main(){
BinaryTree bt;
srand(time(NULL));
for(int i = 0; i < 10; i++){
bt.insert_node(rand() % 20, "test");
}
bt.print();
printf("min val = %d\n", bt.find_min()->val);
puts("#################################");
bt.delete_min();
bt.print();
printf("min val = %d\n", bt.find_min()->val);
return 0;
}
You are taking a reference to a pointer as argument (TreeNode *&node), and then modify it. So if you pass the root of your tree to this method, you will eventually set the root to NULL.
Additionally this actually deletes absolutely nothing, because you call delete after setting the pointer to NULL, and delete called on a null-pointer does nothing.
Update after edit of original post:
I assume root is a member of BinaryTree. It would be easier to help you if you posted a Short, Self Contained, Compilable Example.
You're still taking root as a reference. Thus node == root will always be true, and you'll never reach your delete statement. So actually you're not deleting anything, but you're changing your root to be the left-most child node.
Try changing your method signature to
int BinaryTree::delete_min_helper(TreeNode *node){
that is, without the ampersand (&).
After edit:
Ok, now I've run your program, and found your second problem. In the line
node = NULL;
You're changing the value of a local pointer, not of the left-pointer in the parent node. This means than when you traverse the tree the next time, you will access a pointer to the memory you just deleted. That triggers undefined behaviour. To get around this you should set the left-pointer of your parent node to 0 when you delete your leaf-node.
Related
I am working on a BST and when I print out the elements in any order, I get a random '0' appended to it, but I cannot find where its coming from.
I followed the pseudo code thats present in Introduction to algorithms by Cormen and have also looked at Geeks for Geeks but I have no luck getting rid of that 0.
#include <iostream>
using namespace std;
class Node {
public:
int data;
Node* LeftChild;
Node* RightChild;
Node(int data){
this->data = data;
this->LeftChild = NULL;
this->RightChild = NULL;
}
//pointers of the class
};
class BST {
private:
Node* root;
public:
BST(){ ///creating an empty tree in Constant Time
root = new Node(NULL);
}
Node* getRoot(){ return this->root; };
int i =0;
void printTree(Node *root)
{
if (root == NULL)
return;
else {
printTree(root->LeftChild);
cout << root->data << " ";
printTree(root->RightChild);
}
}
Node* InsertNode(Node *root,int data)
{
Node *z = new Node(data);
Node *y = new Node(NULL);
Node *x = this->root;
//if(x->data < z->data){
// x = z;
//return x;
//}
while(x!= NULL){
y = x;
if(data < x->data){
x = x->LeftChild;
}
else{
x = x->RightChild;
}
}
if(y== NULL) y= z;
else if(data < y->data){
y->LeftChild = z;
}
else{
y->RightChild =z;
}
return y;
/*
if(this->root->data== NULL){
this->root =z;
return root;
}
else{
this->root =y;
}
*/
//this->root = z;
//return root;
}
bool FindNode(Node *root,int data);
int Largest(Node *root){
return root->data;
}
};
int main()
{
BST myBst;
Node * root = (myBst.getRoot());
root = myBst.InsertNode(root, 24);
myBst.InsertNode(root, 60);
myBst.InsertNode(root, 55);
myBst.InsertNode(root, 32);
myBst.printTree(root);
return 0;
}
Here is the output:
0, 24,32,55,60
The constructor does not make a sense
BST(){ ///creating an empty tree in Constant Time
root = new Node(NULL);
}
There is created a dummy node with initialization of the data member data with NULL.
What you need is just to write
BST() : root( nullptr ) { ///creating an empty tree in Constant Time
}
The function InsertNode must have only one parameter instead of two parameters as you wrote
Node* InsertNode(Node *root,int data){
The pointer root is the data member of the class. So there is no need to pass it to the function. Otherwise the function should be declared as a static member function of the class (that nevertheless does not make a great sense).
That is the function should be declared like
void InsertNode( int data ){
Also the function has at least a memory leak
Node* InsertNode(Node *root,int data){
Node *z = new Node(data);
Node *y = new Node(NULL);
Node *x = this->root;
while(x!= NULL){
y = x;
//...
The function can be written for example the following way
void InsertNode( int data )
{
Node *new_node = Node( data );
Node **current = &root;
while ( *current != nullptr )
{
if ( data < ( *current )->data )
{
current = &( *current )->LeftChild;
}
else
{
current = &( *current )->RightChild;
}
}
*current = new_node;
}
I am writing a code to return data of a node in BST based on id.
below is my node class:
struct Node{
int id;
string data;
Node *left;
Node *right;
Node();
};
below is my node constructor: I defined id and data in addNode function
Node :: Node(){
this->left = nullptr;
this->right = nullptr;
}
below is my BST class:
class BST{
private:
Node * root = nullptr;
void setRoot(Node *);
Node* getRoot();
public:
Node *addNode(BST *, int);//helper function
Node *addNode(Node *,int);
string getEntry(BST*,int);//helper function
string getEntry(Node*,int);
}
below is my helper functions:
Node *BST::addNode(BST *bst, int val){
addNode(bst->getRoot(),val);
}
string BST::getEntry(BST* bst,int id){
getEntry(bst->getRoot(),id);
}
below is my addNode class:
Node* BST::addNode(Node* root, int val) {
Node *newNode = new Node();
newNode->id = val;
newNode->data = "Number " + to_string(val);
if (root == nullptr) {
if (getRoot() == nullptr){
setRoot(newNode);
}
setCount(getCount()+1);
return newNode;
}
if (root->id > val) {
root->left = addNode(root->left, val);
} else {
root->right = addNode(root->right, val);
}
return root;
}
below is my getEntry class:
string BST::getEntry(Node *base,int id) {
if (base == nullptr){
return "";
}
if (base->id == id){
cout<<base->data<<endl;
return base->data;
}
getEntry(base->left,id);
getEntry(base->right,id);
}
below are the nodes I passed in from main:
int main(){
BST *newBst = new BST();
newBst->addNode(newBst,1);
newBst->addNode(newBst,2);
newBst->addNode(newBst,3);
newBst->addNode(newBst,2);
newBst->addNode(newBst,3);
newBst->addNode(newBst,5);
newBst->addNode(newBst,7);
newBst->addNode(newBst,10);
cout<<newBst->getEntry(newBst,5)<<endl;
return 0;
}
The code would compile but does not return anything, I tried to debug, at the "return base->data statement", there is an error "can not access memory at address 0xc8". What causes the problem and what can I do about it?
this is the warning I got when I debug the code.
if (base->id != id){
getEntry(base->left,id);
getEntry(base->right,id);
}
As you are using a sorted tree, you know which of the right or left node you need to have a look at. Also, you need to return something:
if (base->id > val){
return getEntry(base->left,id);
}
return getEntry(base->right,id);
But the design with addNode is very bad, you shouldn't have to pass the root twice!
In the code bellow, I'm searching for ways to optimize the speed of insertions when dealing with Millions of inserts at the time. the code runs fine but is very slow when performing large amounts of inserts. I already tried some ideas but is always slow. I guess the solution is to use Multi-threading to perform the inserts, and use a global variable "truct Node* nodeObj".
I have very few/no experience with Multi-threading and Synchronization in C, C++, If you could give me an example based on the code bellow I would be very appreciated.
Any additional Ideas are welcome.
The rules are:
1- In the end (after insert all numbers) the linked list must be ordered, meaning that each Node->data starts at lowest number up to the Largest number.
2 - The caller is using a for loop, this for loop cannot be started inside the Insert function.
3 - Any of the code, caller or insert function can be optimized as long as the insert function does not automatically add inserts by it self, that's the caller s job.
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct Node {
int data;
struct Node* nextAddr;
};
struct Node* Insert(Node* p, int data);
void Print(struct Node* p);
void RevPrint(struct Node* p);
int main() {
struct Node* nodeObj = NULL;
printf("---------------------------------\n"
"Insert()\n---------------------------------\n");
for (int i = 1; i < 1000000; i++) {
nodeObj = Insert(nodeObj, i);
printf(" %d", i);
}
printf("\n---------------------------------\n"
"Print()\n---------------------------------\n");
Print(nodeObj);
printf("---------------------------------\n"
"RevPrint()\n---------------------------------");
RevPrint(nodeObj);
printf("\nPress any key to continue...");
_getch();
}
struct Node* Insert(Node* _pToNode, int _nbr)
{
Node *newValue = (struct Node*)malloc(sizeof(struct Node));
newValue->data = _nbr;
newValue->nextAddr = NULL;
if (_pToNode == NULL) _pToNode = newValue;
else {
Node* pLocal = _pToNode;
while (pLocal->nextAddr != NULL) {
pLocal = pLocal->nextAddr;
}
pLocal->nextAddr = newValue;
}
return _pToNode;
}
void Print(struct Node* p) {
if (p == NULL) {
printf("\n");
return;
}
printf(" %d", p->data);
Print(p->nextAddr);
}
void RevPrint(struct Node* p) {
if (p == NULL) {
printf("\n");
return;
}
RevPrint(p->nextAddr);
printf(" %d", p->data);
}
Thank you.
Linked lists are unwelcome on modern machines. The L1 and L2 caches love vectors and arrays. They utterly despise linked lists. Use a std::vector, not a linked list, (and certainly not a hand-rolled linked list). Try to .reserve() enough memory in the vector to accommodate all the new stuff. Just push everything onto the back of the vector, and then sort the vector using parallel execution. C++17 can do that painlessly. std::stable_sort parallels quite nicely.
Caveat: This works only for tail insertion/append:
int
main()
{
struct Node* nodeObj = NULL;
struct Node* nodeTail = NULL;
// your other stuff ...
for (int i = 1; i < 1000000; i++) {
nodeObj = Insert(nodeObj, &nodeTail, i);
printf(" %d", i);
}
// your other stuff ...
}
struct Node* Insert(Node* _pToNode, Node **tail,int _nbr)
{
Node *newValue = (struct Node*)malloc(sizeof(struct Node));
newValue->data = _nbr;
newValue->nextAddr = NULL;
if (_pToNode == NULL) {
_pToNode = newValue;
}
else {
(*tail)->nextAddr = newValue;
}
*tail = newValue;
return _pToNode;
}
You could clean this up with a "list" struct that has both the head and tail in it (vs. the separate args)
UPDATE:
Very cool/smart, but unfortunately is still slow...
malloc et. al. can be slow for a large number of small allocations. One way to speed things up is to use a subpool of allocations [as WeatherVane suggested].
As I mentioned, adding a "list" struct can make things tidier and I used it in two places. Once you commit to that, you can add other things besides head/tail, such as count. Also, it makes it easier to convert from a singly-linked list to a doubly-linked in the future, if you so choose.
Side note: With a doubly-linked list, insertions are [slightly] more complex, but insertions in the middle of the list are much faster because you don't have to traverse the list to find the previous pointer (e.g. the node would have a prev pointer in it). Also, RevPrintList would become as simple as PrintList.
Note that [on my system], the reverse print ran out of stack space [and segfaulted], so I recoded the print functions to not be recursive.
Here's a cleaned up version that should do the insertions faster because of the reduced number of individual malloc calls.
Side note: I didn't add the requisite checks for malloc, etc. returning null.
#include <stdio.h>
#include <stdlib.h>
//#include <conio.h>
typedef struct Node_ {
int data;
struct Node_* next;
} Node;
typedef struct List_ {
int count;
Node* head;
Node* tail;
} List;
Node* NewNode(void);
Node* Insert(List* p, int data);
void Print(Node* p);
void PrintList(List *list);
void RevPrint(Node* p);
void RevPrintList(List *list);
List freelist;
int
main()
{
List nodelist = { 0, NULL, NULL };
printf("---------------------------------\n"
"Insert()\n---------------------------------\n");
for (int i = 1; i < 1000000; i++) {
Insert(&nodelist, i);
#if 0
printf(" %d", i);
#endif
}
printf("\n---------------------------------\n"
"Print()\n---------------------------------\n");
#if 0
Print(nodelist.head);
#else
PrintList(&nodelist);
#endif
printf("---------------------------------\n"
"RevPrint()\n---------------------------------");
#if 0
RevPrint(nodelist.head);
#else
RevPrintList(&nodelist);
#endif
printf("\nPress any key to continue...");
#if 0
_getch();
#else
getchar();
#endif
}
Node*
NewNode(void)
{
Node *node;
// NOTE: adjust the count setup (e.g. 1000) to what ever value you want
if (freelist.count <= 0) {
freelist.count = 1000;
freelist.head = calloc(freelist.count,sizeof(Node));
}
node = freelist.head++;
freelist.count -= 1;
return node;
}
Node*
Insert(List* list,int _nbr)
{
Node *node = NewNode();
node->data = _nbr;
node->next = NULL;
if (list->head == NULL) {
list->head = node;
}
else {
list->tail->next = node;
}
list->tail = node;
list->count += 1;
return node;
}
void
Print(Node* p)
{
if (p == NULL) {
printf("\n");
return;
}
printf(" %d", p->data);
Print(p->next);
}
void
PrintList(List* list)
{
Node *node;
for (node = list->head; node != NULL; node = node->next)
printf(" %d", node->data);
printf("\n");
}
void
RevPrint(Node* p)
{
if (p == NULL) {
printf("\n");
return;
}
RevPrint(p->next);
printf(" %d", p->data);
}
void
RevPrintList(List *list)
{
Node **rlist = malloc(sizeof(Node**) * list->count);
Node *node;
int ridx;
ridx = list->count - 1;
for (node = list->head; node != NULL; node = node->next, --ridx)
rlist[ridx] = node;
for (ridx = 0; ridx < list->count; ++ridx) {
node = rlist[ridx];
printf(" %d",node->data);
}
printf("\n");
free(rlist);
}
UPDATE #2:
you could make freeList a list of lists (using a different "node" struct which would have a pointer to list instead of a number), so that memory could be released when the program is done.
Here is a modified version that does that:
#include <stdio.h>
#include <stdlib.h>
//#include <conio.h>
typedef struct Node_ {
int data;
struct Node_* next;
} Node;
typedef struct List_ {
int count;
Node* head;
Node* tail;
} List;
typedef struct Freelist_ {
int count;
Node* head;
Node* tail;
Node* avail;
} FreeList;
Node* NewNode(void);
Node* Insert(List* p, int data);
void Print(Node* p);
void PrintList(List *list);
void RevPrint(Node* p);
void RevPrintList(List *list);
void FreeAll(void);
FreeList freelist = { 0 };
int
main()
{
List nodelist = { 0, NULL, NULL };
printf("---------------------------------\n"
"Insert()\n---------------------------------\n");
for (int i = 1; i < 1000000; i++) {
Insert(&nodelist, i);
// this printf will radically slow things down
#if 0
printf(" %d", i);
#endif
}
printf("\n---------------------------------\n"
"Print()\n---------------------------------\n");
#if 0
Print(nodelist.head);
#else
PrintList(&nodelist);
#endif
printf("---------------------------------\n"
"RevPrint()\n---------------------------------");
#if 0
RevPrint(nodelist.head);
#else
RevPrintList(&nodelist);
#endif
// release all nodes back to the malloc free pool
FreeAll();
printf("\nPress any key to continue...");
#if 0
_getch();
#else
getchar();
#endif
}
Node*
NewNode(void)
{
Node *node;
// NOTE: adjust the count setup (e.g. 1000) to what ever value you want
if (freelist.count <= 0) {
freelist.count = 1000;
node = calloc(freelist.count,sizeof(Node));
// maintain linked list of nodes that are at the _start_ of a
// malloc area/arena
if (freelist.head == NULL)
freelist.head = node;
else
freelist.tail->next = node;
freelist.tail = node;
// burn the first node as a placeholder
freelist.avail = node + 1;
freelist.count -= 1;
}
node = freelist.avail++;
freelist.count -= 1;
return node;
}
void
FreeAll(void)
{
Node* node;
Node* next;
for (node = freelist.head; node != NULL; node = next) {
next = node->next;
free(node);
}
}
Node*
Insert(List* list,int _nbr)
{
Node *node = NewNode();
node->data = _nbr;
node->next = NULL;
if (list->head == NULL) {
list->head = node;
}
else {
list->tail->next = node;
}
list->tail = node;
list->count += 1;
return node;
}
void
Print(Node* p)
{
if (p == NULL) {
printf("\n");
return;
}
printf(" %d", p->data);
Print(p->next);
}
void
PrintList(List* list)
{
Node *node;
for (node = list->head; node != NULL; node = node->next)
printf(" %d", node->data);
printf("\n");
}
void
RevPrint(Node* p)
{
if (p == NULL) {
printf("\n");
return;
}
RevPrint(p->next);
printf(" %d", p->data);
}
void
RevPrintList(List *list)
{
Node **rlist = malloc(sizeof(Node**) * (list->count + 1));
Node *node;
int ridx;
ridx = list->count - 1;
for (node = list->head; node != NULL; node = node->next, --ridx)
rlist[ridx] = node;
for (ridx = 0; ridx < list->count; ++ridx) {
node = rlist[ridx];
printf(" %d",node->data);
}
printf("\n");
free(rlist);
}
UPDATE #3:
Here's a version that adds reuse of nodes by adding a reuse member to FreeList and a FreeOne function that can be called when a node is deleted.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <conio.h>
typedef struct Node_ {
int data;
struct Node_ *next;
} Node;
typedef struct List_ {
int count;
Node *head;
Node *tail;
} List;
typedef struct Freelist_ {
int count;
Node *head;
Node *tail;
Node *avail;
Node *reuse;
} FreeList;
Node *NewNode(void);
Node *Insert(List *p,int data);
void Print(Node *p);
void PrintList(List *list);
void RevPrint(Node *p);
void RevPrintList(List *list);
void FreeOne(Node *p);
void FreeAll(void);
FreeList freelist = { 0 };
int
main()
{
List nodelist = { 0, NULL, NULL };
printf("---------------------------------\n" "Insert()\n---------------------------------\n");
for (int i = 1; i < 1000000; i++) {
Insert(&nodelist,i);
// this printf will radically slow things down
#if 0
printf(" %d",i);
#endif
}
printf("\n---------------------------------\n" "Print()\n---------------------------------\n");
#if 0
Print(nodelist.head);
#else
PrintList(&nodelist);
#endif
printf("---------------------------------\n" "RevPrint()\n---------------------------------");
#if 0
RevPrint(nodelist.head);
#else
RevPrintList(&nodelist);
#endif
// release all nodes back to the malloc free pool
FreeAll();
printf("\nPress any key to continue...");
#if 0
_getch();
#else
getchar();
#endif
}
Node *
NewNode(void)
{
FreeList *list;
Node *node;
list = &freelist;
do {
// try to reuse a node that has been released by FreeOne
node = list->reuse;
if (node != NULL) {
list->reuse = node->next;
node->next = NULL;
break;
}
// NOTE: adjust the count setup (e.g. 1000) to what ever value you want
if (list->count <= 0) {
list->count = 1000;
node = calloc(list->count,sizeof(Node));
// maintain linked list of nodes that are at the _start_ of a
// malloc area/arena
if (list->head == NULL)
list->head = node;
else
list->tail->next = node;
list->tail = node;
// burn the first node as a placeholder
list->avail = node + 1;
list->count -= 1;
}
// grab one from the current allocation array
node = list->avail++;
list->count -= 1;
} while (0);
return node;
}
void
FreeOne(Node *node)
{
FreeList *list;
list = &freelist;
// push this node onto the front of the reuse list (i.e. it's fast)
node->next = list->reuse;
list->reuse = node;
}
void
FreeAll(void)
{
Node *node;
Node *next;
for (node = freelist.head; node != NULL; node = next) {
next = node->next;
free(node);
}
memset(&freelist,0,sizeof(FreeList));
}
Node *
Insert(List *list,int _nbr)
{
Node *node = NewNode();
node->data = _nbr;
node->next = NULL;
if (list->head == NULL) {
list->head = node;
}
else {
list->tail->next = node;
}
list->tail = node;
list->count += 1;
return node;
}
void
Print(Node *p)
{
if (p == NULL) {
printf("\n");
return;
}
printf(" %d",p->data);
Print(p->next);
}
void
PrintList(List *list)
{
Node *node;
for (node = list->head; node != NULL; node = node->next)
printf(" %d",node->data);
printf("\n");
}
void
RevPrint(Node *p)
{
if (p == NULL) {
printf("\n");
return;
}
RevPrint(p->next);
printf(" %d",p->data);
}
void
RevPrintList(List *list)
{
Node **rlist = malloc(sizeof(Node **) * (list->count + 1));
Node *node;
int ridx;
ridx = list->count - 1;
for (node = list->head; node != NULL; node = node->next, --ridx)
rlist[ridx] = node;
for (ridx = 0; ridx < list->count; ++ridx) {
node = rlist[ridx];
printf(" %d",node->data);
}
printf("\n");
free(rlist);
}
I assume that the order of the elements that you mention is ordered by comparison. I.e. A < B => A is before B in the container.
If that assumption holds the best you can hope for is a log(N) insertion of each element. So if you want to insert an element among those millions of elements you should expect to do about 20+ comparisons. If the millions you want to insert have an order you can do better.
Now, a pointer based structure is never the answer to a fast data structure.
std::vector should be your go to.
To read this fast-insertion structure with a for statement you'd need a very peculiar iterator or you need to rearrange the inserted stuff in to another vector.
In conclusion, you want to insert stuff into a simple binary heap.
Multithreading would not make this faster unless you can devise a merge like scheme to your inserts.
Can anyone explain to me why the delete_min() function does not work? I have been trying to figure this out for hours now.
I have tried different variations of the function, but it seems to either delete the wrong thing, only alter data and not delete it, or delete the wrong thing. Also, the insertion function doesn't always work properly after delete_min() is called, so I'm sure this has something to do with pointers.
Here is the function:
int BinaryTree::delete_min_helper(TreeNode *node){
while(node->left != NULL){
node = node->left;
}
if(node == root){
puts("attempted to delete root");
return 1;
}
delete node;
node = NULL;
return 0;
}
And here is a compilable example:
#ifndef _TREE_H_
#define _TREE_H_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct TreeNode {
int val;
char *str;
TreeNode *left;
TreeNode *right;
};
class BinaryTree {
public:
BinaryTree();
~BinaryTree();
int insert_node(unsigned int val, char *str);
TreeNode *find_min();
int delete_min();
void print();
private:
int insert_node_helper(TreeNode *&node, unsigned int val, char *str);
int delete_min_helper(TreeNode *node);
void print_helper(TreeNode *node);
TreeNode *root;
};
#endif
BinaryTree::BinaryTree(){
this->root = NULL;
}
BinaryTree::~BinaryTree(){
}
int BinaryTree::insert_node(unsigned int val, char *str){
return insert_node_helper(this->root, val, str);
}
int BinaryTree::insert_node_helper(TreeNode *&node, unsigned int val, char *str){
if(node == NULL){
node = new TreeNode;
node->val = val;
node->str = strdup(str);
node->left = NULL;
node->right = NULL;
if(node != NULL){
return 0;
}else{
puts("inserted null node");
return 1;
}
}else if(val <= node->val){
return insert_node_helper(node->left, val, str);
}else if(val > node->val){
return insert_node_helper(node->right, val, str);
}
return 1;
}
void BinaryTree::print(){
print_helper(this->root);
}
void BinaryTree::print_helper(TreeNode *node){
if(node != NULL){
print_helper(node->right);
printf("%d occurrences of \"%s\"\n", node->val, node->str);
print_helper(node->left);
}
}
TreeNode *BinaryTree::find_min(){
TreeNode *temp = this->root;
while(temp->left != NULL){
temp = temp->left;
}
return temp;
}
int BinaryTree::delete_min(){
return delete_min_helper(root);
}
int BinaryTree::delete_min_helper(TreeNode *node){
while(node->left != NULL){
node = node->left;
}
if(node == root){
puts("attempted to delete root");
return 1;
}
delete node;
node = NULL;
return 0;
}
#include <time.h>
int main(){
BinaryTree bt;
srand(time(NULL));
for(int i = 0; i < 10; i++){
bt.insert_node(rand() % 20, "test");
}
bt.print();
printf("min val = %d\n", bt.find_min()->val);
puts("#################################");
bt.delete_min();
bt.print();
printf("min val = %d\n", bt.find_min()->val);
return 0;
}
Two things come to mind right away:
a parent node is still pointing at the node which you deleted
the node you're deleting might have a node->right pointer hanging off of it, which you are lopping off the tree, instead of re-hooking it back into the tree.
There might be more problems, but you need to address these issues at a bare minimum.
Reference from his parent still point to some address in memory.
Try this:
int BinaryTree::delete_min_helper(TreeNode *node){
TreeNode *prev = NULL;
while(node->left != NULL){
prev = node;
node = node->left;
}
if(node == root){
puts("attempted to delete root");
return 1;
}
prev->left = NULL;
delete node;
node = NULL;
return 0;
}
typedef struct treeNode {
treeNode* left;
treeNode* right;
int data;
treeNode(int d) {
data = d;
left = NULL;
right = NULL;
}
}treeNode;
void insert(treeNode *root, int data) {
if (root == NULL) {
cout << &root;
root = new treeNode(data);
}
else if (data < root->data) {
insert(root->left, data);
}
else {
insert(root->right, data);
}
}
void inorderTraversal(treeNode* root) {
if (root == NULL)
return;
inorderTraversal(root->left);
cout<<root->data;
inorderTraversal(root->right);
}
int main() {
treeNode *root = new treeNode(1);
cout << &root << endl;
insert(root, 2);
inorderTraversal(root);
return 0;
}
So I'm pretty tired, but I was whipping some practice questions up for interview prep and for some reason this BST insert is not printing out that any node was added to the tree. Its probably something im glossing over with the pointers, but I can't figure it out. any ideas?
void insert(treeNode *root, int data) {
if (root == NULL) {
cout << &root;
root = new treeNode(data);
}
This change to root is lost as soon as the function ends, it does not modify the root passed as argument but its own copy of it.
Take note that when u insert the node, use pointer to pointer (pointer alone is not enough):
So, here is the fixed code:
void insert(treeNode **root, int data) {
if (*root == NULL) {
cout << root;
*root = new treeNode(data);
}
else if (data < (*root)->data) {
insert(&(*root)->left, data);
}
else {
insert(&(*root)->right, data);
}
}
And in main:
int main() {
treeNode *root = new treeNode(1);
cout << &root << endl;
insert(&root, 2);
inorderTraversal(root);
return 0;
}
Your logic is correct!
The only issue is that when you create a local variable, even if it is a pointer, its scope is local to the function. In your main:
...
insert(root, 2);
...
function call sends a copy of the root which is a pointer to treeNode (not the address of root). Please note that
void insert(treeNode *root, int data)
gets a treeNode pointer as an argument (not the address of the pointer). Attention: This function call may look like "call by pointer" (or reference) but it is actually "call by value". The root you define in the main function and the root inside the insert method have different addresses in the stack (memory) since they are different variables. The former is in main function stack in the memory while the latter is in insert method. Therefore once the function call insert finishes executing, its stack is emptied including the local variable root. For more details on memory refer to: stacks/heaps.
Of course the data in the memory that you allocated using:
*root = new treeNode(data);
still stays in the heap but you have lost the reference to (address of) it once you are out of the insert function.
The solution is either passing the address of original root to the function and modifying it (as K-ballo and dip has suggested) OR returning the modified local root from the function. For the first approach please refer to the code written by dip in his/her answer.
I personally prefer returning the modified root from the function since I find it more convenient especially when implementing other common BST algorithms. Here is your function with a slight modification of your original code:
treeNode* insert(treeNode *root, int data) {
if (root == NULL) {
root = new treeNode(data);
}
else if (data < root->data) {
root->left=insert(root->left, data);
}
else {
root->right=insert(root->right, data);
}
return treeNode;
}
The function call in main will be:
int main() {
treeNode *root = new treeNode(1);
cout << &root << endl;
root = insert(root, 2);
inorderTraversal(root);
return 0;
}
Hope that helps!
After a while seeing some complicated methods of dealing with the Binary tree i wrote a simple program that can create, insert and search a node i hope it will be usefull
/*-----------------------Tree.h-----------------------*/
#include <iostream>
#include <queue>
struct Node
{
int data;
Node * left;
Node * right;
};
// create a node with input data and return the reference of the node just created
Node* CreateNode(int data);
// insert a node with input data based on the root node as origin
void InsertNode (Node* root, int data);
// search a node with specific data based on the root node as origin
Node* SearchNode(Node* root, int data);
here we define the node structure and the functions mentioned above
/*----------------------Tree.cpp--------------*/
#include "Tree.h"
Node* CreateNode(int _data)
{
Node* node = new Node();
node->data=_data;
node->left=nullptr;
node->right=nullptr;
return node;
}
void InsertNode(Node* root, int _data)
{
// create the node to insert
Node* nodeToInsert = CreateNode(_data);
// we use a queue to go through the tree
std::queue<Node*> q;
q.push(root);
while(!q.empty())
{
Node* temp = q.front();
q.pop();
//left check
if(temp->left==nullptr)
{
temp->left=nodeToInsert;
return;
}
else
{
q.push(temp->left);
}
//right check
if(temp->right==nullptr)
{
temp->right=nodeToInsert;
return;
}
else
{
q.push(temp->right);
}
}
}
Node* SearchNode(Node* root, int _data)
{
if(root==nullptr)
return nullptr;
std::queue<Node*> q;
Node* nodeToFound = nullptr;
q.push(root);
while(!q.empty())
{
Node* temp = q.front();
q.pop();
if(temp->data==_data) nodeToFound = temp;
if(temp->left!=nullptr) q.push(temp->left);
if(temp->right!=nullptr) q.push(temp->right);
}
return nodeToFound;
}
int main()
{
// Node * root = CreateNode(1);
// root->left = CreateNode(2);
// root->left->left = CreateNode(3);
// root->left->left->right = CreateNode(5);
// root->right = CreateNode(4);
// Node * node = new Node();
// node = SearchNode(root,3);
// std::cout<<node->right->data<<std::endl;
return 0;
}