Component finding in graph BFS - c++

I have written the program in which I am making the adjacency list of a graph, which is working perfectly:
class graph {
private:
Q q;
int v; //no of vertixes
struct node* array; //array of vertixes
public:
...
};
Now I want to find the number of components in that graph. I have written this code, but it gives me a segmentation fault error at run time when it try to run findingcomponent() function.
void findingcomponents()
{
int compcount = 0;
int noOfcomp = 0;
node* curr;
for (int i = 0; i < v; i++)
{
curr = array[i].head;
if (curr->next != NULL)
{
while (curr)
{
//while loop for the connected nodes traversel
while (curr)
{
compcount++;
//loop for inner loop traversal and for enque data
if (curr->next->visited != true) //if the next loop of this parent loop is not visited.
{
q.EnQ(curr->next->nodeno);
curr->next->visited = true;
curr = curr->next;
}
else
{
curr = curr->next;
}
}
curr = array[q.deQ()].head;
}
noOfcomp++;
}
}
cout << endl << "There Are : " << noOfcomp << "components in this graph.";
}
Additional infos:
The nodes are defined as follows:
struct node {
int nodeno;
bool visited;
struct node* next;
struct node* head;
};
I use a Q class for managing a queue.
class Q
{
private:
node* front;
node* roar;
public:
...
int deQ()
{
node * temp = new node;
if (front == NULL){
cout << endl << "\t Queue is Empty";
return NULL;
}
else
{
temp = front;
front = front->next;
return temp->nodeno;
}
}
...
};

