Binary search tree doesn't work - c++

I'm having a pretty confusing problem in building a binary tree. Apparently this should be an easy task but somehow I may mess up with the pointers in it.
Here's the simplified code (of course it's not the real code) :
#include <string.h>
#include <iostream>
using namespace std;
#define DIM1 2
typedef enum {LEFT,RIGHT} direction;
typedef char tName[MAX_NAME_LEN + 1];
struct Rectangle {
tName _name;
struct Rectangle *_binSon[DIM1];
};
struct Rectangle *recTree;
void insertRectToTree(char str[]){
struct Rectangle rect;
struct Rectangle *point;
struct Rectangle *parent;
strcpy(rect._name,str);
rect._binSon[RIGHT] = NULL;
rect._binSon[LEFT] = NULL;
point = &rect;
if (recTree == NULL){
recTree = point;
} else {
struct Rectangle *current;
current = recTree;
while (current){
parent = current;
if (strcmp(point -> _name, current -> _name) > 0){
current = current -> _binSon[RIGHT];
} else {
current = current -> _binSon[LEFT];
}
}
if (strcmp(point -> _name, parent -> _name) < 0){
parent -> _binSon[LEFT] = point;
} else {
parent -> _binSon[RIGHT] = point;
}
}
}
int main(){
recTree = NULL;
char str[] = "LIKE";
insertRectToTree(str);
char str2[] = "GUIDE";
insertRectToTree(str2);
printf(recTree -> _name);
return 0;
}
As you can see, this binary tree tries to organize a record based on its name, so the smallest alphabetical order will go to the left side and so on.
The problem is, after the first insertion "LIKE", I want "GUIDE" inserted to the tree as well, with "LIKE" still as the root. However, the printf() shows that "GUIDE" takes over as its root. (In other word, "GUIDE" is the output). Any good explanation for this? Ask me if I need to add some more thing to this question. Thanks for all of your help.

Within the following lines
struct Rectangle rect;
...
point = &rect;
...
recTree = point;
you assign a reference to a local variable to a global pointer. After leaving the function it may no longer contain valid data.

Howard is correct. But to correct the problem use new.
i.e. instead of
point = &rect;
Put
point = new struct Rectangle;

Related

How to iterate over a template class?

