I am trying to reverse a linked list using recursion. I made the reverse() function to reverse the list. I created a linked list in main() and also defined print() method.
I don't know what mistake I am making. Please help me correct it. The code snippets are given below.
struct node
{
int data;
struct node *next;
}*head;
void reverse(node **firstnode,node *n)
{
if(n==NULL)
{
head=n;
return;
}
reverse(&head,n->next);
struct node *q=n->next;
n->next=q;
q->next=NULL;
}
void main()
{
......
head=first;
reverse(&first,first);
print(head);
}
It may not address your question directly. However, you mentioned C++11 in the tags. So, take look at std::forward_list. It is a standard container that is based on single linked-list.
List* recur_rlist(List* head)
{
List* result;
if(!(head && head->next))
return head;
result = recur_rlist(head->next);
head->next->next = head;
head->next = NULL;
return result;
}
void printList(List* head)
{
while(head != NULL) {
std::cout<<head->data<<" ";
head = head->next;
}
}
void main()
{
List* list = createNode(2);
append(list, createNode(3));
append(list, createNode(4));
append(list, createNode(5));
append(list, createNode(6));
List* revlist = recur_rlist(list);
printList(revlist);
}
I think you mixed up your addressing at the end of the reverse function, it should probably look like:
q->next=n;
n->next=NULL;
Also, I am not sure if you need the "firstnode" argument.
Since you want to understand the code, and you have several great resources with finished code already, more finished code examples aren't needed. I'll just answer with some concepts and point you at the errors you need to fix.
First, some background concepts.
Linked lists: first and rest
Any linked list is either empty, or can be broken down into first (a node) and rest (a smaller linked list, or empty). This makes recursion much easier.
if (head){
node * first = head;
node * rest = head->next;
}
Invariant (simplified): A guarantee that is always true at the start and end of your function.
In a linked list, you expect that head points to a node, which points to another node, and so forth, until you get to the end, which is signaled by a nullptr. All of the nodes are different. All of the nodes are valid. These are the guarantees that must be true before you call your function and when your function returns.
In a recursive function, the invariants must hold on the sublist you are reversing at every step, because you return from the function at every step. But this makes recursion much easier because all you have to do is make sure that if the input is good, then your function will return a good value at the current step.
End of recursion:
You can prove that your recursive function never gets in an infinite loop by combining the previous concepts. If the invariants hold, then each step will work, and because each recursive call will take rest, which is guaranteed to be either nullptr or a shorter list, eventually we have to reach the end. And of course show that you handle the end.
Okay, on to the actual problems:
You don't handle end of recursion correctly. You just set head=nullptr at the end, and I'm pretty sure that's not what you want for head. You may want to handle the end if (nullptr == n->next), because then you know that is the last node. Of course, you still have to correctly handle the trivial case where nullptr==head.
You don't preserve invariants. You tried, but it looks like your bookkeeping is just all wrong. I suggest using the debugger or 3x5 notecards to step through what you're actually doing to fix the actual work of swapping things around. For example, it looks like you just confused which node is which in this code snippet:
struct node *q=n->next; // n is first, q is rest
// what if nullptr == q?
n->next=q; // n->next = n->next doesn't actually change anything
q->next=NULL; // this must already be true if reverse(rest) did its job
// q and n were swapped?
Also, your function takes "firstnode" but does not use it, but instead sets the global variable "head" as a side effect.
Related
I am trying implementing the huffman algorithm following the steps described in this tutorial: https://www.programiz.com/dsa/huffman-coding, and so far I got this code:
void encode(string filename) {
List<HuffmanNode> priorityQueue;
List<Node<HuffmanNode>> encodeList;
BinaryTree<HuffmanNode> toEncode;
//Map<char, string> encodeTable;
fstream input;
input.open(filename, ios_base::in);
if (input.is_open()) {
char c;
while (!input.eof()) {
input.get(c);
HuffmanNode node;
node.data = c;
node.frequency = 1;
int pos = priorityQueue.find(node);
if(pos) {
HuffmanNode value = priorityQueue.get(pos)->getData();
value++;
priorityQueue.update(pos, value);
} else {
priorityQueue.insert(node);
}
}
}
input.close();
priorityQueue.sort();
for(int i=1; i<=priorityQueue.size(); i++)
encodeList.insert( priorityQueue.get(i) );
while(encodeList.size() > 1) {
Node<HuffmanNode> * left = new Node<HuffmanNode>(encodeList.get(1)->getData());
Node<HuffmanNode> * right = new Node<HuffmanNode>(encodeList.get(2)->getData());
HuffmanNode z;
z.data = 0;
z.frequency = left->getData().frequency + right->getData().frequency;
Node<HuffmanNode> z_node;
z_node.setData(z);
z_node.setPrevious(left);
z_node.setNext(right);
encodeList.remove(1);
encodeList.remove(1);
encodeList.insert(z_node);
}
Node<HuffmanNode> node_root = encodeList.get(1)->getData();
toEncode.setRoot(&node_root);
}
full code for the main.cpp here: https://pastebin.com/Uw5g9s7j.
When I try run this, the program read the bytes from the file, group each character by frequency and order the list, but when I try generate the huffman tree, I am unable to traverse this tree, always falling into a infinte loop (the method get stuck in the nodes containing the 2 first items from the priorityQueue above).
I tried the tree class with BinaryTree<int>, and everything works fine in this case, but with the code above the issue happens. The code for the tree is this (in the code, previous == left and next == right - I am using here the same Node class already implemented for my List class): https://pastebin.com/ZKLjuBc8.
The code for the List used in this example is: https://pastebin.com/Dprh1Pfa. And the code for the Node class used for both the List and the BinaryTree classes is: https://pastebin.com/ATLvYyft. Anyone can tell me what I am missing here? What I am getting wrong here?
UPDATE
I have tried a version using only c++ stl (with no custom List or BinaryTree implementations),but the same problem happened. The code is that: https://pastebin.com/q0wrVYBB.
Too many things to mention as comments so I'm using an answer, sorry:
So going top to bottom through the code:
Why are you defining all methods outside the class? That just makes the code so much harder to read and is much more work to type.
Node::Node()
NULL is C code, use nullptr. And why not use member initialization in the class?
class Node {
private:
T data{};
Node * previous{nullptr};
Node * next{nullptr};
...
Node::Node(Node * node) {
What is that supposed to be? You create a new node, copy the value and attach it to the existing list of Nodes like a Remora.
Is this supposed to replace the old Node? Be a move constructor?
Node::Node(T data)
Write
Node<T>::Node(T data_ = T{}) : data{data_} { }
and remove the default constructor. The member initialization from (1) initializes the remaining members.
Node::Node(T data, Node * previous, Node * next)
Again creating a Remora. This is not inserting into an existing list.
T Node::getData(), void Node::setData(T value)
If everyone can get and set data then just make it public. That will also mean it will work with cons Node<T>. Your functions are not const correct because you lack all the const versions.
Same for previous and next. But those should actually do something when you set the member. The node you point to should point back to you or made to do so:
void Node::setPrevious(Node * previous) {
// don't break an existing list
assert(this->previous == nullptr);
assert(previous->next == nullptr);
this->previous = previous;
previous->next = this;
}
Think about the copy and move constructors and assignment.
Follow the rule of 0/3/5: https://en.cppreference.com/w/cpp/language/rule_of_three . This goes for Node, List, ... all the classes.
List::List()
Simpler to use
Node<T> * first{nullptr};
List::~List()
You are deleting the elements of the list front to back, each time traversing the list from front till you find index number i. While horrible inefficient the front nodes have also already been deleted. This is "use after free".
void List::insert(T data)
this->first = new Node<T>();
this->first->setData(data);
just write
first = new Node<T>(data);
And if insert will append to the tail of the list then why not keep track of the tail so the insert runs in O(1)?
void List::update(int index, T data)
If you need access to a list by index that is a clear sign that you are using the wrong data structure. Use a vector, not a list, if you need this.
void List::remove(int index)
As mentioned in comments there are 2 memory leaks here. Also aux->next->previous still points at the deleted aux likely causing "use after free" later on.
int List::size()
Nothing wrong here, that's a first. But if you need this frequently you could keep track of the size of the list in the List class.
Node * List::get(int index)
Nothing wrong except the place where you use this has already freed the nodes so this blows up. Missing the const counterpart. And again a strong indication you should be using a vector.
void List::set(int index, Node * value)
What's this supposed to do? Replace the n-th node in a list with a new node? Insert the node at a specific position? What it actually does it follow the list for index steps and then assign the local variable aux the value of value. Meaning it does absolutely nothing, slowly.
int List::find(T data)
Why return an index? Why not return a reference to the node? Also const and non-const version.
void List::sort()
This code looks like a bubblesort. Assuming it wasn't totaly broken by all the previous issues, would be O(n^4). I'm assuming the if(jMin != i) is supposed to swap the two elements in the list. Well, it's not.
I'm giving up now. This is all just the support classes to implement the BinaryTree, which itself is just support. 565 lines of code before you even start with your actual problem and it seems a lot of it broken one way or another. None of it can work with the state Node and List are in. Especially with copy construction / copy assignment of lists.
I was reading this: https://www.geeksforgeeks.org/reverse-a-linked-list/
I think I found an easier answer but since it wasn't written their and they used more complicated one I think something is wrong with mine and can't figure it out.
We start from the first node which we will copy and insert it into a new list.
then we go one step to the right, copy the value, create a new list with that value and setting its right the previous list and so on.
What's wrong with my algorithm?
If I interpret your idea correctly, it is something like this:
void forward_list::reverse() {
forward_list new_list;
for(auto& v : *this)
new_list.emplace_front(std::move(v));
std::swap(new_list, *this); // or *this = std::move(new_list);
}
... and this would work. It even looks pretty nice I'd say.
What's wrong with my algorithm?
It's creates a new node for every old node and then has to copy/move the data from the old node to the new node. The old nodes will then be destroyed.
Some types aren't even copyable or moveable so this reverse algorithm couldn't be used with such types.
It invalidates references and iterators.
Consider the alternative, reversing the links. It's a bit more complex but gets the job done without any of the drawbacks mentioned above.
Here's my take on reversing the links, which is not implemented exactly the same way as in the link you shared but it works pretty much in the same way. I think this one has fewer assignments though.
curr will point one step ahead of head and next is used to save the next pointer when the relinking is being done.
void forward_list::reverse() {
if(head) { // must have at least one node
node* curr = head->next; // head + 1
head->next = nullptr; // this will be the new last node
node* next; // for saving next while relinking
while(curr) { // while curr != nullptr
next = curr->next; // save the next pointer
curr->next = head; // relink backwards
head = curr; // move head forward
curr = next; // move curr forward
}
// head now points at the new start of the list automatically
}
}
Your algorithm is functionally correct, but since you are creating an entirely new list instead of reversing the existing nodes in-place, you are using twice the memory. You also have to deal with the cleanup of deleting the old nodes once you have your new list.
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 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.
I am trying to insert nodes in a list based on the value of a data member. Basically, if the member isVip evaluates to true, that node gets precedence, and should be inserted ahead of any regular node (but behind any existing VIP nodes). Regular nodes simply get added at the end of the list.
I'm pretty sure I have a good idea of how to use two pointers to step through the list and insert elements for n > 2 where n is the number of current list members, but I'm sort of conceptually stuck for the case when there's only one node.
Here is my working version of code below:
void SelfStorageList::rentLocker(Locker e) {
int count = 0;
LockerNode *p = head;
if (isEmpty()) {
head = new LockerNode(e);
tail = head;
}
for(;p!=0;count++, p=p->next) {
if(count == 1) {
if (e.isVip) {
if(p->objLocker.isVip) {
LockerNode*p = new LockerNode(e, p->next);
}
}
}
}
As you can see, I'm checking to see if the passed in object is VIP, and then whether the current one is. Here, I've hit some trouble. Assuming both are VIP, will this line:
LockerNode*p = new LockerNode(e, p->next);
put the passed in locker object in the correct place (i.e. after the current VIP one). If so, would:
LockerNode*p = new LockerNode(e, p);
equivalently place it before? Is the use or absence of the 'next' member of the node what defines the placement location, or is it something entirely different?
Hope someone can clear my doubts, and sorry if it seem a foolish question! Thanks!
Simply iterate over the list while the next node have isVip set (current->next->isVip). After the iteration, the last node visited will be the last with isVip set, and you should insert the new node after that one.
It can be implemented in fewer lines, without the explicit isEmpty check, and without any counter. Even less than that if you use a standard container instead.