First problem:
When you look at the head of the adjacency list, here:
curr = array[i].head;
you can't be sure that the list is not empty (i.e. head is nullptr).
So when you write on the next line:
if (curr->next != NULL)
You might dereference a null pointer, which is undefined behaviour and hence might trigger a rutime error (when you're lucky). This happens whenever you have a node which has no edge.
Next problem:
In the inner while loop, you can be sure that curr != nullptr. This time dereferencing is ok, but you can't be sure that curr->next is not null. So when you write
if (curr->next->visited != true)
you might again dereference a null pointer and have undfined behaviour (for example a segfault, if you're lucky).
With the dataset that you've provided, this happens at the first node: the loop goes through the adjacency list, and on the last element the error is triggered.
Other potential problem
I have not analysed in depth your algorithm, as there is important code missing. But it is not clear to me how it is ensured that all enqueued values are dequeued, and that q.deQ() is not called when not called when the queue is empty.
Well, with the additional code provided, in your deQ() you return 0 (NULL) if the queue is empty. But you also return 0 when encountering the node 0. So you don't process correctly the end of the queue, risking to loop for ever.
Therefore you have to change this function to:
int deQ()
{
node * temp = new node;
if (front == nullptr) {
cout << endl << "\t Queue is Empty";
return -1; // <======!!!! Normally you should declare a constant for this
}
else {
temp = front;
front = front->next;
return temp->nodeno;
}
}
Wrapping it together: correction of your function
void findingcomponents()
{
int compcount = 0;
int noOfcomp = 0;
node* curr;
for (int i = 0; i < v; i++) {
curr = array[i].head;
if (curr == nullptr) { // make sure you don't hit isolated nodes
cout << "isolated node";
}
else if (curr->next != nullptr) {
while (curr) { //while loop for the connected nodes traversel
while (curr) {
compcount++;
//loop for inner loop traversal and for enque data
if (curr->next != nullptr && curr->next->visited != true) //!!!!!!if the next loop of this parent loop is not visited.
{
q.EnQ(curr->next->nodeno);
curr->next->visited = true;
}
curr = curr->next; //!!! this was called at the end of the if, and in an else: just call it systematically.
}
int nodeid = q.deQ(); //!!! here deque and
if (nodeid == -1) //!!! check if a node was found in the quue
curr = nullptr; //!!! if not
else curr = array[nodeid].head;
}
noOfcomp++;
}
}
cout << endl << "There Are : " << noOfcomp << "components in this graph.";
}
Please verify however if the number of components is correct. I suspect that you increase the counter in a wrong manner. But you have now 3 hours left to address it.

Related

Delete Nodes With The Value 0 In Singly Linked List In C++

I can't for the life of me figure this out I've spent days on this exercise but to no avail.
I'm trying to delete nodes with the value 0 from a singly liked list.
Let's say i have |1|3|0|4|0|5|0|0|. The outcome should be |1|3|4|5|
Here is all the code for reference
#include <iostream>
#include <fstream>
using namespace std;
struct node {
int data;
node* next;
};
node* head, *last;
int n;
void creating_list()
{
node* aux;
ifstream f("in.txt");
f >> n;
for(int i=0;i<n;i++)
{
if (head == NULL)
{
head = new node;
f >> head->data;
head->next = NULL;
last = head;
}
else
{
aux = new node;
f >> aux->data;
last->next = aux;
aux->next = NULL;
last = aux;
}
}
}
void displaying_list()
{
node* a;
a = head;
if (a == NULL)
cout << "List is empty! ";
else
{
cout << "Elements of list are: | ";
while (a)
{
cout << a->data<<" | ";
a = a->next;
}
}
}
void delete_first_node()
{
if (head == NULL)
cout << "List is empty";
else
{
cout << "Deleting first node\n";
node* aux;
aux = head;
head = head->next;
delete aux;
}
}
void delete_last_node()
{
if (head == NULL)
cout << "List is empty";
else
{
if (head == last)
{
delete head;
head = last = NULL;
}
else
{
node* current;
current = head;
while (current->next != last)
current = current->next;
delete current->next;
current->next = NULL;
last = current;
}
}
}
void delete_value_0()
{
node* aux;
if (head == NULL)
cout << "List is empty. Can't delete! ";
else
// if (head->data == 0)
// delete_first_node();
// if (last->data == 0)
// delete_last_node();
// else
{
node* a;
a = head;
while (a)
if (a->next->data != 0)
{
a = a->next;
cout << a->data<<" | ";
}
else
if (a->next != last)
{
aux = a->next;
a->next = a->next->next;
delete aux;
break;
}
}
}
int main()
{
creating_list();
displaying_list(); cout <<endl;
delete_value_0();
return 0;
}
Here is the problem that gives me metal problems
I've tried to move one node short of the node that has the 0 value, store the value in another node, aux in this case and delete aux;
I've put comment on those lines because if I don't and the condition it's met it doesn't execute the rest of the code...
If I put break at the end it only shows me the first few numbers until the 0 and then stops short, doesn't move through the full list.
if I don't put break the the program is doesn't stop, it's in an infinite loop, it doesn't exit with code 0
void delete_value_0()
{
node* aux;
if (head == NULL)
cout << "List is empty. Can't delete! ";
else
// if (head->data == 0)
// delete_first_node();
// if (last->data == 0)
// delete_last_node();
// else
{
node* a;
a = head;
while (a)
if (a->next->data != 0)
{
a = a->next;
cout << a->data<<" | ";
}
else
if (a->next != last)
{
aux = a->next;
a->next = a->next->next;
delete aux;
break;
}
}
}
Honestly I'm at a loss I've spent so much time trying to figure this out, and this should be a very simple exercise. I feel like the answear Is really simple but i don't know what to do anymore, Maybe this is not for me.
This is much simpler than it appears on the first glance. The trick to this task is instead of using a pointer to the current node, a pointer to the pointer to the current node gets used instead. The entire task becomes laughably trivial: only one loop, and one if statement that takes care of all possibilities: the list is empty; the node to delete is the first node in the list; ot the last node in the list; or anywhere in the middle of it.
void delete_value_0()
{
node **p= &head;
while (*p)
{
if ((*p)->data == 0)
{
node *nextptr=*p;
*p=(*p)->next;
delete nextptr;
}
else
{
p= &(*p)->next;
}
}
}
The naive solution is something like this:
void delete_value_0()
{
while (head && head->data == 0)
delete_first_node();
if (head == nullptr)
return;
node *cur = head->next;
node *pre = head;
while (cur)
{
if (cur->data == 0)
{
pre->next = cur->next;
delete cur;
cur = pre->next;
}
else
{
pre = cur;
cur = cur->next;
}
}
}
The key point is that you need to have a pointer to both the element you are inspecting and to the previous element in the list. This allows you to pull the current element out if it has data == 0.
The issue with this is that you have to treat the first element special (since it has no previous element).
My suggestion is to study this solution until you understand how it works, then move on to the (much better) solution by #Sam Varshavchik and study that - it does basically the same, but uses a pointer to pointer in a clever way to make the special cases here irrelevant.
I've put comment on those lines because if I don't and the condition it's met it doesn't execute the rest of the code...
OK why there the sketchy iteration is in else for if (last->data == 0)? Your input seems to have 0 as last item so in this case it would never be triggered. Also, if you want to have first/last items as special case, instead of
if (head->data == 0)
delete_first_node();
you would want something like
while (head && head->data == 0)
delete_first_node();
That being said, the real WTF is treating first/last item specially instead of using just single iteration. Also, you don't really check whether the pointers are non-null before trying to access the contents. With C (or C++ in the case you try it at some point) you need to take care with memory access when dealing with pointers.
Some random pieces of help:
You need to break from last item when it's 0 to exit loop simply because you don't assign a to the next item in this case.
If this is your schoolwork this might not be your fault, reading amount of items from the input file (assuming it was given part of the assignment) before actual items is huge WTF as you're reading into a linked list. There is no need to loop for any n items when you can be simply reading a line of input at the time until the file runs out.
Arguments and return values. You should learn those.
#include <iostream>
struct Node {
int data;
Node* next;
};
// Function to delete nodes with the value 0 in a singly linked list
void deleteNodes(Node** head) {
// Edge case: empty list
if (*head == nullptr) {
return;
}
// Delete all nodes with the value 0 at the beginning of the list
while (*head != nullptr && (*head)->data == 0) {
Node* temp = *head;
*head = (*head)->next;
delete temp;
}
// Edge case: list with only one node
if (*head == nullptr) {
return;
}
// Delete nodes with the value 0 in the rest of the list
Node* current = *head;
while (current->next != nullptr) {
if (current->next->data == 0) {
Node* temp = current->next;
current->next = temp->next;
delete temp;
} else {
current = current->next;
}
}
}
int main() {
// Create a singly linked list: 1 -> 0 -> 2 -> 0 -> 3 -> 0 -> 4
Node* head = new Node{1, new Node{0, new Node{2, new Node{0, new Node{3, new Node{0, new Node{4, nullptr}}}}}};
// Delete nodes with the value 0
deleteNodes(&head);
// Print the resulting list: 1 -> 2 -> 3 -> 4
Node* current = head;
while (current != nullptr) {
std::cout << current->data << " ";
current = current->next;
}
std::cout << std::endl;
return 0;
}
hope it help

Enqueue successful, but Queue prints nothing?

I am trying to enqueue data into a queue.
The enqueue appears to have been successful because the current Node's Thing pointer is not null. Indeed, its Thing id is 7 as expected. However, something is wrong with ThingQueue's print function because the exact same Node is identified as a nullptr. This is despite the fact that both the enqueue and print functions iterate through the queue in the same way. Can anybody explain the discrepancy?
Here is a minimal reproducible example of my problem:
#include <iostream>
using namespace std;
struct Thing
{
int id;
Thing()
{
id = 7;
}
void print()
{
cout << "Thing " << to_string(id) << " ";
}
};
class ThingQueue
{
public:
ThingQueue()
{
front = nullptr;
}
void enqueue(const Thing &thing)
{
cout << "Attempting to enqueue..." << endl;
Node *curr = front;
while (curr != nullptr) curr = curr->next;
curr = new Node;
curr->next = nullptr;
curr->thing = (Thing*) &thing;
cout << "Is curr->thing nullptr? 1 for true, 0 for false: ";
cout << to_string(curr->thing == nullptr) << endl;
cout << "curr->thing->id = " << to_string(curr->thing->id) << endl;
}
void print()
{
cout << "Attempting to print..." << endl;
Node *curr = front;
cout << "Is curr nullptr? 1 for true, 0 for false: ";
cout << to_string(curr == nullptr) << endl;
while (curr != nullptr) {
curr->thing->print();
curr = curr->next;
}
}
private:
struct Node
{
Node *next;
Thing *thing;
};
Node *front;
};
int main() {
Thing *pThing = new Thing();
ThingQueue *pThingQueue = new ThingQueue();
pThingQueue->enqueue(*pThing);
pThingQueue->print();
std::cout << std::endl;
return 0;
}
Output:
Attempting to enqueue...
Is curr->thing nullptr? 1 for true, 0 for false: 0
curr->thing->id = 7
Attempting to print...
Is curr nullptr? 1 for true, 0 for false: 1
SOLUTION:
Thanks for all the tips and feedback. After working out the remaining bugs and feedback, I have a minimum reproducible example that compiles, runs and produces the expected output! (Not worrying about memory management for this example)
#include <iostream>
using namespace std;
struct Thing
{
int id;
Thing()
{
id = 7;
}
void print()
{
cout << "Thing " << to_string(id) << " ";
}
};
class ThingQueue
{
public:
ThingQueue()
{
front = nullptr;
}
void enqueue(const Thing &thing)
{
if (front == nullptr)
{
// queue is empty, set front to new node
front = new Node;
front->next = nullptr;
front->thing = thing;
}
else
{
// queue is not empty, find last node
Node* curr = front;
while (curr->next != nullptr)
curr = curr->next;
// curr now points to the last node, set curr->next to new node
curr->next = new Node;
curr->next->next = nullptr;
curr->next->thing = thing;
}
}
void print()
{
Node *curr = front;
while (curr != nullptr) {
curr->thing.print();
curr = curr->next;
}
}
private:
struct Node
{
Node *next;
Thing thing;
};
Node *front;
};
class Stuff
{
};
int main() {
Thing thing = Thing();
Thing thing2 = Thing();
Thing thing3 = Thing();
ThingQueue tq = ThingQueue();
tq.enqueue(thing);
tq.enqueue(thing2);
tq.enqueue(thing3);
tq.print();
return 0;
}
Look at enqueue, it creates a new Node and sets curr to point at it. But curr is a variable in the enqueue function, it has nothing to do with your queue, as soon as the enqueue function is exited the curr variable is lost.
You also have another problem. For some reason you have made thing a pointer. This means you end up with pointers to variables which have been destroyed. Change thing to be a non-pointer.
Here's how enqueue should look.
void enqueue(const Thing &thing)
{
if (front == nullptr)
{
// queue is empty, set front to new node
front = new Node;
front->next = nullptr;
front->thing = thing;
}
else
{
// queue is not empty, find last node
Node* curr = front;
while (curr->next != null)
curr = curr->next;
// curr now points to the last node, set curr->next to new node
curr->next = new Node;
curr->next->next = nullptr;
curr->next->thing = thing;
}
}
I can see you are suffering from the pointers everywhere syndrome that beginners sometimes have.
Here's main rewritten without all the unnecessary pointers
int main() {
Thing thing;
ThingQueue thingQueue;
thingQueue.enqueue(thing);
thingQueue.print();
std::cout << std::endl;
return 0;
}
The only pointers you need in this code are the next field in your node class, the front member in your queue class, and the curr variable.

Freeing memory space when deleting a node in a circular LinkedList without declaring a new pointer

I've come across a problem in dynamic programming in which we are asked to delete nodes of a circular LinkedList, in the following manner.
Delete the first node then skip one and delete the next, then skip two and delete the next, then skip three and delete the next and it continues until we are left with only one node, and that one node is our answer.
For example, if we have 5 nodes, then the nodes will be deleted in the following order – 1 3
2 5 4, and the last node would be 4.
Similarly, if we have 4 nodes, then the nodes will be deleted in the following order – 1 3 4
2, and the last node would be 2.
This is a screenshot of the part of the code that requires improvement
using this code in c++, I've been successful in solving the problem but I want to free the memory using delete command as I delink a node. Can anyone please help me to solve this problem by improving this code (while using minimal memory)?
The node can be deleted by declaring another pointer, but that would only increase the memory usage, which I don't want at the moment.
The entire code is given below
#include<iostream>
using namespace std;
class linked {
public:
int x;
linked* next;
//methods
linked(int p); //constructor
static void insert(linked*& head, int p);//method to insert new node
static int print(linked* head);//method to print the result
static void del(linked*head, int size) {//method to delete all the undesired nodes
linked* temp = head;
while (temp->next != head) {//traversing until we find the node just behind the node we want to del
temp = temp->next;
}
for(int i=1;i < size;i++) {
for (int k = 1; k < i; k++) {//del nodes with increment
temp = temp->next;
}
temp->next = temp->next->next; //delinking the
}
}
};
int main() {
int no_of_nodes;
cout << "enter the number of nodes you want to have" << endl;
cin >> no_of_nodes;
linked* head = new linked(1);
for (int i = 1; i <= no_of_nodes; i++) {
linked::insert(head, i);//for inserting nodes, as desired by the user
}
linked::del(head, no_of_nodes);
cout<< linked::print(head);
}
linked::linked(int p) {
x = p;
next = NULL;
}
void linked::insert(linked*& head, int p) {
linked* temp = head;
linked* n = new linked(p);//for the new node
if (p == 1) {
head->next = head;
return;
}
while (temp->next != head) {
temp = temp->next;
}
temp->next = n;
n->next = head;
}
int linked::print(linked* head) {
linked* temp = head;
for (int i = 0; i < 25; i++) {//this can go longer(or shorter), i limited it to 25 only, just to ensure that it is a circular linked list
temp = temp->next;
if (temp == temp->next) {
return temp->x;
}
}
cout << endl;
}
P.S. The problem was taken from ICPC Asia Topi 2022, link: (https://giki.edu.pk/wp-content/uploads/2022/03/ICPC_Day_2.pdf)
It seems neither professional programmer are going to help you.:)
So we, beginners, should help each other.:)
You should declare a class of the circular singly-linked list with non-static member functions.
As for the task to remove all elements from the circular singly-linked list except one using the described algorithm then I can suggest the following approach.
At first within the function remove the cycling. This will make easy to remove elements from the circular singly-linked list.
After all elements except one will be removed then restore the cycling.
Here is a demonstration program.
#include <iostream>
#include <utility>
#include <stdexcept>
class CircularList
{
private:
struct Node
{
int data;
Node *next;
} *head = nullptr;
public:
CircularList() = default;
CircularList( const CircularList & ) = delete;
CircularList &operator =( const CircularList & ) = delete;
~CircularList()
{
clear();
}
void clear()
{
if (head)
{
Node *current = head;
do
{
delete std::exchange( current, current->next );
} while (current != head);
head = nullptr;
}
}
void insert( int data )
{
Node *new_node = new Node{ data };
if (not head)
{
new_node->next = new_node;
head = new_node;
}
else
{
Node *current = head;
while (current->next != head) current = current->next;
new_node->next = head;
current->next = new_node;
}
}
const int & top() const
{
if (not head)
{
throw std::out_of_range( "Error. The list is empty." );
}
return head->data;
}
void remove_except_one()
{
if (head)
{
Node *last = head;
while (last->next != head) last = last->next;
last->next = nullptr;
Node **current = &head;
for (size_t n = 0; head->next != nullptr; ++n)
{
for (size_t i = 0; i != n; i++)
{
current = &( *current )->next;
if (*current == NULL) current = &head;
}
Node *tmp = *current;
// The statement below is uncommented for the debug pyrpose.
std::cout << ( *current )->data << '\n';
*current = ( *current )->next;
if (*current == nullptr) current = &head;
delete tmp;
}
head->next = head;
}
}
friend std::ostream &operator <<( std::ostream &os, const CircularList &list )
{
if (list.head)
{
const Node *current = list.head;
do
{
os << current->data << " -> ";
current = current->next;
} while (current != list.head);
}
return os << "null";
}
};
int main()
{
CircularList list;
for (int i = 0; i < 5; i++)
{
list.insert( i + 1 );
}
std::cout << "The list: ";
std::cout << list << '\n';
list.remove_except_one();
std::cout << "The list: ";
std::cout << list << '\n';
list.clear();
std::cout << '\n';
for (int i = 0; i < 4; i++)
{
list.insert( i + 1 );
}
std::cout << "The list: ";
std::cout << list << '\n';
list.remove_except_one();
std::cout << "The list: ";
std::cout << list << '\n';
}
The program output is
The list: 1 -> 2 -> 3 -> 4 -> 5 -> null
1
3
2
5
The list: 4 -> null
The list: 1 -> 2 -> 3 -> 4 -> null
1
3
4
The list: 2 -> null
Within the function remove_except_one this statement
std::cout << ( *current )->data << '\n';
is present for the debug purpose only. You may remove or comment it if you want.
There are some problems with your code:
1) empty list should be nullptr
In main:
linked* head = new linked(1);
should be
linked* head = nullptr;
You start with an empty list. You do not know what data you will insert first and you assume the first value inserted will be 1. With this change you also have to change your insert:
if (p == 1) {
has to check
if (head == nullptr) {
2) replace head with tail
In a circular single linked list you always need the previous node to delete a node or to insert at the head. That means you have to traverse the whole list when given the head to find the previous. This is rather slow, so store the tail of the list instead. Then the head is tail->next and you can delete the head or insert at the head directly.
3) del breaks head
static void del(linked*head, int size) {
If this deletes the first node in the list then the head the caller passed in becomes a dangling pointer. There is no way to update the pointer the caller holds for the list. Just like with insert you need to pass in a reference:
static void del(linked*&head, int size) {
Now for your problem of how to delete the node without extra memory:
You can't. You always need extra memory to temporarily store the node to be deleted while you fix up the links in the list and then delete it. You already needed that extra memory to find the tail of the list and you called it temp.
static void del(linked*&tail) {
if (tail == nullptr) return; // no list, nothing to delete
for (std::size_t skip = 0; tail->next != tail; ++skip) { // keep going till only one node is left
for(std::size_t i = 0; i < skip; ++i) tail = tail->next; // skip nodes
// delete node
linked* temp = tail->next;
tail->next = tail->next->next;
delete temp;
}
}

How to delete nodes in the middle and end of a doubly linked list

I am trying to delete the odd numbers that are randomly generated (see oddsOnly), and then print the list in reverse order. I can delete odds that may pop up at the beginning of the list, but I cannot find a way to delete anything after that.
I have tried various forms of if and while loops. Too many different ways for me to remember or list them all.
#include<iostream>
#include<ctime>
#include<fstream>
using namespace std;
struct Node
{
int element;
Node *next;
Node *previous;
};
// Function headers
int takeNumber(int);
int writeFile(int);
struct Node *readFile(Node *&, int);
void printBackward(Node *);
struct Node *oddsOnly(Node *&, int);
void deleteList(Node *);
// Main
int main(int argc, char *argv[])
{
// Check to see if the user passed us the size to make the list.
if (argc == 2)
{
// Check to see if what the user passed us is in fact a valid number.
// Here we attempt to assign what was passes in to us to an int variable
int Size = atoi(argv[1]);
// Now we check a few things. If the !Size tells us whether or not the
// assignment above was successful. If the size is less than 20 or
// greater than 100, the input did not follow the instructions. In
// either case we ask the user to enter another number with the printed
// instructions of it must be between 20 and 100.
if (!Size || Size < 20 || Size > 100)
{
Size = takeNumber(Size);
}
// Create the pointer for Head. This is used to keep track of the beginning
// of the list.
Node *Head = new Node;
if (Head == NULL)
{
cout << "fatal error: could not create Head node." << endl;
return(1);
}
Head = NULL;
// If we are here, the head node was created successfully. As this is
// currently also the last node in the list, set the pointer to NULL.
//Head->next = NULL;
writeFile(Size);
readFile(Head, Size);
// When you allocate memory with new or malloc, you should always clean
// free up the memory when you are through.
deleteList(Head);
}
else
{
int Size = 0;
Size = takeNumber(Size);
// Create the pointer for Head. This is used to keep track of the beginning
// of the list.
Node *Head = new Node;
if (Head == NULL)
{
cout << "fatal error: could not create Head node." << endl;
return(1);
}
Head = NULL;
// If we are here, the head node was created successfully. As this is
// currently also the last node in the list, set the pointer to NULL.
//Head->next = NULL;
writeFile(Size);
readFile(Head, Size);
oddsOnly(Head, Size);
// When you allocate memory with new or malloc, you should always clean
// free up the memory when you are through.
deleteList(Head);
}
system("pause");
return 0;
}
// Function to take input (size) from the user
int takeNumber(int Size)
{
while (Size < 20 || Size > 100)
{
// Output specific instructions to the user.
cout << endl << "Please inter a number between 20 and 100 (inclusive). " << endl;
// Take a given input from the user.
cin >> Size;
// Set a while loop so that if the incorrect input is given, the user will
// be asked to enter a different input untill it fits the given discription
// in the instructions.
if (cin.fail() || Size < 20 || Size > 100)
{
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
// Print error message and ask for another input.
cout << "Input is not between 20 and 100 (inclusive). " << endl;
cout << "Please try again. " << endl << endl;
}
}
// Return the input.
return Size;
}
// Function to write random numbers to a binary file
int writeFile(int size)
{
// Create and open the file you will write to.
ofstream WriteFile;
WriteFile.open("numbers.bin", ios::out | ios::binary);
// Make sure to print an error message if the file was not created.
if (!WriteFile)
{
cout << "Could not create/open binary file for writing. " << endl << endl;
return (1);
}
// Seed the random fuction with the time from the computer.
srand(int(time(NULL)));
// create a variable to store the random variable gernerated.
int number = 0;
// Create a loop to generate some random data and store it in
// the number variable. Then write the number to the to the binary
// file.
for (int i = 0; i < size; i++)
{
// Generate a random number between 0 and 99.
number = (rand() % 100);
// Write the number to the binary file.
WriteFile.write(reinterpret_cast<const char *>(&number), sizeof(int));
}
WriteFile << endl;
// Close the binary file.
WriteFile.close();
return(0);
}
// Function to read the binary file and create a linked list.
struct Node *readFile(Node *&Head, int Size)
{
// Create and open a read binary file
ifstream ReadFile;
ReadFile.open("numbers.bin", ios::in | ios::binary);
// Check to make sure file has been created and opened.
if (!ReadFile)
{
cout << "Could not open the binary file for reading. " << endl << endl;
}
int Data = 0;
int counter = 1;
while (ReadFile)
{
ReadFile.read((char*)&Data, sizeof(int));
if (ReadFile.eof())
{
break;
}
if (Head == NULL)
{
Node *tmp = new Node;
if (tmp == NULL)
{
cout << "fatal error: could not create tmp node" << endl;
}
tmp->element = Data;
tmp->next = NULL;
tmp->previous = NULL;
Head = tmp;
counter = counter + 1;
}
else if (Head != NULL)
{
Node *current = new Node;
if (current == NULL)
{
cout << "fatal error: could not create current node" << endl;
}
current = Head;
while (current->next != NULL)
{
current = current->next;
}
struct Node *temp = new Node;
if (temp == NULL)
{
cout << "fatal error: could not create temp node" << endl;
}
temp->element = Data;
temp->next = NULL;
temp->previous = current;
current->next = temp;
counter = counter + 1;
}
}
cout << endl;
// Close the file
ReadFile.close();
oddsOnly(Head, Size);
printBackward(Head);
cout << endl;
return Head;
}
// Function to read the numbers.bin file and put numbers in reverse order
void printBackward(Node *Head)
{
// We now have a list. We can traverse the list and print the elements.
// We have the head, we create a current or tmp node pointer to set it
// equal to head.
Node *temp;
temp = Head;
while (temp->next != NULL)
{
temp = temp->next;
}
// We can use a counter to format the output on the console
int counter = 1;
// This is our loop to traverse the list. We start at head, and go until
// we reach the null pointer of the last node.
while (temp != NULL)
{
// This prints the number in the node, then a tab separator to the
// console.
cout << temp->element << "\t";
// This is used for formatting the output on the screen. In this case,
// after every five nodes have been printed, we insert a newline and
// reset the counter to 0.
if (counter % 5 == 0)
{
cout << endl;
counter = 0;
}
// We advance the tmp or current pointer to the next value and increment the counter.
temp = temp->previous;
counter = counter + 1;
}
}
// Function to weed out the odd numbers in the list
struct Node *oddsOnly(Node *&Head, int size)
{
int counter = 1;
while (Head->element % 2 == 0)
{
struct Node *temp = Head;
Head = Head->next;
Head->previous = NULL;
delete(temp);
return Head;
counter = counter + 1;
}
Node *tmp = Head;
while (tmp != NULL)
{
Node *current = Head;
while (current->element % 2 != 0 && current->next->next != NULL)
{
current = current->next;
tmp = current->next;
}
current->previous->next = current->next;
current->next->previous = current->previous;
delete(current);
struct Node *current1 = Head;
while (current1->next != NULL)
{
current1 = current1->next;
}
if (current1->element % 2 == 0)
{
current1->previous->next = NULL;
delete(current1);
}
tmp = tmp->next;
}
cout << endl;
return Head;
}
// Function to delete the link list.
void deleteList(Node *Head)
{
Node *tmp;
// Loop through the list, deleting one node at a time. We set tmp equal
// to the node after head, delete head and then set head equal to tmp.
while (Head != NULL)
{
tmp = Head->next;
delete(Head);
Head = tmp;
}
}
I am looking for a list that allows 5 integers before starting a new line, and that has only odd values from the linked list.
I have gotten a lot of error messages displaying 0xDDDDDDDD in the oddsOnly function. at this point though, I am only seeing that it is not deleting the evens after the first node in the list.
Refer to the following diagram:
To delete node B from the list, set A.Next to A.Next.Next, set C.Prev to C.Prev.Prev, and free node B.
To delete node D from the list, set C.Next to C.Next.Next (which is null), and free node D.
Make sure you write a function that does this. Your code suggests that you're not fully taking advantage of the encapsulation and "divide and conquer" strategy that functions provide.
Further Reading
Delete a node in a Doubly Linked List
Stealing Roberts great picture
I don't quite agree with his description (it is correct though just a bit complicated when written in English).
Node* x = findNodeToDelete();
if (x == nullptr) { // Check to make sure you have an actual node to delete.
return;
}
if (x->next != nullptr) {
x->next->prev = x->prev;
}
if (x->prev != nullptr) {
x->prev->next = x->next;
}
else {
Head = x->next;
}
// Now you have unlinked the node from the list.
x->next = nullptr; // Optional if node has a complex destructor may be worth it.
x->prev = nullptr; // Optional
// Now release the node you are deleting.
delete x;
As you know there are 3 situations in deleting being
1 - head
2 - middle
3- end
I think you covered the first one lest proceed to the second one
lets say you are in a traversing loop
while( cur != NULL ){
...
cur = cur -> next;
let's look at the middle of the node for second case
For deletion in the middle, you should know the previous node and the next node of to deleted node and you should bind them together. Luckily you both have previous and next in doubly linked list.
so lets add a statement to a code
if ( cur -> data == oddNumber ){
Node* prev = cur -> prev;
Node* next = cur -> next;
prev -> next = cur -> next;
delete cur;
cur = cur -> next;
}
that was for the middle, and for the end (3 rd case) you can simply check if the next node of the node is NULL and delete it peacefully.

Linked list search function modifying list

I am trying to implement a doubly linked list in C++ and the add function is working properly but the find node function is modifying the list.
All other function like insertAfter, delete depend on this find function and hence they are also not working as expected.
I am new to C++, so I don't completely understand pointers. I simply tried to replicate my Java program in C++. I know for sure that in the find function the pointer to the head node is causing the problem but I don't completely understand how.
Below is my code :
struct Node{
int data;
Node* next;
Node* prev;
Node(int d) {
data = d;
};
};
struct DLL {
Node* head;
Node* tail;
int size;
//Adding a Node to the Doubly LL
void addNode(Node* n) {
//If LL is empty add the first Node
if (tail == NULL) {
tail = n;
head = n;
}
//Else add add node to the tail. Connect n to the tails next and make n the tail
else {
tail->next = n;
n->prev = tail;
tail = n;
tail->next = NULL;
}
size++;
};
//Finding a random Node in the linked List
//It will return the Node with the FIRST occurrence where data = d
Node* findNode(int d) {
//We will start at the head and then traverse through the entire list to find a Node where data = d
Node* start = head;
if (start == NULL) {
cout<<"No element in the List" <<endl;
return NULL;
}
// If head is the Node we are looking for
if (start->data = d) {
cout<< "Node found with matching data : " << start << endl;
return start;
}
//While next pointer is not null, traverse to search for a match.s
while (start->next != NULL) {
start = start->next;
if (start->data == d) {
cout<< "Node found with matching data : " << start << endl;
return start;
}
}
cout << "No node found with matching data = " << d <<endl;
return NULL;
};
};
start->data = d
This line in your second if block is assigning d to start->data rather than comparing the two.
This is a good time to learn about constness.
Node* findNode(int d) {
//We will start at the head and then traverse through the entire list to find a Node where data = d
Node* start = head;
if (start == NULL) {
cout<<"No element in the List" <<endl;
return NULL;
}
// If head is the Node we are looking for
if (start->data = d) {
cout<< "Node found with matching data : " << start << endl;
return start;
}
This function has write access to the list, and you don't want that. Unfortunately, you abuse this access in the last if statement:
if (start->data = d) {
this code assigns the value of d to start->data and then tests if the value assigned to it was not null.
We can mark this function as const easily:
//////////////////////vvvvv/////////////////
Node* findNode(int d) const {
//We will start at the head and then traverse through the entire list to find a Node where data = d
Node* start = head;
if (start == NULL) {
cout<<"No element in the List" <<endl;
return NULL;
}
// If head is the Node we are looking for
if (start->data = d) {
cout<< "Node found with matching data : " << start << endl;
return start;
}
and now the if will generate a compiler error.
A cleaned up version of your code might look something like the following:
#include <iostream>
struct Node {
int data_;
Node* next_ { nullptr };
Node* prev_ { nullptr };
Node(int data) : data_(data) {}
};
struct DLL {
Node* head_ { nullptr };
Node* tail_ { nullptr };
int size_ { 0 };
//Adding a Node to the Doubly LL
void addNode(Node* node) {
//If LL is empty add the first Node
if (tail_ == nullptr) {
tail_ = node;
head_ = node;
node->prev_ = node->next_ = nullptr;
}
//Else add add node to the tail. Connect n to the tails next and make n the tail
else {
tail_->next_ = node;
node->prev_ = tail_;
tail_ = node;
node->next_ = nullptr;
}
size_++;
}
//Finding a random Node in the linked List
//It will return the Node with the FIRST occurrence where data = d
Node* findNode(int data) const {
//We will start at the head and then traverse through the entire list to find a Node where data = d
//While next pointer is not null, traverse to search for a match.s
for (Node* start = head_; start != nullptr; start = start->next_) {
if (start->data_ == data) {
std::cout << "Node found with matching data : " << start << '\n';
return start;
}
}
std::cout << "No node found with matching data = " << data << '\n';
return nullptr;
}
};
int main()
{
DLL dll;
Node n1(1), n3(3), n5(5);
dll.addNode(&n1);
dll.addNode(&n3);
dll.addNode(&n5);
if (dll.findNode(1) != &n1)
std::cerr << "wrong result for findNode(1)\n";
if (dll.findNode(2) != nullptr)
std::cerr << "wrong result for findNode(2)\n";
if (dll.findNode(3) != &n3)
std::cerr << "wrong result for findNode(3)\n";
if (dll.findNode(4) != nullptr)
std::cerr << "wrong result for findNode(4)\n";
if (dll.findNode(5) != &n5)
std::cerr << "wrong result for findNode(5)\n";
}
Live demo: http://ideone.com/X34EgY