void MultiMap::insert(string key, unsigned int value)
{
if(head == nullptr)
head = new Node(key, value);
else
{
Node* tempNode = head;
while(tempNode != nullptr)
{
if(key <= tempNode->m_key)
tempNode = tempNode->m_left;
else if(key > tempNode->m_key)
tempNode = tempNode->m_right;
}
/*line 1*/tempNode = new Node(key, value);
//*line 2*/head->m_left = new Node(key, value);
}
}
For an assignment, I have to make a binary tree class, "MultiMap" with nodes that contain a string and an int.
The above is code to insert a new node into the tree. The nodes are sorted by their strings. If the string of the node I am trying to insert is > the current node, the program should try to insert it on the right branch of the tree, and if it is <=, the program should try to insert it on the left branch of the tree.
I tested it by trying to insert two nodes: (Joe, 5) and (Bill, 1) in that order, so if the program works properly, "bill" should be on the left branch of "joe".
Line 2 is commented out.
If I use line 1, the program compiles and "inserts" the second node, but when I try to look for it with other code, it only finds a nullptr. If I replace line 1 with line 2, the program works as expected.
"tempNode" is what I'm using to trace through the tree to find the appropriate place to insert a new node. "head" is a pointer to the first node in the tree. "m_left" and "m_right" are pointers to nodes, representing the left and right branches of a node, respectively.
I don't know why the two lines don't do the same thing even though at that point, it seems like tempNode and head->m_left are pointing to the same location in memory: the left branch of the first node.
Pointers are variables that hold addresses. There is nothing magic about them. Line 1 does this:
tempNode = new Node(key, value);
This doesn't insert anything into your tree. In fact, it just leaks memory.
What tempNode pointed to prior to this statement is irrelevant. More importantly, how tempNode held that prior value is already lost because you're already descended down the tree one level. Two pointers holding the same address just means the address is reachable with two pointers. Assigning a new address to a pointer has no effect on the previously addressed entity (if there was any).
Your task should be finding the pointer that should be filled in with the address of a newly allocated object. You found it (sort of). Unfortunately you also lost it as soon as you walked into it with your step "down" the tree for the final null-detection. As soon as this:
while (tempNode != nullptr)
becomes false and breaks, you're already one node too far. There are a number of ways to handle this. Some people like using a "parent" pointer, but that just means you have to special-case an empty map condition. Consider this instead:
void MultiMap::insert(string key, unsigned int value)
{
// pp will always point to the pointer we're testing
// i.e. a pointer to pointer.
Node **pp = &head;
while (*pp) // while whatever pp points to is a non-null-pointer
{
if (key < (*pp)->m_key)
pp = &(*pp)->m_left; // put address of left-pointer into pp
else if ((*pp)->m_key < key)
pp = &(*pp)->m_right; // put address of right pointer into pp
else break; // strict weak order match
}
if (*pp)
{
// found matching key. NOTE: unclear if you wanted to just update or not
}
else
{
// allocate new node.
*pp = new Node(key,value);
}
}
And you'll notice other than initializing our pointer-to-pointer with the address of the head node pointer, head is never referenced again.
Finally, notice there is no special-case head-node test. If the map is empty and the head pointer is NULL, this will automatically create a new node and make it the root.
What is going on here:
Node* tempNode = head;
while(tempNode != nullptr)
{
if(key <= tempNode->m_key)
tempNode = tempNode->m_left;
else if(key > tempNode->m_key)
tempNode = tempNode->m_right;
}
OK, now tempNode == nullptr, and it does not point to any node of the tree. As it is the variable on the stack, the next line:
/*line 1*/tempNode = new Node(key, value);
just initializes this local pointer and does not affect the tree itself. (Really here will be a memory leak.)
In your second line you initialize the node in the tree:
head->m_left = new Node(key, value);
But only for head->m_left.
So you can write:
if (key <= tempNode->m_key) {
if (tempNode->m_left == nullptr) {
tempNode->m_left = new Node(key, value);
break; // traverse tree loop
} else {
tempNode = tempNode->m_left;
}
}
Related
I am writing some code in visual studio with c++, it's an ordered linked list but I am having a bit of trouble with the pointers.
I have three different methods / functions that carry out this task.
/*
* insert_head: Insert a node at the beginning of the list.
*/
book *inserta_head(book *head, book *newNode){
newNode->next = head;
return newNode;
}
/*
* insert_after: Insert a new node after another one.
*/
void insert_after(book *node, book *newNode){
newNode->next = node->next;
node->next = newNode;
}
/*
* insert: Adds a new node (ordered by code) to the list.
*/
void insert(book* head, int code, char name[40], char lastName[40], char title[40], int year, int lend) {
book* newNode = crear_libro(code, name, lastName, title, year, lend);
book* aux = head;
// If the list is empty.
if (head == NULL){
head = insert_head(head, newNode);
} else {
// If the new node goes before the head.
if (aux->code > newNode->code){
head = insert_head(head,newNode);
} else {
while (aux != nullptr && aux->code < newNode->code)
aux = aux->next;
// Verify the code isn't repeated
if (aux != nullptr && aux->code == newNode->code){
printf("Error: Verify the code. \n");
} else {
insert_after(aux,newNode);
}
}
}
}
I've tried running the code. Every time I try to print the list it says it's empty. I've checked my printing method and the method that creates nodes, both of them are working so I'm pretty sure it is related to the pointers but I can't find the error.
Your insert function changes the head pointer. But that pointer is a copy of the head pointer you called the function with. So the head pointer outside the insert function is unchanged. That's why nothing gets added to the list.
One simple fix is to make the head parameter a reference.
void insert(book*& head, int code, ...
The problem is how you handle the head.
After this line:
head = insert_head(head, newNode);
head in the function should be correct (double check with a debugger).
However, the head in the caller will remain unchanged. This is because you don't change the data in the existing head, but you create a new one.
A simple fix is to take the pointer to the pointer to head. book** head This way you can change the pointer in the caller as well (after fixing all the compilation errors).
Value of node in *node=*(node->next), if node is the last element in linked list?
Value of node would be NULL or not?
Given a singly linked list consisting of N nodes. The task is to remove duplicates (nodes with duplicate values) from the given list (if exists).
Note: Try not to use extra space. Expected time complexity is O(N). The nodes are arranged in a sorted way.
This solution didn't work for test case 2 2 2 2 2 (five nodes with equal values).
Node *removeDuplicates(Node *root)
{
if(root->next==NULL)
return root;
Node * t1=root;
Node* t2=root->next;
while(t2!=NULL)
{
if(t1->data==t2->data)
{
*t2=*(t2->next);
}
else
{
t1=t1->next;
t2=t2->next;
}
}
return root;
}
This worked:
Node *removeDuplicates(Node *root)
{
if(root->next==NULL)
return root;
Node * t1=root;
Node* t2=root->next;
while(t2!=NULL)
{
if(t1->data==t2->data)
{
if(t2->next==NULL)
{
t1->next=NULL;
t2=NULL;
}
else
{
*t2=*(t2->next);
}
}
else
{
t1=t1->next;
t2=t2->next;
}
}
return root;
}
Normally I wouldn't post the full code for something that is clearly homework but I wasn't sure how to properly articulate all of the points. I also haven't compiled and ran this because I didn't want to create my own Node class.
First we can talk about the algorithm. If your singly linked list is already sorted and NULL terminated then essentially we have a current node pointing to a node in the list and a travel node (nextNode) that walks down the list. The main thing we need to make sure we do is update the pointers to point to the next node once we've found a non-duplicate.
In the code below I've also added NULL checks which is incredibly important. Get in the habit of knowing exactly which state your variables could be in as it is easy to accidentally call a method on a null pointer which would cause the program to crash.
Node* removeDuplicates(Node* root)
{
// Check that root isn't null before checking that its next pointer is also not NULL
if (root == NULL || root->next == NULL)
return root;
// Set up our current node and the travel node
Node* currentNode = root;
Node* nextNode = root->next;
// Until we've reached the end of the singly linked list
while (nextNode != NULL)
{
// Find the next node that isn't a duplicate
// Also check that we don't reach the end of the list
while (nextNode->data == currentNode->data && nextNode != NULL)
nextNode = nextNode.next;
// Update the current node's next pointer to point to the travel node
currentNode->next = nextNode;
// Update the current node to its next for the next iteration
currentNode = nextNode;
// Update the next node being careful to check for NULL
nextNode = nextNode == NULL ? NULL : nextNode->next;
}
return root;
}
This is not the only way to handle this problem. By reorganizing when you do certain checks and associations you can eliminate some of the NULL checks or make the program more clear. This is just one possible solution.
I've pasted my work so far here:
http://codepad.org/WhJuujRm
The concepts of linked lists boggle my mind, so I thought I'd practice. I know how to add nodes, and edit nodes, but I don't know how to remove nodes in my particular scenario.
My Pseudo Code:
previous == now - 1;
if(stdid == now->getID());
previous->setNext(now->getNext);
delete now;
return;
How could I implement this?
The mind-tease in deleting an element from a linked list is updating the pointer that brought you to the element in the first place. In your list case, that could be top (and/or possibly bottom), it could be some node's next. As you walk through the list hunting with a cur pointer, keep a prev pointer which you advance one step behind as you enumerate. Assuming you find the victim node (if you don't, there's nothing to do, woot!), prev will be in one of two states:
It will be NULL, in which case top is the pointer that refers to your victim node and top must be updated, or...
It will be some pointer to a node, in which case that node's next member needs to be updated to the reflect the victim node's next member value.
In both cases bottom may need updating as well. In the first case bottom will need to change if the list only had one node and you're deleting it. i.e. you will have an empty list when finished. Easy enough to tell, since top will be NULL after to detach cur and set top equal to cur->next. Even easier for you, since you're keeping a size member in your list container; if it was 1, you know both head and bottom
In the second case, the last node may be the victim node. In that case bottom has to be updated to reflect the new end of the list (which is coincidentally in prev, and may be NULL if, once again, the list had only a single element. How do you tell if the victim was the last node in the list? If it's next member is NULL, it has to be the last node, and bottom must be updated.
So something like this, a delete function based on ID search
void deleteStudent(int id)
{
student *cur = top, *prev = nullptr;
while (cur && cur->getID() != id)
{
prev = cur;
cur = cur->getNext();
}
// found a node?
if (cur)
{
student *pNext = cur->getNext();
// set new next pointer for prev, or new top
if (prev)
prev->setNext(pNext);
else
top = pNext;
// update bottom if needed
if (!pNext)
bottom = prev;
delete cur;
--scnt;
}
}
Other delete options and criteria I leave to you.
Best of luck.
This should work, but I have not tested it.
There is a special case, when the first node is deleted. previous is set to NULL for the first iteration, and the top has to be adjusted in this case.
I didn't use bottom, because it's not the way I would do it. If you use bottom, there is a second special case, when you delete the last student. I would mark the end of the list with a next pointer set to NULL, because this eliminates this special case.
bool deleteStudent(int id)
{
student* now = top;
student* prev = NULL;
while(now != NULL) {
student* next = now->getNext();
if(id == now->getID()) {
delete now;
if(prev) prev->setNext(next);
else top = next;
return true;
}
prev = now;
now = next;
}
return false;
}
I did not use your notation but I think you can get the point.
prev = NULL;
current = top;
while (current != NULL && !isfound(current)){
prev = current;
current = current->next;
}
// current point to the element you want to delete (if not NULL)
if(current != NULL) {
if(previous != NULL) {
previous->next = current->next;
}
else {
top = current->next;
}
delete current;
}
I'm trying to understand this function to insert an element into a linked list of sorted integers in ascending order, however, there are parts of the code that are confusing me...
Node* insert (int x, Node *p) {
if (p==nullptr)
return cons(x,nullptr); //inserts the new node at front of list,before node with nullptr?
if (x < = p-> val)
return cons(x,p); //this also inserts node at front of the list?
//now the rest of the function is where I get confused...
Node *prev=p; //so this is not allocating new memory, so what exactly does it mean?
Node * after=prev->next;
//prev->next is pointing at whatever after is pointing to but after has no address?
while (after!=nullptr && x > after->val) { //whats going on here?
prev=after;
after=after-> next;
}
prev->next=cons(x,after);
return p;
}
Node* insert (int x, Node *p) {
if (p==nullptr)
return cons(x,nullptr);
//inserts the new node at front of list,before node with nullptr?
if (x < = p-> val)
return cons(x,p); //this also inserts node at front of the list?
The code above takes care of inserting the new element at the beginning of the linked list. There are two instances for this to happen:
if the head, which is p in this case, is NULL.
if the element that p is pointing to has a value greater than the current element to be inserted.
Now going forward...
//now the rest of the function is where I get confused...
Node *prev=p;
//so this is not allocating new memory, so what exactly does it mean?
This is just like a temporary pointer which can be used to traverse the linked list. So you store the head 'p' in the prev pointer.
Node * after=prev->next;
//prev->next is pointing at whatever
//after is pointing to but after has no address?
This is a pointer that would store the next element of head.
while (after!=nullptr && x > after->val) { //whats going on here?
prev=after;
after=after-> next;
}
prev->next=cons(x,after);
return p;
}
Here, this looks a little buggy. You might want to do something like this, instead:
While(prev->data >= inputdata && (after->data < inputdata || after == NULL)
{
//insert your inputdata here because it is greater than the previous but less than after
// or after is NULL.
}
I hope this clarifies your confusion. Let me know if you have any questions.
Solved - Problem with constructor
Matthew Flaschen and Michael Burr pointed out the problem of the overloaded constructor of Node(int) calling Node() which doesn't work because...
Thanks guys!
I have built a program (I am debugging it) and have run into a weird problem... A `if` statement is not getting triggered when it should be... This is a school project where we must build an AVL Tree with at least one 'optimizing' feature.
I am sure and have tested that the `rdown` and `ldown` work (as the balancing factors) - the tree is not perfectly balanced. Rather it is based on the hight of the branches (i.e. - `balance()` should only return (1,0,-1) otherwise it is unbalanced.
I hope this is enough information to solve this weird problem... I have never ran into anything like this before with Microsoft Visual Studio 2010.
Node struct:
struct Node {
int data; // the data in the Node
int rdown; // the number of ellements below the node on the right side
int ldown; // the number of ellements below the node on the left side
Node * parrent; // the node's parrent
Node * lchild; // the nodes left child
Node * rchild; // the nodes right child
Node () { rdown = 0, ldown = 0; data = 0; parrent = NULL; lchild = NULL; rchild = NULL; }
Node (int dat) {rdown = 0, ldown = 0; parrent = NULL; lchild = NULL; rchild = NULL; data = dat; }
bool end() { if (lchild == NULL && rchild == NULL) return true; // check if this node is the 'end of the line' - where it doesn't
return false; } // have any children
bool goodToAdd() { if (lchild == NULL || rchild == NULL) return true; // make sture the current node has at least one spot to add
return false; } // a new node to - either lchild or rchild must be NULL
int balance() { return (ldown - rdown); } // get a balance number for the node
};
Search function that is causing the problems
Node * AVL_Tree::search(const Node * num) {
Node * tmpNode = AVL_Tree::root; // tmpNode is a place holder for the search
for (int i = 1; true; i++) { // increment int i to check for excess searching -> pervents endless loop
if (tmpNode == NULL) //****** causing problems******** // the search has reached a dead end (the data is not contained) ==> NULL
return NULL;
if (tmpNode->data == num->data) // if the data of num is the same as tmpNode the data is contained ==> Node *
return tmpNode;
// since the node has not been found yet move down the tree...
if (tmpNode->data > num->data && tmpNode->lchild != NULL) // if the data is smaller than the tmpNode move to the lchild
tmpNode = tmpNode->lchild;
else if (tmpNode->rchild != NULL) // since the node has been proven to not be = to the data to be searched for
tmpNode = tmpNode->rchild; // and it is not smaller... move to the right
if (i > (root->ldown + 1) && i > (root->rdown + 1) ) { // the while loop has searched suffecent time and has not ended
string tmp = "the search incountered a critical error... aborting..."; // to prevent an endless loop the string error
throw tmp; // is thrown (should not happen) - indicates a broken tree
}
}
}
A screen shot of the first encounter with the for loop
A screen shot of the second encounter with the for loop
If you would note in the 'Autos' tab at the bottom that all the data and the node itself's address is NULL - yet in the next screen shot it continues
The program continues!!! what?>!
I pushed F-10 (the 'go to next command' button) ... and it jumps right over the statement? why?
0xcdcdcdcd is not a NULL pointer - that value is used in the debug builds of MSVC for memory that has been allocated but not initialized.
See When and why will an OS initialise memory to 0xCD, 0xDD, etc. on malloc/free/new/delete? for more details.
The root of your problem might be in the constructor that takes an int parameter:
Node (int dat) { Node(); data = dat; }
The Node(); statement ends up doing nothing. This constructor leaves most of the members of the structure uninitialized.
tmpNode is not null in any screenshot.
It's first 0x00294820, then 0xcdcdcdcd. The second is the magic debug value for uninitialized malloced memory.
NULL, in C++, tends to be (but is not guaranteed to be) 0.
In your second/third screenshots, tmpNode = 0xcdcdcdcd, which is not NULL. 0xcdcdcdcd is the value Visual Studio gives to uninitialized variables (when running a debug release).
Make sure to initialize all all your nodes' fields:
Node* root = NULL;
or
Node* root = new Node(); //Don't forget to delete!
Setting fields to NULL is not done automatically in C++ as it is in other languages like Java and C#.
tmpNode is referencing uninitialized memory, which is generally not guaranteed to be null. For instance, the following statement does not guarantee that tmpNode is null.
Node* tmpNode; // or assignment to another uninitialized variable.
You are assigning tmpNode to root and I suspect that root is uninitialized, hence the non-null value of tmpNode. Please check your initialization of root -- I cannot comment on it as you haven't posted this specific code.