I'm creating a customer loyalty program type code using a Linked list and BST's. It uses a list of loyalty programs, each node containing a BST of customer ID's. Currently I am attempting to create a function that searches the list for a loyalty program, once found (creates if not) adds the customer ID into the BST of that node. However when testing, im running into a reading violation on the insert new list node (insert_at_front) function.
Any help would be greatly appreciated!
Ive tried altering the function type of the find_list function and creating wrapper functions for it as I have previously done with similar functions for BST's, but I keep getting lost in the code and it seems to break it more.
list.h header file:
typedef struct listNode {
char* name; //Name of company
BST *customer; //Tree of customer ID's
struct listNode *next; //Pointer for next compnay
} *ListNodePtr;
void option_insert(List *self) {
char* input_loyalty;
int input_ID;
printf("What loyalty program do you wish to add a customer to? \n");
scanf("%s", &input_loyalty);
printf("What is the customer ID \n");
scanf("%d", &input_ID);
find_list(self, input_loyalty, input_ID);
}
void find_list(List *self, char* data, int ID) {
ListNodePtr current = self->head;
if (current != NULL) {
if (current->name == data) {
insert_bst(self->head->customer, ID);
}
else {
current = current->next;
}
}
else {
insert_at_front(self, data);
insert_bst(self->head->customer, ID);
}
}
void insert_at_front(List *self, char* data) {
int n = strlen(data);
ListNodePtr new_node = malloc(n * sizeof(char*));
strcpy(new_node->name, data);
new_node->next = self->head;
self->head = new_node;
}
I have included the functions being utilised in the problem but note that they are separated in different .c files. (however this should cause no difference) and I can certainly provide more code if needed
The answer is probably in your use of malloc(). You are creating memory based off the size of data, and not the size of a struct.
I should also mention that if you are using C++ (and not C) it is probably better to learn how to use the new keyword instead.
Anyway, if you still decide to use malloc, try this instead:
void insert_at_front(List *self, char* data) {
int n = strlen(data);
ListNodePtr new_node = malloc(sizeof(listNode)); // note, we're using the size of a node instead
new_node->name = malloc(n * sizeof(char)); // now, we need to allocate the string too.
strlcpy(new_node->name, data, n); // if you want to use a "secure" copy
new_node->next = self->head;
self->head = new_node;
}
Related
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});
}
This what I need to do:
append_data adds a node onto the end of the list pointed to by top. the resulting list is one element longer, and the newly appended node has the given data value. consider using the 'append' function to help.
void AppendData(int data);
append is the same as append_data, except we're adding a node, rather than a value.
void Append(shared_ptr new_node);
I was able to AppendData using this code, but in an ideal world I could pass Append thru AppendData and get the same result. I'm struggling with Append right now even though the answer is in the AppendData code
'''
void LinkedList::AppendData(int data){
shared_ptr<node> temp(new node);
temp->data = data;
temp->next = shared_ptr<node>(NULL);
shared_ptr<node> end_node(top_ptr_);
if(end_node == NULL) {
end_node=temp;
} else {
while(end_node->next!=NULL) {
end_node=end_node->next;
}
end_node->next=temp;
}
}
void LinkedList::Append(shared_ptr<node> new_node){}
'''
in an ideal world I could pass Append thru AppendData and get the same result.
Actually, Append has less information than AppendData, in particular, the data itself. It would make more sense to refactor it in a way that AppendData creates a node, and then calls Append to append that node to the list:
void LinkedList::AppendData(int data){
// construct node
shared_ptr<node> temp(new node);
temp->data = data;
temp->next = shared_ptr<node>(NULL);
// append it
Append(temp);
}
void LinkedList::Append(shared_ptr<node> new_node){
shared_ptr<node> end_node(top_ptr_);
if(end_node == NULL) {
end_node = new_node; // new_node instead of temp
} else {
while(end_node->next!=NULL) {
end_node=end_node->next;
}
end_node->next = new_node; // ditto
}
}
Also, this line end_node = new_node; is incorrect. If the list is empty, you need to update top_ptr_ like this:
top_ptr_ = new_node;
Hi this is a code from my SearchTree class.
Node* is a structure whith m_info type int, and m_left(smaller nodes by info) and m_right(bigger nodes by info)
void SearchTree::insert(const int &x) {
Node* tempo = m_root;
while (tempo != nullptr) {
if (tempo->m_info >= x) {
tempo = tempo->m_left;
} else {
tempo = tempo->m_right;
}
}
tempo = new Node(x);
}
I am trying to insert a new node to the tree.
But looks like I am missing something in memory management.
There tempo is a pointer to a new node, however it is not being related to m_root.
I am confused here. I really love the power of c++ but it bends my logic.
What am I missing here?
You keep advancing tempo until it is equal to nullptr. At this point you have left the tree and all you have in hand is a pointer into nothingness. Note that in particular the program has no way of determining which node you last visited that led to tempo becoming null.
What you need to do instead is stop one step earlier: While tempo is still pointing to a node, but the next step would make it point to null. Now you still have a valid node of the tree in your hand and can attach the newly allocated node to it.
You can't save the pointer in tempo only. Tempo is a copy of your current position in the tree. You have to assign it to actual variable.
My solution to this problem would be to check if child is nullptr before you iterate
void SearchTree::insert(const int &x) {
if (!m_root) {
m_root = new Node(x);
return;
}
Node* tempo = m_root;
while (true) {
if (tempo->m_info >= x) {
if (!tempo->m_left) {
tempo->m_left = new Node(x);
return;
}
tempo = tempo->m_left;
} else {
if (!tempo->m_right) {
tempo->m_right = new Node(x);
return;
}
tempo = tempo->m_right;
}
}
}
Also you should use smart pointers instead of raw pointers.
An alternative solution is a pointer to pointer. I didn't test it but you can try
void SearchTree::insert(const int &x) {
Node** tempo = &m_root;
while (*tempo) {
if ((*tempo)->m_info >= x) {
tempo = &(*tempo)->m_left;
} else {
tempo = &(*tempo)->m_right;
}
}
*tempo = new Node(x);
}
In this image you can see. If you use Node* tempo = m_root then tempo contains a copy of the value in m_root. If you change tempo then m_root stays unchanged.
If you use Node** tempo = &m_root then tempo is pointer to m_root. You can change m_root through tempo.
Many thanks in advance!
So, I've made attempts to make this function work. There are mistakes in the function but cannot catch them.
It seems to me, that I've missed the logic of sorting.
Could you point me 'where to go'?
/* node*/
typedef struct client {
int number; /* */
int balance;/* */
char lastName[20]; /* */
char firstName [20];/* */
char phone[11]; /* */
char email[20];
struct client *prev;/* */
struct client *next;
struct client *tmp; /* */
} Client;
Client *firstc,*currentc,*newc, *a, *b,*tmp; /*pointers*/
/* *"firstc' firstc element in list
*'currentc' current node
*'newc' new node
*'a' temporary pointer to Sort function
*'b' temporary pointer to Sort function
*'tmp' temporary pointer to Sort function
*/
int counter = 0;
int cnum = 0; /*cnum gives unique account numbers avoiding misentering*/
/*---Sort function------*/
void Sort()
{
/* */
int a = 0;/*variables to store balance*/
int b = 0;/*variables to store balance*/
if(firstc==NULL)
printf("Database is empty"); /*message*/
else
currentc = firstc;
currentc->prev = NULL;
tmp = NULL;
while((currentc=currentc->next)!= NULL)
{ /* 1) compare two nodes;
2) IF balance >*/
int a = currentc->balance;
int b = currentc->next->balance;/* debugger stopped here... */
if (a>b)
//if(currentc->balance >currentc->next->balance)
{ /*swap nodes*/
/*code using three pointers*/
tmp = currentc->next;
currentc->next->next = currentc->next;
currentc->next->next = tmp;
}
/*3)move along the list*/
else
currentc = currentc->next;
/*4) repeat to the end of list*/
}
currentc = firstc;
listAll();
return;
}
int b = currentc->next->balance;/* debugger stopped here... */
When currentc is pointing to the last item in the list currentc->next will be null. So currentc->next->balance is an access through a null pointer.
Also, practices like making assignments in conditions like while((currentc=currentc->next)!= NULL) will eventually come back to hurt you. In this case it seems you are skipping the first item in the list.
You probably meant:
if(firstc == NULL)
printf("Database is empty"); /*message*/
else
{ /* missing braces spotted by others */
currentc = firstc;
currentc->prev = NULL;
tmp = NULL;
for( ; currentc != NULL; currentc = currentc->next)
{
if(currentc->next == NUL)
/* nothing to compare */
break;
...
}
}
Furthermore the swapping code is swapping the wrong nodes:
tmp = currentc->next;
currentc->next->next = currentc->next;
currentc->next->next = tmp;
will almost (but not quite) swap the next node (b), with the one after it instead of with (a). You need to use the prev pointer (However since this looks like homework I had better not tell you exactly how to do it). Also, you are initialising prev but you need to keep it up to date in the loop. Actually, your 3 lines above are equivalent to:
tmp = currentc->next;
currentc->next->next = tmp;
so I think you meant something else.
the problem is when currentc is the last node, currectc->next is null, thus currentc->next->balance make it crash.
add some validation like
if (currentc->next == null)
and set b to a default/predefined value or put some logic whether you swap the nodes or not.
This is an interview question that I found interesting.
Write a method that takes a pointer to a Node structure as a parameter and returns a complete copy of the passed-in data structure.
The Node structure contains two pointers to other Node structures.
For example, the method signature could look like so:
Node* Copy(Node* root);
Note - Do not make any assumptions about the data structure – it could be a tree, linked list, graph, etc.
How can this be done for any data structure ?
In the generic graph case, you need a mapping from nodes in the original graph to nodes in the new graph, so that when a cycle is encountered, the proper link gets created. If you happen to have extra temporary space in each node, large enough to hold a pointer, then you can store the mapping directly in the nodes; otherwise, you'll need to use an external map, such as an associative array or hash table.
Then it's just a matter of traversing the graph, copying nodes, and looking up the corresponding edges. Something like this:
struct Node
{
Node(int _data) : data(_data) { memset(links, 0, sizeof(links)); }
int data;
Node *links[2];
}
Node *Copy(Node *root)
{
typedef std::map<Node*, Node*> NodeMap;
NodeMap nodeMap;
std::deque<Node*> nodesToVisit;
// Set up initial new root and mapping for the root
Node *newRoot = new Node(root->data);
nodeMap[root] = newRoot;
// Breadth-first search the graph
nodesToVisit.push_back(root);
while(!nodesToVisit.empty())
{
Node *cur = nodesToVisit.front();
nodesToVisit.pop_front();
Node *newCur = nodeMap[cur];
for(int i = 0; i < 2; i++)
{
Node *link = cur->links[i];
if(link)
{
// If we've already created the corresponding node for this
// link, use that. Otherwise, create it and add it to the map.
NodeMap::iterator mappedLink = nodeMap.find(link);
if(mappedLink != nodeMap.end())
{
newCur->links[i] = mappedLink->second;
}
else
{
Node *newLink = new Node(link->data);
nodeMap[link] = newLink;
newCur->links[i] = newLink;
nodesToVisit.push_back(link);
}
}
}
}
return newRoot;
}
The problem as stated is impossible. You have to assume that the entire data structure is stored entirely within the content of nodes that are accessible from that initial one. But that is not an assumption you are allowed to make. Even your standard basic double linked list might not fit that description.
class Copier {
std::map <Node*, Node*> copies;
Node* Copy(Node* n) {
if (!n) return 0;
Node*& copy = copies[n];
if (!copy) {
copy = new Node();
copy.node1 = Copy(n.node1);
copy.node2 = Copy(n.node2);
}
return copy;
}
}
Node* Copy(Node* root) {
if (root == NULL)
return root;
std::unordered_map<Node*, Node*> completed;
std::deque<Node*> todo;
Node *ret = new Node(*scur);
completed.push_back(std::make_pair(root, ret));
todo.push_pack(root);
//while there's more nodes to duplicate
do {
//duplicate the node
Node* oldNode = todo.back();
Node* newNode = completed[cur];
todo.pop_back();
if(oldNode->left) {
auto iter = completed.find(oldNode->left);
//if it has a left child that needs duplicating, add it to the todo list
if (iter == completed.end()) {
newNode->left = new Node(*(oldNode->left));
completed.push_back(std::make_pair(oldNode->left, newNode->left));
todo.push_back(oldNode->left);
} else {
newNode->left = completed[oldNode->left];
}
}
if(oldNode->right) {
auto iter = completed.find(oldNode->right);
//if it has a right child that needs duplicating, add it to the todo list
if (iter == completed.end()) {
newNode->right = new Node(*(oldNode->right));
completed.push_back(std::make_pair(oldNode->right, newNode->right));
todo.push_back(oldNode->right);
} else {
newNode->right= completed[oldNode->right];
}
}
} while(todo.empty() == false)
//return the translation of the root
return ret;
}
Doesn't have stack overflow, root can be NULL, doesn't fail if left or right are NULL.
[Edit]Adam Rosenfield made me realize this was incorrect if there was loops in the network. Had to rewrite almost from scratch. Due to the large amount of code required, I prefer his code's for loop.
return new Node(*node);
Trick question?
You should write it recursively;
Node * Copy( Node * root )
{
Node * node_copy;
node_copy = new Node; // Assume Node1 and Node2 are initialized to 0
node_copy->content = root->content;
if( root->Node1 ) node_copy->Node1 = Copy( root->Node1 );
if( root->Node2 ) node_copy->Node2 = Copy( root->Node2 );
return node_copy;
}
So, this does not make any assumption on the data type
Given that a copy constructor exists that copies only the contents of a node and not its children:
Node* Copy(Node* root)
{
Node* copy = new Node(*root);
copy->left = Copy(root->left);
copy->right = Copy(root->right);
return copy;
}
In a more general sense, I would use copy-constructors that fully copy the entire data structure:
Node* Copy(Node* root)
{
return new Node(*root);
}