I'm trying to create a generic menu class that will be used with a 4 line LCD.
I have a specific (non template) version working, but want to extend it to allow the menu to modify a variety of data types (int, float, unsigned...).
Here's the non template version that's working as expected...
/*
* ideally this design allows for defining an arbitrary menu as shown below
* example...
* root1
* sub1-1
* sub1-2
* root 2
* root 3
* sub3-1
* sub3-2
* sub3-2-1
* sub3-2-2
*
* each node in the menu can be executed, and allow for moving to the next/prev sibling or child/parent
* this flexibility requires that each node contains pointers to parent, child, and sibling nodes.
*/
class MenuNode
{
private:
char *prompt;
int value;
public:
MenuNode *parent=NULL;
MenuNode *child=NULL;
MenuNode *prevSibling=NULL;
MenuNode *nextSibling=NULL;
void SetValue(int value)
{
this->value = value;
}
int GetValue()
{
return value;
}
char *Prompt()
{
return prompt;
}
MenuNode(char *prompt, int initialValue, MenuNode *parent, MenuNode *prevSibling)
{
Serial.print(prompt);Serial.println(F(" MenuNode"));
this->prompt = prompt;
if (prevSibling != NULL)
{
this->prevSibling = prevSibling;
prevSibling->SetNextSibling(this);
this->parent = prevSibling->parent;
}
// prevSibling if provided sets the parent
if (prevSibling==NULL && parent != NULL)
{
this->parent = parent;
this->parent->SetChild(this);
}
value = initialValue;
}
void SetChild(MenuNode *child)
{
Serial.print(prompt);Serial.println(F(" SetChild"));
this->child = child;
}
void SetNextSibling(MenuNode *nextSibling)
{
Serial.print(prompt);Serial.println(F(" SetNextSibling"));
this->nextSibling = nextSibling;
}
};
Here's some test code that creates the menu structure...
// Test menu...
MenuNode r1("R1",10,NULL,NULL);
MenuNode r2("R2",20,NULL,&r1);
MenuNode r21("R21",30,&r2,NULL);
MenuNode r22("R22",40,&r2,&r21); // setting parent is optional, the parent will be set by the prev sibling parent
MenuNode r221("R221",50,&r22,NULL);
MenuNode r2211("R2211",60,&r221,NULL);
MenuNode r2212("R2212",70,NULL,&r2211);
MenuNode r3("R3",30,NULL,&r2);
This code iterates over each element printing out the structure
void PrintMenuStructure(MenuNode *node,int offset)
{
while(node != NULL)
{
for (int i=0;i<offset;i++)
Serial.print("-");
Serial.print(node->Prompt());
Serial.print(" = ");
Serial.print(node->Value());
if (node->parent != NULL)
{
Serial.print(" parent=");
Serial.print(node->parent->Prompt());
}
if (node->prevSibling != NULL)
{
Serial.print(" prevSib=");
Serial.print(node->prevSibling->Prompt());
}
if (node->nextSibling != NULL)
{
Serial.print(" nextSib=");
Serial.print(node->nextSibling->Prompt());
}
if (node->child != NULL)
{
Serial.print(" child=");
Serial.print(node->child->Prompt());
}
Serial.println();
if (node->child != NULL)
PrintMenuStructure(node->child,++offset);
node = node->nextSibling;
}
}
This is the output of the previous function demonstrating the structure of the menu...
R1 = 10 nextSib=R2
R2 = 20 prevSib=R1 nextSib=R3 child=R21
-R21 = 30 parent=R2 nextSib=R22
-R22 = 40 parent=R2 prevSib=R21 child=R221
--R221 = 50 parent=R22 child=R2211
---R2211 = 60 parent=R221 nextSib=R2212
---R2212 = 70 parent=R221 prevSib=R2211
-R3 = 30 prevSib=R2
It all works the way I want, but GetValue/SetValue only operate on int data.
I can create a template version of the class, with the data types of GetValue and SetValue defined by the template parameter, but I don't know now to iterate over the nodes once I do that.
Seems like a simple enough task, but I've been beating my head against the wall for a while, and haven't come up with anything that works. Any help pointing me in the right direction would be appreciated.
I'm trying to figure out how to iterate over a linked list of classes, but can't figure out how to get a pointer to start iterating.
Sorry, I couldn't get the code formatting to work... :(
The way I interpret your requirement: it seems your should make your
int value;
a std::variant.
That's the lowest cost path.
If you templatize the MenuNode class with its value type. Then a MenuNode<int>* cannot be the parent of a MenuNode<float*>, etc. Not without some effort. You'd probably better off make it polymorphic by derivate each type of value your want to support from a common abstract virtual base, and depend on how you want to use the value, design your interface.

what's wrong here ? while (temp->ptr_Next != NULL) fails, if (temp->ptr_Next == NULL) works

