I have a problem with a linked list that I had to create.
The program should take in user input of names and then put them into a linked list. This list should be sorted alphabetically, so when a new node is added it should go to the right position.
My code looks like this for now:
struct node
{
string info;
node *next;
};
class Passenger
{
private:
node* pname;
public:
void insert(string);
Passenger();
};//closes Passenger class
Passenger::Passenger()
{
pname = new node;
pname -> info = "ABC";
pname -> next = NULL;
}
void Passenger::insert(string name)
{
node *temp, *p, *s;
p = pname;
s = pname;
temp = new node;
temp->info = name;
if(p-> info == "ABC") //new pname linked list, put temp at the front
{
p->info = name;
p->next = NULL;
}
//if there is already one in the list
while(s != NULL)
{
cout<<"inside while loop"<<endl;
//if new node goes to left
if(temp->info < s->info)
{
temp->next = p;
pname = temp;
}//closes if
if(temp->info > s->info)
{
if(p->next != NULL)
{
s = s->next;
if(s->next == NULL)
s->next = temp;
}//closes if
}//closes if
p = p->next;
s = s->next;
}//closes while
I really don't know how to change it that it works. I got it done when there was one node in the list and then add a second one. But if there are 2 nodes already there I don't know how to sort a third or fourth node in.
Dave
BTW, representing an empty list with a sentinel node containing "ABC" is ridiculous. What if someone wants to store a list that starts with that? Using a NULL pointer to represent an empty list is a near-universal convention.
Also, the nodes don't have to be a different type. The list class itself can be a node. So an instance of the class is just an instance of the first node, with a pointer to the rest of the list. Or, to better handle empty lists, you can have a container class that keeps pointers to the head (and optionally tail, for fast tail-insertion). Implementation helper-functions can be member functions of the nodes, not the container class, if you like, so you can call them on any sub-list.
When I was learning linked-lists, I often found it helped to draw circles for nodes and arrows for pointers on a piece of paper, like in the data structures textbook. Then you don't have to keep as much up-in-the-air mentally while you think through the process and any special-cases (beginning or end of the list).
You could break the problem down into two parts: the search, and the insert.
Your search function should return a pointer to the node before the insert position. i.e. the last node with a string that compares less than the one you're inserting. Your loop in that function then needs to either look ahead to the next element, or keep a prev pointer. Either way, you need to get back to the node before the one that stops the loop.
Inserting after a node is pretty easy. There's a special case when you need to insert a new head of the list, because there's no node to modify to point to the new current list. But you don't need to care whether the node you're inserting after was the end of the list or not.
You can reduce the amount of special cases by using a "dummy" node pointing to the actual first element. This trick might not work for a comparison-based insert, compared to an InsertNth. My answer on that question breaks things down into find and insert, and is written in a C-like style even though that was a Java code-review question. See the InsertAfter and InsertBefore methods.
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'm attempting to print the nodes in a linked list (forwards direction).
It's defined as follows:
struct Node {
string val;
Node* next;
Node* prev;
};
struct Stew {
Node* first;
Node* last;
};
Where Stew has two special pointers, one pointing to the first element, and one to the last.
I'm positive that what I attempted is correct, but it's actually not.
void print (const Stew& q, char direction) {
assert (!isEmpty(q));
{
Node* current = new Node;
current = q.first;
cout << current -> val;
while((current -> next) != NULL)
{
current = current -> next;
cout << (current -> val);
}
delete current;
}
I know there's a logic mistake in there, but I can't seem to pinpoint it. Any help would be appreciated.
Why do you do not read answers on your question? I already showed you how to print the list in the direct and reverse order. See here.
Implementing a push function in c++
I think your problem might be that you are treating your current pointer like it is an object. Current is a pointer that simply points to another object in memory. Hence your line Node *current = new Node is meaningless and you are losing that Node in memory. Your problem is probably the last line where you delete current. Current is pointing to the last item in your list and when you call delete you are freeing the memory current points to. Hence you are deleting the last object in your list. You only need to use delete when you are creating an object in memory, and current should not be a new item it should be a pointer to existing memory.
First:
Node* current = new Node;
That is totally unnecessary. You don't want to create a new node from the heap. All you want to do is point current node to the head node:
Node* current = q.first;
Then assuming that current is a valid node, and the next pointer will either point to the next valid node or NULL to denote the end of the list, your while() loop is starting off wrong. It should be something like this:
while(current != NULL)
{
cout << current->val;
current = current->next;
}
And of course, remove the call to "delete" at the end.
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.
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.