I am done with insertion, search in circular linked list but for removal I am getting compiler errors...
Following is my structure for nodes.
struct node
{
int p_data;
struct node* p_next;
node(node* head, int data)
{
p_next = head;
p_data = data;
}
explicit node(int data)
{
p_next = nullptr;
p_data = data;
}
};
node* remove_circular(node* head, node* target)
{
if (head == target->p_next)
{
delete head;
return nullptr;
}
auto next_pointer = target->p_next;
target->p_data = next_pointer->p_data;
target->p_next = next_pointer->p_next;
delete target->p_next;
return target;
}
and in main function I call
head = remove_circular(head, head);
head = remove_circular(head, temp);
this is to remove head element and another element that temp points to.
But I am getting errors
Anybody has any idea to remove one element from circular list??
I changed it to delete target->p_next;
but now it deletes everything in the list.
Any idea???
This is how a circular linked list works:
Each node points to the next in line, and the tail of the list points to the header node. That's the difference from a circular linked list to a regular linked list (which, in the case above, would make 37 point to a terminator null).
In the case of your list having only one object, then it should look something like this:
So, as you can see, there is no object pointing to null anywhere, yet it happens on your code with your explicit constructor (which will run if I write node n = node(12)).
I suggest you take a look at this link to have a better understanding of how your algorithm should look like.
Once you resolve your compiler error, you are still going to have algorithmic issues. I suggest you draw a circular list on paper and think about the steps required to remove an element. Consider all the cases, for example: empty list, list of 1 item, element not in the list, etc.
You need to consider several things.
1.) the case of an empty list
if(head == nullptr){//Empty list case
return nullptr;
}
2.) The target to be removed is the head node and this is the only node in the list.
if (head == target && target->p_next == head){
create a temp node with the data value of target
target = nullptr;//Since nothing points to target now it is for all intents and purposes deleted from the list but the data is still there so you can do something with it. I assume this is necessary because you return a node *.
return the temp node
}
3.) Create a loop that iterates through the entire list. You have something that would only delete the next node which works if you have a two item list and target was the second item.
auto next_pointer = head->p_next;//could not be target->p_next as this assumed
while (next_pointer->p_next != target){//This while loop traverses the list rather than just deleting the next entry.
4.)Inside you loop add a check to see if the list has been traversed and target was never found.
if (next_pointer->p_next == head){
return nullptr;
}//end IF
5.) Inside the loop add the else case which means target was in an arbitrary location in the list. Since I gave you the rest I'll leave you to get this part. It's not hard just a few lines longer than the statements above.
Related
I'm having a problem in a function that deletes an element in a singly linked list. The element needs to be deleted conditionnaly with the function ShouldDelete().
I haven't found a similar request in C++ in the forums so any help would be appreciated. It seems that the following code does not work 100% of the time.
void SinglyLinkedList::DeleteObjects(Node *pStartPtr)
{
Node *pPtr = pStartPtr;
Node *temp;
while(pPtr != nullptr)
{
temp = pPtr->next;
if (pPtr->ShouldDelete())
{
delete pPtr;
pPtr = temp;
if(temp != nullptr)
{
pPtr->next = temp->next;
}
}
pPtr = temp;
}
}
Any thoughts ?
Thanks!
As a general C++ advice, I would use std::list instead of a DIY list implementation, but since I wrote quite some list logic myself in the old C-days, I'll give it a go.
There are 2 essential problems with your code.
First of all, whenever removing an element from a single-linked list, you should set the next pointer of the previous element to the next of the deleted one. You can do something like this:
Node *previousNode = …;
while(pPtr != nullptr)
{
Node *nextNode = pPtr->next;
if (pPtr->ShouldDelete())
{
delete pPtr;
previousNode->next = nextNode;
}
else
{
previousNode = pPtr;
}
pPtr = nextNode;
}
Notice that no specific if-tests within the for-loop on nullptr are needed. You should tell the previous node that it now points to the next of the deleted one. If there is no next one, the previous->next will just point to a nullptr, indicating the end of the list.
The second problem is that you don't have any clear 'start of the list'. In my example code above, it becomes quite hard to indicate what the previousNode is. Is it a nullptr? But what if we remove the first element?
You could easily add logic here to indicate keep track of the first element, initialize previousNode to nullptr, and if the first is deleted, just don't set previousNode->next' and keep previousNode equal tonullptr` in that case, but what about the caller? How can the caller know that the first element has been deleted and the list now starts later.
My preferred solution is to have a separate List struct/class, and keep the start of the list (the first Node) as a member in the List struct/class. Instead of passing the startNode to the function, you should pass the List instance.
The code in the while-loop now becomes a bit longer, since you need to adapt previousNode->next=nextNode to this:
if (previousNode)
previousNode->next = nextNode;
else
list->firstNode = nextNode;
If you don't want to introduce a new List struct/class, you could also return the new firstNode as return value from your function, like this:
if (previousNode)
previousNode->next = nextNode;
else
startNode = nextNode;
…
return startNode;
Notice that this second approach only works if the caller if the function is also the one maintaining the list, and there are no other places pointing to the startNode.
In any case, if multithreading is involved, you probably want a decent List class with a kind of mutex (ideally std::list decorated with a mutex).
I'm currently taking data structures and algorithms class and turns out it is heavily geared on the concepts of linked lists. These are some things I have difficulty understanding:
How do I insert a number into data?
How do I move from one node to the next?
How do I call the Node class in main and print out the data values?
I have the following code. Am I doing it wrong?
#include <iostream>
using namespace std;
class LinkedList
{
class Node
{
public:
Node (int data, Node *n);
int data;
Node *next;
};
Node *head;
};
int main()
{
LinkedList::Node NodeObj;
NodeObj.data = 5;
cout <<NodeObj.data;
return 0;
}
A nice tutorial is at: http://www.zentut.com/c-tutorial/c-linked-list/
From a programming perspective, normally your LinkedList class would have some methods to do the things you asked about, for example:
Add - create a new entry at the end of the linked list
InsertAfter(Node *n) - create a new entry after indicated node in the listed list
Remove(Node *n) - removes the indicated node from the linked list
Count() - returns a count of the number of nodes within the linked list
Get(long i) - returns a pointer to the ith entry in the linked list
Find(some type of criteria) - return a pointer to the node that matches
Destroy - remove all the nodes in the linked list
Then your mainline just invokes these methods to utilize the linked list (the whole point of object encapsulation). Note that there is a LinkedList object instantiated, and it instantiates and manages the Node objects.
So if you had 10 numbers to store from some input array (inArray), you could do something like:
Node* n;
llObj = new LinkedList;
For (i=0; i<=9; i++) {
n = llObj.add();
n.data = inArray[i];
}
To step through the linked list, you would do something like:
For (i=0; i<=llObj.Count(); i++) {
n = llObj.get(i);
n.data = n.data + 1;
}
However, if you write yourself a .get() method from the code samples below, you will see that above code is terribly inefficient, and is not the ideal way to step through the entire linked list from mainline code.
To find the number 6:
n = llObj.find(6);
And so forth. Normally a linked list does not store just one data value such as in your example, but rather stores a structure or an object. Hence methods like Find become more useful because you can create Find methods that look at various fields in a structure or an object.
An Add method just traverses all the existing entries in the listed list until the last one is found, then creates a new entry, and links the former last entry to the now new last entry.
Node* LinkedList::add() {
void *n = NULL;
if (head != NULL) {
// one or more Nodes do exist
// first loop until we find the last-most node who's n.next == NULL
n = head;
while (n.next != NULL) n = n.next;
// found the last node, now allocate a new Node, and store a pointer to it in the formerly last node's .next property
n.next = new Node;
n = n.next;
// IMPORTANT: ensure this new last Node's .next is forced to be null
n.next = NULL;
}
else
{
// the header is NULL, so there is no first node yet
// allocate a new Node and store a pointer to it in the LinkedList header
head = new Node;
n = head;
// IMPORTANT: ensure this first Node's .next is forced to be null
n.next = NULL;
{
return n;
}
Note the While loop ... this is the key linked-list traversal mechanism. That loop checks the current node's .next field ... if it has a non-NULL pointer, then the loop cycles by copying that .next pointer to the loop pointer n, and tests again. Once the loop finds a node who's .next is NULL, then the lastmost node has been found, and the loop exits, with n containing the pointer to that lastmost node.
Note also the If statement concerning the .head property of the LinkedList class. One always has to do some special code for accounting for when the linked list is empty. There are a couple of ways of handling that; I chose the one that uses the least data memory.
Removing a node means just "skipping over it" in the linked list. We traverse the listed list until we find the one to remove, the we just "move back" its .next property to the prior entry's .next pointer. A good image is in the Linked List wikipedia entry:
A code example:
void LinkedList::remove(Node* nodeToRemove) {
// do nothing if we've been given a NULL pointer
if (nodeToRemove == NULL) return;
Node *n;
if (nodeToRemove == head) {
// the node to remove is the very first node, so set the head
// to the contents of the first node's .next property
head = n.next;
delete n;
return;
}
// need to find the indicated node; the following loop locates the
// node that is immediately before the node to be removed; note too
// that we have to test for the end of the linked list because the
// caller may have provided a bad pointer value
n = head;
while (n.next != NULL && n.next != nodeToRemove) n = n.next;
if (n.next == NULL) return; // reached end of linked list without finding the node
// good, the node immediately before the node to remove has been found!
Node* r = n.next; // this is inefficient code but want to make it very clear to newbies
n.next = r.next;
delete r;
}
Note that again we have to do some special logic concerning the LinkedList header. And do pardon the fact that I've used returns in the code; many finicky stickers would consider that a no-no. Also note in the code above, we don't need to do special logic to account for the end of the linked list, only just its beginning. If the node-to-remove is the last node in the linked list (and its r.next therefore == NULL), then the "n.next = r.next" line of code just moves the NULL back one position in the linked list, which is exactly what we would want.
You should be able to now figure out how to create all those other methods in your LinkedList class that I mentioned.
===============================
I do like someone's answer that unfortunately he deleted. For a 5 year old, a linked list is indeed a lot like the game of Treasure Hunt. In a Treasure Hint, you have to physically go to each location to get the clue to the next location. And in a linked list you have to access the location of a node to find the address of the location of the next node. A perfect analogy, and kudos for the answerer that first provided it.
I have a basic linked list problem that I have attempted to solve below. I would appreciate any inputs on my approach, correctness of the algorithm (and even coding style). The problem calls for a function that deletes all occurrences of an int in a circular linked list and returns any node from the list or NULL (when the list is null).
Here's some C++ code that I have so far:
struct Node{
Node* next;
int data;
};
Node* deleteNode(Node* &node, int num){
if(!node){
return NULL;
}
Node* given = node;
Node* del;
while(node->next != given){
if(node->next->data == num){
del = node->next;
node->next = node->next->next;
delete del;
}
node = node->next;
}
//Check if the first node needs to be deleted, with variable node pointing to last element
if(given->data == num){
node->next = given->next;
delete given;
}
return node;
}
The delete node; should be delete del;.
Also, use Node* node as parameter, instead of Node* &node which will prevent non-lvalues from passing in.
p.s. Forgot a semicolon after struct definition? :)
Without following all your logic I can see at a glance this code cannot work.
You are checking for the input list being empty and that's the only case in which your code returns NULL. But what happens if you are passed a list in which all elements must be deleted?
This problem also has a subtlety in it. To check if you completed a circular list you need to compare to the first address to see if you got linked back to the start. However if this element has been deleted then by C++ standard you're not even allowed to use its address in a comparison.
To avoid making two passes over the elements to be deleted one possible trick is to "break the loop" when starting iteration so you can check for NULL instead of checking for the address of the starting node.
!=I am currently working on the following erase recursive bool function that thakes list and int as arguments and return true if the int was found and deleted and false if it was not found in the list. It seems to work, but the problem is that it deletes the next int number in the list, and not the current:
typedef struct E_Type * List;
struct E_Type
{
int data;
List next = 0;
};
bool erase(const List & l, int data){
List current = l;
if (current == 0)
{
return false;
}
else if (current->data == data)
{
List deleteNode = new E_Type;
deleteNode = current->next;//probably this causes the error, but how can I point it to the current without crashing the program
current->next = deleteNode->next;
delete deleteNode;
return true;
}
else if (current->data != data)
{
return erase(current->next, data);
}
}
There are two basic type of lists:
single-linked lists (each node knows its next node) and
double-linked lists (each node knows its next as well as its previous node).
If, like in your case, one has a single-linked list, you must not check the CURRENT node for equality to 'data', because at that point it is too late to change the next pointer of the last node. So you always have to check the NEXT pointer for equality, like this:
bool erase(const List & l, int data)
{
List current = l;
if (current == 0)
return false;
// special case: node to be deleted is the first one
if (current->data == data)
{
delete current;
return true;
}
if (current->next && current->next->data == data) // next exists and must be erased
{
List deleteNode = current->next; // Step 1: save ptr to next
current->next = deleteNode->next; // Step 2: reassign current->next ptr
delete deleteNode; // Step 3: delete the node
return true;
}
return erase(current->next, data);
}
Note: I spared your last 'else if' condition. The 'else' because the previous if had a return in it, and the 'if' since its condition was just the negation of the previous 'if', which - if the program comes this far - would always hold.
Regards
The only node you're considering is the current one, so you must have a provision for modifying l:
if (current->data == data)
{
l = current->next;
delete current;
return true;
}
Here are some pointers.
An iterative approach
When you're iterating over your list, maintaining a pointer to the current element is not enough. You also need to maintain a pointer to the previous element, since you will need to fix up previous->next if you delete the current element.
On top of that, deleting the first element of the list will require special handling.
A recursive approach
Write a recursive function that will take a pointer to the head of the list, find & delete the required element, and return a pointer to the new head of the list. To do this, you need to:
Define and implement the base case. Handling one-element lists seems like a natural candidate.
Define the recursion. There are two cases: either the head of the list is the element you're looking for, or it isn't. Figure out what you need to do in both cases, and take it from there.
If you have a list:
A --> B --> C --> D
And you want to delete C, you have to:
Store C in a temp variable
Change B->next=C->next
delete C.
So you need to find B to be able to modify it.
You should certainly not create any new instance of E_type.
Your condition
else if (current->data == data)
will stop on the node which has the value data. You then go on to delete the node after this node in your code.
If you want to keep the rest of the code same, then that line should be :
else if ((current->next)->data == data)
with an extra check, in case the first element is the only element in the list.
A simpler way would be to keep a pointer that points to the element before the current element, and then deleting the node which is referred by that pointer.
You will need to change the next pointer of the preceding entry. So everything is find, but you have to check current->next->data against data, not current->data.
Be sure to check for NULL-pointers in case current is the last entry in the list!
When you delete a node from a list, you need to point the previous node to the next one. Since you have a singly linked list, there are 2 options:
Maintain a pointer to previous node in your erase function. When encountering desired node, link previous node to current->next and delete current node. Needs special treatment for the first node in the list.
When you encounter desired node, copy the content of the current->next into current, then delete current->next. This way you don't need an extra parameter in your function. Needs special treatment for the last node in the list.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
LinkedList “node jump”
I just need to have a linked list in order by name. I can only get it as far as 1st, 3rd, 5th, .. nodes. I just can't think this far. I want to be a C++ programmer but if I can't understand this is their any hope? STL containers std::lists are not an option for me at this point as a student. What you see in the list function is what I am TRYING to understanding.
list::node::node(const winery &winery) : item( winery.getName(), winery.getLocation(),
winery.getAcres(), winery.getRating() ), nextByName( NULL ), nextByRating( NULL )
{
}
void list::insert(const winery& winery)
{
node *current_node = new node( winery ); // but came here and did this so it has new info!
node *next_node = NULL;
node *tail_node = current_node;
if ( headByName == NULL ) // then we are here for the first item
{
headByName = current_node; // the list ptrs will have the first node's address.
headByRating = current_node;
}
while ( headByName->nextByName != NULL )
{
headByName->nextByName = tail_node;
tail_node = next_node;
//next_node = current_node;
}
tail_node = new node( winery );
headByName->nextByName = tail_node;
}
And the pointers that are available to me:
struct node
{
winery item;
node * nextByName;
node * nextByRating;
};
class list
{
...
private:
node * headByName;
node * headByRating;
};
Of course there's hope, we all have to start somewhere! :)
First of all, I must point out a dangerous practice in your implementation that my own students used, and it usually resulted in lots of head scratching to find the problem:
while ( headByName->nextByName != NULL )
{
headByName->nextByName = tail_node;
tail_node = next_node;
//next_node = current_node;
}
Don't use a list member like headByName as your iterating variable within your loop unless it is absolutely necessary. You should find the appropriate node first using a local variable, and then modify that node.
That said, Rob Kennedy is right that you should handle the name and rating separately (in other words a 'pure' implementation would have two instances of a generic list class that is unaware of what 'name' and 'rating' mean), however I assume the interfaces for list, node and function above were given to you in the assignment (if not, disregard the rest of my answer :) ).
Given the above interfaces, my approach would be as follows:
void list::insert(const winery& winery)
{
node* wineryNode = new node(winery);
assert (wineryNode);
// Find a node in the list with a name greater than wineryNode's
node* prevNode = NULL;
node* searchNode = headByName; // Note: not using the list member itself to iterate but a local variable
while (NULL != searchNode &&
searchNode.winery.getName() < wineryNode.winery.getName())
{
// Keep iterating through the list until there are no more items, or the right node is found
prevNode = searchNode;
searchNode = searchNode->nextByName;
}
/* At this point searchNode is either NULL
or it's name is greater than or equal to wineryNode's */
// Check for the case where the list was empty, or the first item was what we wanted
if (NULL == prevNode)
{
headByName = wineryNode;
}
else
{
// prevNode is not NULL, and it's Name is less wineryNode's
prevNode-> nextByName = wineryNode;
}
wineryNode->nextByName = searchNode;
/* Now you just need to insert sorted by rating using the same approach as above, except initialize searchNode to headByRating,
and compare and iterate by ratings in the while loop. Don't forget to reset prevNode to NULL */
}
You called this a doubly linked list, and although it's true that your nodes each have two links, this isn't really what most people think of when they hear about doubly linked lists. Since your two links are apparently unrelated to each other — nobody rates wineries alphabetically — it will be a little easier if you don't think of this as a doubly linked list.
The usual place to insert into a linked list, when a more specific order isn't required, is at the end of the list. The steps to do that are as follows:
Create a new node.
Find the place to insert the new node (i.e., the last node, at end of the list)
Update the last node's "next" pointer to point to the new node.
When you're inserting into what may be the middle of the list, as is the case for you, then there is another step:
2.5. Update the new node's "next" pointer.
The reason that step isn't usually there is that when inserting at the end of a list, the new node's "next" pointer is already null from when you constructed the new node object.
You've figured out the first step already. In fact, you've done it too well because your code actually creates two new nodes. One you store in current_node and the other you store in tail_node. Get rid of the second one.
Step 2 is about figuring out which node should be the one that immediately precedes the new node. When ordering by name, that would be the node that comes before the first node you find that has a name after the current name. You're going to have to walk along the list, possibly looking at every node, until you find one that belongs after the new node's name. As you move along the list, you're going to have to keep track of which node came before that node, too, because once you find the node you're looking for, you're going to have to backtrack.
Worry about the name and the rating separately. Don't try to solve both parts at once. Once you get the winery inserted correctly by name, then duplicate that code and replace "name" with "rating." But don't create another node object. Keep using the same one you created before.
As you've worked on this assignment, have you drawn any pictures of nodes with arrows pointing to other nodes? Try it. You've surely seen it done in your textbook or on your teacher's chalkboard. If a problem is too big for you to reason about entirely in your head, then use something to help you keep track of things outside your head. The professionals do it, too. Since each node has multiple pointers, you should either label the pointers or use different colors for name and rating.
To be a doubly-linked list each node must have a pointer to the node before and after it. You can also store a head and tail node if you like, but that would be more for personal convenience than to meet the requirements of a doubly-linked list.
I'm not sure I understand your question exactly. Going just from the topic "How do you insert into a sorted linked list?" I would do something like this (pseudocode):
list::insert(Winery winery) {
Winery node = getHeadNode();
// winery comes before the head node
if (winery.name < node.name) {
// the old head node now goes after this new node
// and this new node becomes the new head node
winery.next = node;
setHeadNode(winery);
return;
}
// let's iterate over the rest of the nodes in the list
Winery previous_node = node;
node = node.next;
while (node != NULL) {
// we found our insertion point
if (winery.name < node.name) {
// insert our new node
previous_node.next = winery;
winery.next = node;
return;
}
// insertion point not found, move to the next node and repeat
previous_node = node;
node = node.next;
}
// all elements visited, append our new element to the end of the list
previous_node.next = winery;
winery.next = NULL;
}
The above algorithm will insert your new node in the appropriate place in the list, assuming the list is sorted by name.