I've been making a doubly linked list and the while statements are going a bit wonky. I'm sure there's a simple explanation, but I'm not seeing it. Can anyone help?
This is working (Embarcadero RAD studio 11.1 - C++ Builder - Classic compiler (not Clang))
TDoubleLinkedNode* __fastcall TDoubleLinkedList::CreateNode ( int ID
, UnicodeString Name
)
{
// use "malloc" (or "new") to create the node in the heap
// it won't be deleted automatically when the function goes out of scope
// return the pointer to the structure
// let the calling function set PriorNode & NextNode
struct TDoubleLinkedNode* ReturnNode = (struct TDoubleLinkedNode*)malloc(sizeof(struct TDoubleLinkedNode));
ReturnNode->ID = ID;
ReturnNode->Name = Name;
ReturnNode->ptr_PriorNode = NULL;
ReturnNode->ptr_NextNode = NULL;
return ReturnNode;
}
void __fastcall TDoubleLinkedList::Add_ToEnd ( int ID
, UnicodeString Name
)
{
struct TDoubleLinkedNode* newNode = CreateNode(ID,Name);
if(this->IsEmpty)
{
// Head Pointer has not been initialised. Set as newNode
this->FHeadNode = newNode;
// This is the first Record.
newNode->ptr_PriorNode = NULL;
newNode->ptr_NextNode = NULL;
return;
}
else
{
struct TDoubleLinkedNode* oldHeadNode = this->FHeadNode;
struct TDoubleLinkedNode* tempNode = this->FHeadNode;
do // keep iterating until a break statement is triggered
{
if (tempNode->ptr_NextNode == NULL) // terminate the do while statement
{
break;
}
tempNode = tempNode->ptr_NextNode; // Move to the "Next" record
}
while (true); // always repeat...
tempNode->ptr_NextNode = newNode;
newNode->ptr_PriorNode = tempNode;
newNode->ptr_NextNode = NULL;
}
}
However, if I replace
do
{
if (tempNode->ptr_NextNode == NULL)break;
}
while (true);
with
while (tempNode->ptr_NextNode != NULL)
{
tempNode = tempNode->ptr_NextNode ;
}
the while statement does not break when tempNode->ptr_NextNode == NULL resulting in tempNode being set to NULL making the tempNode->ptr_NextNode = newNode that follows fail (Since you can't assign data to a non existent object).
I have been stepping through and the while is definitely running when tempNode->ptr_NextNode == NULL , when my understanding is it shouldn't??
I'm sure this isn't the only area that's messed up (there's quite a few while statements).
I'm adding 6 test records and only able to retrieve 5! so it's obvious something is up. If you can shed some light on what I'm not understanding I'd be grateful
thanks, J
I haven't used CBuilder for some 20 years, but if what you say is exactly what happens, if replacing that 'do while' loop to that 'while' changes the behavior, and you can clearly see that crazy thing occurring when you are stepping through it, it also seems illogical to me.
I don't know how it is now, but there in the beginning of the century, since C++ Builder has a lot of complicated links with Object Pascal libraries, it was not so uncommon to reach very strange situations, with crazy things happening in debug. What used to help was to do a "rebuild all", possibly removing all temporary files I could find in the project.
Maybe it helps.
And I also support our colleagues' comments regarding updating your code to use more appropriate C++ alternatives, much simpler and efficient ones, if possible (I know sometimes you can be working on legacy software which may not be easy to update).
And it is actually also very likely you are seen undefined behavior due to corrupted content, as people also said in comments. That only you can determine.
Update: I've just seen the other answer here just posted by Remy Lebeau, and I would like to add he is right regarding the bad use of malloc. Searching google I see that UnicodeString seems to be an object from Object Pascal, is it? Really seems a non-POD and you will run into trouble anyway, don't use malloc for C++ objects.
Your while loop code is fine (though coded oddly, and inefficiently).
Chances are, you are invoking undefined/illegal behavior elsewhere in the code, and that is likely affecting the while loop as a side effect.
For instance, DO NOT use malloc()/free() in C++, use new/delete instead. Your TDoubleLinkedList struct contains a Name member that is clearly a non-POD class type, as it is being assigned a UnicodeString value. That member's constructor will NOT be run when using malloc() to allocate the TDoubleLinkedList instance, thus invoking undefined behavior when the Name parameter is assigned to the ReturnNode->Name member.
That being said, you should get rid of CreateNode() entirely, and instead add a proper constructor to TDoubleLinkedNode itself, eg:
struct TDoubleLinkedNode
{
int ID;
UnicodeString Name;
TDoubleLinkedNode *ptr_PriorNode;
TDoubleLinkedNode *ptr_NextNode;
TDoubleLinkedNode(int id, UnicodeString name, TDoubleLinkedNode *priorNode = NULL, TDoubleLinkedNode *nextNode = NULL) :
ID(id),
Name(name),
ptr_PriorNode(priorNode),
ptr_NextNode(nextNode)
{
}
};
And then Add_ToEnd() can be simplified:
void __fastcall TDoubleLinkedList::Add_ToEnd ( int ID,
UnicodeString Name
)
{
TDoubleLinkedNode* newNode = new TDoubleLinkedNode(ID, Name);
if (!FHeadNode)
{
// Head Pointer has not been initialised. Set as newNode
FHeadNode = newNode;
return;
}
TDoubleLinkedNode* tempNode = FHeadNode;
while (tempNode->ptr_NextNode) {
tempNode = tempNode->ptr_NextNode; // Move to the "Next" record
}
tempNode->ptr_NextNode = newNode;
newNode->ptr_PriorNode = tempNode;
}
Which can actually be simplified much further:
void __fastcall TDoubleLinkedList::Add_ToEnd ( int ID,
UnicodeString Name
)
{
TDoubleLinkedNode** tempNode = &FHeadNode;
while (*tempNode) {
tempNode = &((*tempNode)->ptr_NextNode);
}
*tempNode = new TDoubleLinkedNode(ID, Name, *tempNode);
}
And more so if you add a FTailNode member to your class:
void __fastcall TDoubleLinkedList::Add_ToEnd ( int ID,
UnicodeString Name
)
{
TDoubleLinkedNode **tempNode = (FTailNode) ? &(FTailNode->ptr_NextNode) : &FHeadNode;
FTailNode = new TDoubleLinkedNode(ID, Name, FTailNode);
*tempNode = FTailNode;
}
That being said, a better solution is to not create a linked list manually at all. Use the standard std::list container in the <list> header, eg:
#include <list>
struct TNodeData
{
int ID;
UnicodeString Name;
};
class TDoubleLinkedList
{
private:
std::list<TNodeData> FData;
public:
...
void __fastcall Add_ToEnd(int ID, UnicodeString Name);
...
};
void __fastcall TDoubleLinkedList::Add_ToEnd ( int ID,
UnicodeString Name
)
{
FData.push_back(TNodeData{ID, Name});
}

