I've a uni assignment and unfortunately, encountered a problem.
I'm still struggling with pointers and references so it's quite hard for me to find a solution, even though I've searched for it for an entire day.
Here are essential parts of my code:
struct BuenoList
{
int value;
BuenoList* prev;
BuenoList* next;
};
Declaration:
void insertNode(BuenoList*, int);
Definition:
void insertNode(BuenoList* tail, int _id)
{
BuenoList* temp = new BuenoList;
temp->value = _id;
temp->prev = tail;
tail->next = temp;
tail = temp;
tail->next = NULL;
}
Now, in main() I do this:
int main(int argc, char **argv)
{
BuenoList* BuenoListing = new BuenoList;
BuenoListing->value = 1;
BuenoListing->prev = NULL;
BuenoListing->next = NULL;
BuenoList* BuenoHead = BuenoListing;
BuenoList* BuenoTail = BuenoListing;
insertNode(BuenoTail, 2); // Add value 2 to the list
insertNode(BuenoTail, 3); // Add value 3 to the list
return 0;
}
Okay, so here's the problem:
When I print the list from the first element it prints like this:
1
1 2
1 3
So apparently this line
insertNode(BuenoTail, 3);
overwrites value 2.
My guess would be that BuenoTail does not change so there must be a problem with the reference.
How can I solve this?
Print should be: 1 2 3
#EDIT
void deleteNode(BuenoList*& tail, int _id) // Search list for specified value and delete it
{
BuenoList* temp = tail;
while (temp->prev != NULL)
{
if (temp->value == _id) break;
else temp = temp->prev;
}
if (temp->value == _id)
{
temp->prev = temp->next;
free(temp);
}
else std::cout << "ERROR KEY DOES NOT EXIST\n";
}
Considering you wanted to keep your code structure the same, I will show you two possible solutions. In a C fashion, you can use a pointer to a pointer to update your head and tail pointers. Or, in C++ you can pass your head and tail pointers by reference. Personally, in C++ I would make a LinkedList class that keeps track of your head and tail nodes though.
Passing pointer by reference (C++)
In order to pass your pointer by reference, first change your insertNode declaration,
void insertNode(BuenoList *&tail, int _id);
Next, change your insertNode definition with the same function arguments. That is it, your code should work now. This minute change works, because your tail node in the insertNode function is aliased with the tail node in your main function.
Passing pointer by pointer (C)
I know you put a C++ tag, but you can also update your head and tail nodes using a more C like method. First, in the main function, you will need to declare BuenoHead and BuenoTail as a pointer to a pointer.
int main(int argc, char **argv)
{
BuenoList* BuenoListing = new BuenoList;
BuenoListing->value = 1;
BuenoListing->prev = NULL;
BuenoListing->next = NULL;
BuenoList** BuenoHead = new BuenoList*;
BuenoList** BuenoTail = new BuenoList*;
*BuenoHead = BuenoListing;
*BuenoTail = BuenoListing;
insertNode(BuenoTail, 2); // Add value 2 to the list
insertNode(BuenoTail, 3); // Add value 3 to the list
return 0;
}
Then you need to update the insertNode function declaration to take a pointer to a pointer.
void insertNode(BuenoList** tail, int _id);
Then, you need to update the function definition to deference your pointer to a pointer correctly,
void insertNode(BuenoList** tail, int _id)
{
BuenoList* temp = new BuenoList;
temp->value = _id;
temp->prev = *tail;
temp->next = NULL;
(*tail)->next = temp;
*tail = temp;
}
EDIT
Deleting a node
You modified your question, asking for help on deleting a node. You have several problems with your deleteNode function.
You mix new with free. Whenever you allocate with new, you should correspondingly use delete.
You call temp->prev without checking whether temp could be NULL or not.
You need to change the function arguments to include your head pointer. It might turn out the node you want to delete is the head of your linked list. If so, you'll need to update the head node accordingly.
You need to check whether the node you are deleting is in the middle of the list, the head of the list, the tail of the list, or if is is the only node of the list. Each of these cases requires different operations to update your linked list.
Considering this is a university assignment, I don't want to give you the full blown solution. Here is a partially modified deleteNode function. I left some parts for you to fill out though. Hopefully that helps. Maybe next time focus the question a bit more, so I don't have to give a partial solution.
void deleteNode(BuenoList*& tail, BuenoList*& head, int _id) {
BuenoList* toDelete;
toDelete = tail;
// Traverse list and find node to delete
while( ( toDelete != NULL ) && ( toDelete->value != _id ) ) {
toDelete = toDelete->prev;
}
// If node not found, return
if( toDelete == NULL ) {
std::cout << "ERROR KEY DOES NOT EXIST\n";
return;
}
// Check to see if node to delete is tail
if( toDelete == tail ) {
if( toDelete->prev != NULL ) {
tail = toDelete->prev;
tail->next = NULL;
} else { //Deleting only node in list
tail = NULL;
head = NULL;
}
delete toDelete;
return;
}
// Check to see if node to delete is head
if( toDelete == head ) {
// FILL OUT WHAT TO DO HERE
return;
}
// Node to delete is neither head nor tail.
// FILL OUT WHAT TO DO HERE
return;
}
You are updating tail within insertNode(), but the updated value is not communicated back to the caller in any way. So BuenoTail does not change, as you suspected.
One easy way to fix that is to change your function to:
BuenoList *insertNode(BuenoList* tail, int _id)
and return the new tail from that function. Also change the calls to
BuenoTail = insertNode(BuenoTail, 2); // Add value 2 to the list
You may want to consider using a node structure and a list structure:
struct BuenoNode
{
int value;
BuenoNode* prev;
BuenoNode* next;
};
struct BuenoList
{
BuenoNode* head;
BuenoNode* tail;
size_t count; // having a count would be optional
}
The list functions would take a pointer to the list structure, such as InsertNode(BuenoList * blptr, int data); The list structure would be initialized so that both head and tail pointers == NULL, and you'll need to check for adding a node to an empty list or removing the only node in a list to end up with an empty list.
Related
Here is how I defined and initialized a linked list
struct listrec
{
struct listrec *prev;
float value;
struct listrec *next;
};
listrec *head, *tail;
int main() {
int number;
cin >> number;
float content = 2.0;
for (float i = 0; i < number; i++)
{
if (i == 0)
{
head = new listrec;
head->prev = nullptr;
head->value = 1.0;
head->next = nullptr;
tail = head;
}
else
{
auto *newNode = new listrec;
newNode->value = content++;
newNode->next = nullptr;
newNode->prev = tail;
tail->next = newNode;
tail = tail->next;
}
}
return 0;
}
This is how the linked list looks like
I need to " write a function that takes two input parameters - the pointer head OR tail plus a parameter of which direction to traverse - to traverse the linked list and return the number of elements in the list. "
I have no idea how to write a function like that…
I know, if I want to count the number of elements from the first node, then I can write a function like this:
float listSize(listrec* head)
{
int count = 0;
listrec* current = head; // Initialize current
while (current != NULL)
{
count++;
current = current->next;
}
return count;
}
Or, if I want to count the elements from the last element, then
float listSize2(listrec* tail)
{
int count = 1;
listrec* current = tail;
while (tail->prev != NULL)
{
count++;
tail = tail->prev;
}
return count;
}
But how can I combine these two? Any hints would be appreciated!
Here is the function, assuming a doubly linked list:
enum class Direction {FORWARD, REVERSE};
struct Node
{
Node * previous;
Node * next;
};
unsigned int Count(Node * p_begin, Direction traverse_dir)
{
unsigned int node_count = 0U;
while (p_begin != nullptr)
{
++node_count;
if (traverse_dir == FORWARD)
{
p_begin = p_begin->next;
}
else
{
p_begin = p_begin->previous;
}
}
return node_count;
}
Per the requirement, the function takes 2 parameters, a pointer to a head or tail node, and a direction and returns the quantity of nodes traversed.
The function starts at the pointer passed, then goes forward or reverse (depending on the direction parameter), and increments the node counter. The loop stops when a null pointer is encountered, which usually signals the beginning or end of a list.
Since only a node class is used, you can inherit from the Node to make various list types:
struct Integer_Node : public Node
{
int data;
};
The data field does not play a role in traversing the list, so it was removed from the fundamental node object.
You don't need to "combine" them. You need to call one or the other depending on the direction:
enum class Direction { Forward, Backwards };
int listSize(listrec* p, Direction dir)
{
if (dir == Direction::Forward)
return listSize(p);
else
return listSize2(p);
}
This is not a review site, that being said I cannot in good conscience leave this answer without some advice for your code:
in C++ you should use RAII. A consequence of that is that you should never use explicit calls to new / delete and you should not use owning raw pointers.
count is an integer, so returning float in your functions makes no sense. Floating point data has its problems, don't use it for integers.
better name your functions. listSize and listSize2 are terrible names. Your functions don't list, they just return the size. So a better name is getSize. Also differentiating between then by a number is another terrible idea. You can use getSize and getSizeReverse.
there is no need to pass pointers to your function. Passing by reference, or even by value in your case is preferred.
you need better OOP abstractions. listrec is a list record (aka a list node). On top of this you need a class that abstracts a list. This would contain a pointer to the head of the list and a pointer to the tail of the list.
you should create a function for insertion into the list (and one for each operation on the list) and not do it manually in main.
While in the process of implementing a Linked List in C++, I ran into an issue with my function for inserting a new element at the tail end of the list.
struct LinkedNode
{
int data;
LinkedNode* next;
};
class LinkedList
{
private:
LinkedNode* head;
LinkedNode* tail;
int size;
public:
LinkedList();
void insert_back(int element);
int at(int index);
int length();
};
Originally, my insert_back function was as follows:
void LinkedList::insert_back(int element)
{
LinkedNode node = {element, 0};
if(size == 0)
{
head = &node;
tail = head;
}
else
{
tail->next = &node;
tail = tail->next;
}
++size;
}
However, when I iterated through a four-element list, printing out each element as such:
LinkedList myList;
myList.insert_back(5);
myList.insert_back(6);
myList.insert_back(7);
myList.insert_back(8);
for(int i = 0; i < myList.length(); ++i)
{
cout << myList.at(i) << endl;
}
the last element in the list printed first and then I would receive three garbage values such as in:
8
-403642776
-403642776
-403642776
I fixed this error by changing the way I create a new node in insert_back. This time, I used the new keyword.
void LinkedList::insert_back(int element)
{
LinkedNode* node = new LinkedNode;
node->data = element;
node->next = 0;
if(size == 0)
{
head = node;
tail = head;
}
else
{
tail->next = node;
tail = tail->next;
}
++size;
}
This fixed the error, but I don't really understand why. For whatever reason, in my old code, head and tail were always pointing at the same address, but they are no longer when I use new. Why could this be?
The issue is that the code LinkedNode node = {element, 0}; ... head = &node; of your first version stored the address of a local variable, which becomes invalid as soon as the insert-function finishes. This leads to undefined behaviour once you access this pointer later (e.g. when traversing your list).
In the second version, with LinkedNode* node = new LinkedNode;, you allocate memory dynamically, and such an object is valid until you explicitly delete it. Hence, the pointer remains valid and you may access it later on.
I have the following function to reverse a linked list:
Node* reverseUtil(Node *head, Node *prev) {
if (head->next == NULL) {
head->next = prev;
return head;
}
Node *next = head->next;
head->next = prev;
prev = head;
head = next;
return reverseUtil(head, prev);
}
When I call this with Node* head = reverseUtil(head, NULL); it works fine and ultimately returns the head of the reversed linked list. Example for 1->2->3, it returns head with 3->2->1.
However, when I modify it to the following, the resultant head pointer only has the last node of the reversed linked list (originally the starting point of the list. Example: for original list 1->2->3, head at the end only has 1.
void reverseUtil(Node *head, Node *prev) {
if (head->next == NULL) {
head->next = prev;
return;
}
Node *next = head->next;
head->next = prev;
prev = head;
head = next; //head pointer should update here on forward path. This seems to be happening.
reverseUtil(head, prev);//after this step returns, head should stick to the last updated point and should return unchanged but this does not happen.
}
I think I am missing something basic here. I would expect that the statement head = next at each call of the function would update the head pointer. It should ultimately point to the new beginning of the reversed linked list and should remain unchanged as the recursive calls return. However it seems to be continuously getting updated on the return path.
When you pass a pointer to a function like void foo(type* bar) the address that the pointer contains is passed by value. If you need to change that address then you need to pass it by reference like void foo(type *&bar). I think that you are confused by the fact that this works
void foo(int * bar) { *bar = 10; }
int man()
{
int x = 5;
foo(&x);
// x is now 10 here
}
Here we are not changing what the pointer points to but we are changing the value of the pointed to object. We can see that changing the address of the pointer has no affect when not passed by reference with:
void foo(int * bar)
{
int foobar = 10;
bar = &foobar;
}
int main()
{
int x = 5;
foo(&x);
std::cout << x;
}
Live Example
I've checked the boards and could not find any help with this. I find it easy to implement recursive functions given base and general cases, but this doesn't work the way I do it. I'm supposed to iterate down a list until I reach the tail of a linked list. If the next node is NULL, then I have to store the value at the last node, remove that node, and return the value. So it's similar to a dequeue method, except it's performed recursively. What am I doing wrong?
int LinkedList::removeTailRec(Node *n)
{
// check for the base case(s)
if(n->next == NULL)
{
Node *tmp = new Node();
tmp = n;
int val = n->value;
tmp = NULL;
return val;
}
else
return removeTailRec(n->next);
// else call the recursive method
}
First, I recommend you use nullptr instead of NULL.
Then, onto your code. You're actually not removing anything from your list.
if(n->next == NULL)
{
Node *tmp = new Node();
^^^^^^^^^^
//Useless, and dangerous. This memory is never free'd
tmp = n;
int val = n->value;
tmp = NULL;
^^^^^^^^^^
//You just set a local variable to NULL, you're not deleting anything
return val;
}
If you want to remove the node, you'll have to keep a reference to the previous node (e.g. having a doubly linked list, that is, having a pointer to the next element and a pointer to the previous element in each node, or working on the previous node directly).
Set this previous node's next to nullptr, store the node's value and then delete the Node pointer.
One way to do this is to work with the pointer to the next node :
int LinkedList::removeTailRec(Node *n)
{
//EDIT: Adding a check for n validity
if(!n){
//Here, you should have a way of detecting
//a call to your method with a null pointer
return 0;
}
Node* nextNode = n->next;
// check for the base case(s)
if(nextNode->next == nullptr)
{
//Get the next node value
int val = nextNode->value;
//Set the current node next member to nullptr
n->next = nullptr;
//Free the last node
delete nextNode;
return val;
}
else{
return removeTailRec(n->next);
}
// else call the recursive method
}
You are storing the result but not deleting it from linked list. You can return result in another variable (pointer : result).
Node* getTail(Node *n,int *result){
//u can even free the memory
if(!n->next)
{
result=n->value;
return NULL;
}
n->next=getTail(n->next,result);
}
or you can do it other way
int getTail(Node *n)
{
if(!n) return 0;
if(n->next)
{
if(!n->next->next)
{
Node *frnode=n->next;
int result=n->next->value;
n->next=NULL;
delete frnode;
return result;
}
getTail(n->next);
}
You are not removing last node in your code, and you leak another (temporary) node here.
To remove last node you have to zero the link in the previous node.
Your code should look like
...
if (n == NULL || n->next == NULL)
throw std::out_of_range("node");
if(n->next->next == NULL)
{
int val = n->next->value;
delete n->next;
n->next = NULL;
return val;
}
else ...
Be aware of the fact that c++ is not a functional language and has no optimizations for tail recursion, so in real application as your lists grow big enough you'll eventually have failure with stack overflow =) use Haskell or Erlang for this style of programming, in c++ use for or while.
You should set the Node n's previous Node's next field to NULL when n is the tail Node.
I am trying to create my own datatype that is like a vector or an array.
I am having troubles with my print function; When I go to print the list, it only prints the last item in the list.
// LinkedListClass.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
class Node
{
public:
int value;
Node* next;
Node::Node(int val)
{
value = val;
};
};
class List
{
public:
Node* firstNode;
Node* currentNode;
int size;
List::List()
{
firstNode = NULL;
currentNode = firstNode;
size = 0;
};
void push(Node* node)
{
if(firstNode == NULL)
{
firstNode = node;
firstNode->next = currentNode;
size++;
}
else
{
currentNode = node;
currentNode = currentNode->next;
size++;
}
};
void print()
{
if(firstNode != NULL)
{
Node* printNode = firstNode;
while(printNode->next != NULL)
{
std::cout << "List Item " << printNode->value << std::endl;
printNode = printNode->next;
}
}
};
};
int _tmain(int argc, _TCHAR* argv[])
{
List ll = List();
for(int i = 0; i < 10; ++i)
{
Node val = Node(i);
ll.push(&val);
}
std::cout << ll.firstNode->value << std::endl;
ll.print();
std::cout << "Size " << ll.size << std::endl;
std::cin.ignore();
return 0;
}
/* Output
9
Size 10
*/
I know this is nowhere near completed, but if you have any other pointers (lol), please feel free to suggest.
There are three important errors:
push() --- fixed
void push(Node* node)
{
if(firstNode == NULL)
{
firstNode = node;
currentNode = node;
// firstNode->next = currentNode; --> this does nothing useful!
size++;
}
else
{
currentNode->next = node;
currentNode = node;
//currentNode = node; -|
//currentNode = currentNode->next; -|----> why? what? Do explain.
size++;
}
}
I think by assigning firstNode->next = currentNode; you expected the next time currentNode was updated, it would update firstNode->next as well.
It doesn't work that way.
firstNode->next = currentNode; implies that the address stored in currentNode is now in firstNode->next. So next time you store something in currentNode = node; you're not storing it in firstNode->next. So you have a broken linked list --- which is why your output didn't go very far.
Also, this is really bad. By setting currentNode=node before setting the current node's next pointer to node, you've broken the list again. You should first point currentNode->next to node and then set the currentNode as node (node being the node which you're pushing onto your list).
Node val = Node(i);
The scope of val is only within that iteration of your loop. Once you loop around, it's off the stack and doesn't exist anymore. But you've copied the pointer of val to your list --- so now with the right push method, you're just adding a dangling pointer.
Node *val = new Node(i);
ll.push(val);
You need to put it on the heap so it stays on till you don't need it anymore.
... which leads us to your destructor!
Since you've allocated a node, you'll need to deallocate it. So do that in your destructor --- traverse your list and deallocate all those nodes.
The following lead to undefined behavior:
Node val = Node(i);
ll.push(&val); // take address of temporary
...
firstNode = node; // store address of temporary here
...
ll.print(); // temporary `val` was destroyed, but all nodes are point to it
You could change your code as follows:
Node* val = new Node(i);
ll.push( val );
And don't forget to delete all nodes later.
Your push() method is incorrect. The first time you push a node, it correctly assigns it to firstNode, but every subsequent push() just sets currentNode to the new node, and then sets currentNode to NULL -- you're not actually adding anything to your list.
I think it bears mentioning that pointers are not reference-by-name in C++. For instance, setting firstNode->next = currentNode doesn't make currentNode the next element in the list; it just makes firstNode->next point to the same address that currentNode does (in this case, NULL).
I'm not going to write the code for you, but here's how your push() function should work. The key is that you should be setting the 'next' field of an existing node to your new node, rather than currentNode to the new node:
In the case where firstNode is NULL,
set firstNode to the new node and
set firstNode->next to NULL (since
it has no next element). You can
also set currentNode = firstNode
here for convenience.
In the case where firstNode is not
NULL, we need to walk from firstNode
down the chain until we find a node
whose next field is NULL, then set
its next field to the new node.
Alternatively, we can use that
currentNode pointer to access the
last element in list and do the same
thing, being sure to set currentNode
to point to the new node when we're
done.
You basically have part 1 done, but part 2 still needs to be implemented. Feel free to ask for clarification/give criticism. :)
try it like Node* val=new Node(i)
previously u were storing the temporary variable. so no store the ndoe in dynamic memory so seprate memory can be given.
when u were creating the node it is create for temparary purpose
&temporary address were stored so when u traverse back the temporary memory had been released u will find there some garbage. value.