How do I return a pointer to a node nested as the value of a map? (Access violation)

So I'm trying to create my own graph interpretation with a linked list;
Essentially, I want to create nodes linked to other nodes by n/e/s/w coordinates and every time I create a node, these nodes get added to a list I can check on later called 'Exists'(if it exists).
I reflect on the pair 'yx' coordinates of these nodes to confirm that. This is what my header looks like this:
class Graph {
private:
struct Area {
pair<int , int> yx;
Area *north;
Area *east;
Area *south;
Area *west;
};
Area *Start;
Area *Current;
map<pair<int, int>, Area*> Exists; //Need to get the stored Area from this map
public:
Graph();
void Create();
//Add_Node calls Connect_Nodes()
void Add_Node(Area before, int code, int symbol, int color, pair<int,int> yx, string title, string description, int coordinate);
//Connects nodes if the key is found in the map
Area* Connect_Nodes(pair<int, int> yx, int coordinate);
};
Here is the implementation, Add_Node() called first in void Create():
Add_Node(*Start, 00001, '.', 3, Start->yx, "Hallway", "Halls", 1);
Add_Node Calls the connection next:
void Graph::Add_Node(Area before, pair<int, int> yx, ... int coordinate){
Area *ptr = Connect_Nodes(yx, coordinate);
And the Connect method:
Graph::Area* Graph::Connect_Nodes(pair<int,int> yx, int coordinate) {
pair<int, int> temp;
switch (coordinate) {
case 1:
temp.first = ++yx.first;
temp.second = yx.second;
break;
....
}
map<pair<int, int>, Area*>::iterator it;
it = Exists.find(temp);
if (it != Exists.end())
return it->second;
else return nullptr;
}
I'm probably missing something important with my pointer implementations, I get the following error:
Exception thrown: read access violation.
std::_String_alloc<std::_String_base_types<char,std::allocator<char> > >::_Get_data(...) returned 0x14.
I can create the nodes just fine, however, in a Display() method, ptr iterates to the node, when I run ptr->title on the created node, I get the read access violation. What could be causing this error?
I hope I've documented this well enough to be understood.
Edit: Step through discovered the following in my un-posted Add_Node method:
void Graph::Add_Node(Area before, pair<int, int> yx, int coordinate){
bool passed = true;
Area *ptr = Connect_Nodes(yx, coordinate);
if (ptr != NULL) {
switch (coordinate) {
case 1:
before.north = ptr;
case 2:
before.east = ptr;
case 3:
before.south = ptr;
case 4:
before.west = ptr;
}
}
else {
do
{
Current = new Area;
Current_Code++;
Current->code = Current_Code;
switch (coordinate) {
case 1:
if (before.north == NULL)
{
Current->yx.first = ++before.yx.first;
Current->yx.second = before.yx.second;
Exists[Current->yx] = Current;
before.north = Current; // PROBLEM LINE
return;
}
When I replaced before.north in the above code with Start->north I was able to display everything just fine without any errors!
Start->north = Current;
However, since I used the before instance throughout the code it will need an adjustment to work.
The idea is that 'before' signifies an existing node that would connect to the node to be added.

C++ get error after trying to check if pointer is null

I'm writing a program using single linked list. I'm adding element from the end of linked list, and to do that i need to do null check. But after i try to check if element is NULL or nullptr, i get error. Is problem in !*head check method? P.S Also would like to hear if there is better way to create/insert new node in the linked list.
#include <iostream>
typedef struct vertex vertex;
typedef struct edge edge;
typedef struct vertex {
int vertex;
struct vertex * next;
edge ** head;
}vertex;
typedef struct edge {
int vertex1, vertex2;
struct edge * next;
}edge;
void add_vertex(vertex ** head, int vertex_value) {
vertex * new_vertex = new(vertex);
new_vertex->vertex = vertex_value;
if (!*head) {
new_vertex->next = NULL;
*head = new_vertex;
return;
}
vertex * tracer = *head;
while (tracer->next != NULL) {
tracer = tracer->next;
}
tracer->next = new_vertex;
new_vertex->next = NULL;
}
int main() {
vertex ** head = nullptr;
add_vertex(head, 1);
}
I get read access violation error in add_vertex functions on if(!*head) moment.
You are trying to dereference head which is it self NULL.
That is illegal in C++.
The way your add_vertex function is built, you must never send it NULL parameter as head.
Allocate a head in main and that problem will be solved.
In main
vertex *head = nullptr;
add_vertex(&head, 1);
This fixes your problem, but it would be better to pass head by reference.

C++ RapidXML get sibling of the same type?

So, in RapidXML, I'm trying to loop through my file to get the data from some tileset nodes:
rapidxml::xml_node<> *root_node = doc.first_node("map");
for(rapidxml::xml_node<> *tileset = root_node->first_node("tileset");
tileset != 0; tileset = tileset->next_sibling("tileset"))
{
// Iteration stuff...
You're probably saying, what's the problem? Well, in RapidXML, the next_sibling() function optionally matches the name:
xml_node<Ch>* next_sibling(const Ch *name=0, std::size_t name_size=0, bool
case_sensitive=true) const;
Gets next sibling node, optionally matching node name. Behaviour is undefined
if node has no parent. Use parent() to test if node has a parent.
Hence, if a node is not found with the name, it'll just return the next sibling regardless. This is a problem in my program, and I just plain don't want the extra iteration. I think this is stupid, but whatever. Is there a way to make it ONLY iterate through my tileset nodes?
"optionally matching node name" - As in the parameter is optional. If you pass a name string, and it is not found you will get a return value of zero.
xml_node<Ch> *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const
{
assert(this->m_parent); // Cannot query for siblings if node has no parent
if (name)
{
if (name_size == 0)
name_size = internal::measure(name);
for (xml_node<Ch> *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling)
if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive))
return sibling;
return 0;
}
else
return m_next_sibling;
}
I also had this problem and I used this small modification as a workaround, which works as intended.
rapidxml::xml_node<> *root_node = doc.first_node("map");
for(rapidxml::xml_node<> *tileset = root_node->first_node("tileset");
tileset != 0;
tileset = tileset->next_sibling())
{
if(strcmp(tileset->name(), "tileset")!=0)
continue;
//TODO: the usual loop contents
}