I'm Trying to do a doubly linked list class that calls a Node class in C++
The Code for the Node class works just fine and I have the code for my class that works only when trying to print the values from least to greatest but if I try to print from greatest to least it only prints some values.
I also need guide on how to make a remove function that works similar to insert
Code of Node class:
Node.h
class Node {
public:
explicit Node(int data = 0, Node *nextPtr = nullptr, Node *beforePtr = nullptr);
int getData() const;
void setData(int data);
Node *getNextPtr() const;
void setNextPtr(Node *nextPtr);
Node *getBeforePtr() const;
void setBeforePtr(Node *beforePtr);
void print() const;
private:
int data;
Node *nextPtr;
Node *beforePtr;
};
Node.cpp
Node::Node(int data, Node *nextPtr, Node *beforePtr) : data(data), nextPtr(nextPtr), beforePtr(beforePtr) {}
int Node::getData() const {
return data;
}
void Node::setData(int data) {
Node::data = data;
}
Node *Node::getNextPtr() const {
return nextPtr;
}
void Node::setNextPtr(Node *nextPtr) {
Node::nextPtr = nextPtr;
}
Node *Node::getBeforePtr() const {
return beforePtr;
}
void Node::setBeforePtr(Node *beforePtr) {
Node::beforePtr = beforePtr;
}
void Node::print() const {
cout << getData() << endl;
}
MyList.h
class MyList {
public:
MyList(Node *currentPrt = nullptr);
void insert(int value);
void print() const;
private:
Node *currentPrt;
};
MyList.cpp
MyList::MyList(Node *currentPrt) {}
void MyList::insert(int value) {
if(currentPrt == nullptr){
currentPrt = new Node;
currentPrt->setData(value);
currentPrt->setNextPtr(nullptr);
currentPrt->setBeforePtr(nullptr);
}
else{
if(value > currentPrt->getData()){
while (currentPrt->getNextPtr() != nullptr && currentPrt->getNextPtr()->getData() < value){
currentPrt = currentPrt->getNextPtr();
}
Node *newPtr = new Node(value);
newPtr->setNextPtr(currentPrt->getNextPtr());
currentPrt->setNextPtr(newPtr);
newPtr->setBeforePtr(currentPrt);
}
else{
while (currentPrt->getBeforePtr() != nullptr && currentPrt->getBeforePtr()->getData() > value){
currentPrt = currentPrt->getBeforePtr();
}
Node *newPtr = new Node(value);
if (currentPrt->getBeforePtr() != nullptr){
currentPrt = currentPrt->getBeforePtr();
newPtr->setNextPtr(currentPrt->getNextPtr());
currentPrt->setNextPtr(newPtr);
newPtr->setBeforePtr(currentPrt);
}
else{
currentPrt->setBeforePtr(newPtr);
newPtr->setNextPtr(currentPrt);
}
}
}
}
void MyList::print() const {
Node *ptr;
ptr = currentPrt;
while(ptr->getNextPtr() != nullptr){
ptr = ptr->getNextPtr();
}
for (ptr; ptr != nullptr; ptr = ptr->getBeforePtr()){
cout << ptr->getData() << endl;
}
}
MyList test;
test.insert(5);
test.insert(3);
test.insert(2);
test.insert(1);
test.insert(2);
test.insert(7);
test.insert(8);
test.insert(6);
test.print();
Output:
8
7
5
3
2
1
and when the print function is this:
void MyList::print() const {
Node *ptr;
ptr = currentPrt;
while(ptr->getBeforePtr() != nullptr){
ptr = ptr->getBeforePtr();
}
for (ptr; ptr != nullptr; ptr = ptr->getNextPtr()){
cout << ptr->getData() << endl;
}
}
Output is: 1
2
2
3
5
6
7
8
as expected
Thank you for all the help
First of all, your code has undefined behaviour because of this constructor:
MyList::MyList(Node *currentPrt) {}
This leaves the currentPrt member uninitialised. It should be:
MyList::MyList(Node *currentPrt) : currentPrt(currentPrt) {}
The problem you have with the list order is caused by the insert method. There are two code blocks there where two next pointers are set, but only one before pointer, so in total three pointers are set. But there should be four of them in the general case.
This issue occurs in the second if block:
Node *newPtr = new Node(value);
newPtr->setNextPtr(currentPrt->getNextPtr());
currentPrt->setNextPtr(newPtr);
newPtr->setBeforePtr(currentPrt);
where newPtr->setNextPtr(currentPrt->getNextPtr()); is not mirrored by a link in the opposite direction. The correction is:
Node *newPtr = new Node(value);
newPtr->setNextPtr(currentPrt->getNextPtr());
if (currentPrt->getNextPtr() != nullptr) // <---
currentPrt->getNextPtr()->setBeforePtr(newPtr); // <---
currentPrt->setNextPtr(newPtr);
newPtr->setBeforePtr(currentPrt);
The same is going wrong in the deeper if block, where the if protection is not needed:
if (currentPrt->getBeforePtr() != nullptr){
currentPrt = currentPrt->getBeforePtr();
newPtr->setNextPtr(currentPrt->getNextPtr());
currentPrt->setNextPtr(newPtr);
newPtr->setBeforePtr(currentPrt);
}
The same statement is missing, and the correction can be:
if (currentPrt->getBeforePtr() != nullptr){
currentPrt = currentPrt->getBeforePtr();
newPtr->setNextPtr(currentPrt->getNextPtr());
currentPrt->getNextPtr()->setBeforePtr(newPtr); // <---
currentPrt->setNextPtr(newPtr);
newPtr->setBeforePtr(currentPrt);
}
Related
I am trying to remove a node from a linked list, when given the value of the node to remove.
Here is my code:
virtual void remove(T value) {
cout << "In remove" << endl;
if(value == my_list) {
Node *current = my_list;
my_list = my_list->next;
delete current;
}
else {
Node *ptr = my_list;
while (ptr != NULL) {
if(ptr == value) {
Node *current = ptr->next;
ptr->next = ptr->next->next;
delete current;
num_items--;
}
else{
ptr = ptr->next;
}
}
}
};
I keep receiving the error:
LinkedList.h:106:15: error: ISO C++ forbids comparison between pointer and integer [-fpermissive]
if(value == my_list) {
~~~~~~^~~~~~~~~~
LinkedList.h:114:21: error: ISO C++ forbids comparison between pointer and integer [-fpermissive]
if(ptr == value) {
I think my problem is "value" is the value of the node and not the position of the node, but I'm not sure how to change that.
EDITED:
Here is the declaration of all the variables:
template <class T>
class LinkedList: public LinkedListInterface<T>
{
private:
struct Node {
T data;
Node* next;
Node(const T& the_data, Node* next_val = NULL) :
data(the_data) {next = next_val;}
};
Node *my_list;
int num_items;
public:
LinkedList(void) {
my_list = NULL;
num_items = 0;
cout << "In constructor" << endl;
};
virtual ~LinkedList(void) {
cout << "In deconstructor" << endl;
while(my_list != NULL) {
Node *current = my_list;
my_list = my_list->next;
delete current;
};
};
unfortunately I don't have full insight of what "Node" contains.
If node contains "value" field then your code should look like this:
virtual void remove(T value) {
cout << "In remove" << endl;
// my_list replaced with my_list -> value
if(value == my_list->value) {
Node *current = my_list;
my_list = my_list->next;
delete current;
}
else {
Node *ptr = my_list;
while (ptr != NULL) {
// ptr replaced with ptr-> value
if(ptr->value == value) {
Node *current = ptr->next;
ptr->next = ptr->next->next;
delete current;
num_items--;
}
else{
ptr = ptr->next;
}
}
}
};
What you did wrong is, you compared address of node element with integer value.
I suppose something like that (but it's difficult because your code is not complete : what is the accessor for value exactly ?) :
virtual void remove(T value) {
cout << "In remove" << endl;
if(value == my_list->data) {
Node *current = my_list;
my_list = my_list->next;
delete current;
}
else {
Node *ptr = my_list;
while (ptr != NULL) {
if(ptr->data == value) {
Node *current = ptr->next;
ptr->next = ptr->next->next;
delete current;
num_items--;
}
else{
ptr = ptr->next;
}
}
}
};
I'm running some tests on a simple 3 node binary search tree. Root node has a value of 1 and its left and right children have values of 0 and 2, respectively.
Here's the source code (3 files):
File name: bst.cpp
#include <iostream>
#include "bst.h"
template <typename T>
void binary_search_tree<T>::insert(const T val2ins)
{
num_nodes++;
if(!root)
{
root = new tree_node<T>(val2ins, nullptr, nullptr, nullptr);
return;
}
//loop from root until we find where to insert val2ins; seek to find a suitable parent with a nullptr
auto curr_node = root;
tree_node<T> *prev_node = nullptr;
while(curr_node)
{
prev_node = curr_node;
if(val2ins >= curr_node->val) //assign equalities on right children
{
curr_node = curr_node->right;
}
else
{
curr_node = curr_node->left;
}
}
//prev_node is the parent of curr_node
curr_node = new tree_node<T>(val2ins, prev_node, nullptr, nullptr);
//curr_node now points to a tree_node that contains a pointer to to the previous node
//we also need to go to previous_node and set its left/right children to curr_node
if(curr_node->val < prev_node->val)
{
prev_node->left = curr_node;
}
else
{
prev_node->right = curr_node;
}
}
template <typename T>
tree_node<T> *binary_search_tree<T>::get_root()
{
return root;
}
File name: bst.h
#ifndef _BST_H_
#define _BST_H_
template<typename T>
struct tree_node
{
T val;
tree_node *parent;
tree_node *left;
tree_node *right;
tree_node() : val(0), parent(nullptr), left(nullptr), right(nullptr) {}
tree_node(T val2ins, tree_node *p_ptr, tree_node *l_ptr, tree_node *r_ptr)
{
val = val2ins;
parent = p_ptr;
left = l_ptr;
right = r_ptr;
}
};
template<typename T>
class binary_search_tree
{
private:
int num_nodes;
tree_node<T> *root;
//helper function for deletion
void transplant(const tree_node<T> *node2replace, const tree_node<T> *node2insert);
public:
binary_search_tree() : num_nodes(0), root(nullptr) {}
binary_search_tree(int N, tree_node<T> *ptr) : num_nodes(N), root(ptr) {}
void insert(const T val2ins);
void delete_node(const tree_node<T> *node2del);
tree_node<T> *get_root();
// void
};
#endif
File name: main.cpp
#include <iostream>
#include "bst.h"
#include "bst.cpp"
template <typename T>
class Solution {
public:
tree_node<T> *trimBST(tree_node<T> *root, int L, int R) {
search_and_delete(root, L, R);
return root;
}
void search_and_delete(tree_node<T> *&node, const int L, const int R)
{
if(!node)
{
return;
}
if(node && node->val >= L && node->val <= R)
{
trimBST(node->right, L, R);
std::cout << node->left << std::endl;
trimBST(node->left, L, R);
std::cout << node->left << std::endl;
std::cout << node->left << std::endl;
}
else if(node && node->val > R)
{
//delete right sub tree
//then check left sub tree
//Also need to delete current node and link left (if needed)
//this can be done by simply setting current node to its left
if(node->left == nullptr && node->right == nullptr)
{
delete node;
node = nullptr;
return;
}
if(node->right)
{
delete node->right;
node->right = nullptr;
}
if(node->left)
{
node = node->left;
}
}
else if(node && node->val < L)
{
//delete left sub tree
//then check right sub tree
//Also need to delete current node and link right (if needed)
//this can be done by simply setting current node to
//its right
if(node->left == nullptr && node->right == nullptr)
{
std::cout << "deleting node 0" << std::endl;
std::cout << "Address prior to freeing: " << node << std::endl;
delete node;
node = nullptr;
std::cout << "Address after freeing: " << node << std::endl;
return;
}
if(node->left)
{
delete node->left;
node->left = nullptr;
}
if(node->right)
{
node = node->right;
}
std::cout << "done" << std::endl;
}
std::cout << "end" << std::endl;
}
};
int main(int argc, char const *argv[])
{
/* code */
binary_search_tree<int> my_tree;
Solution<int> soln;
my_tree.insert(1);
my_tree.insert(0);
my_tree.insert(2);
soln.trimBST(my_tree.get_root(), 1, 2);
return 0;
}
When I execute this code I get the following output:
0x0
0x0
0x0
end
0x7fdeaec02af0
deleting node 0
Address prior to freeing: 0x7fdeaec02af0
Address after freeing: 0x0
0x7fdeaec02af0
0x7fdeaec02af0
end
The pointer pointing to the node with value 0 is being deleted during the recursive call and set to nullptr. However, when it returns from the recursive call (where the pointer was passed by reference), the pointer is still pointing to the same memory address as it did prior to being deleted and set to nullptr.
I cannot figure out why this is happening. My only guess is that I have a memory leak somewhere that's causing issues with the pointer that I supposedly applied delete to.
At first let me to say you that your node struct just should have one content and two pointer of himself that will show the right and left children.
then for showing the BST you should cout the data NOT this pointers
class node
{
friend class BST;
public:
node(int Content=0,node* R_child = NULL, node*L_child = NULL)
{
this->R_child = R_child;
this->L_child = L_child;
this->Content = Content;
}
private:
int Content;
node*R_child;
node*L_child;
};
check this code for node class you can use template instead of integer.
Good Luck
The following is a new programmer's attempt at a Queue. It seg faults in the Push() function, when I try to print the data in the first node. Looks like front_ptr is not actually getting set in head_insert. What am I doing wrong here, is this a completely wrong approach?
Thanks.
#include <cstdlib>
#include <iostream>
using namespace std;
class node {
public:
typedef double data_type;
node(data_type init_data = 0, node * init_next = NULL) {
data = init_data;
next_node = init_next;
}
void set_data(data_type new_data) { data = new_data; }
void set_next(node * new_next) { next_node = new_next; }
data_type get_data() { return data; }
node * get_next() { return next_node; }
private:
data_type data;
node * next_node;
};
void head_insert(node::data_type val, node* head_ptr) {
node* insert_ptr = new node(val, head_ptr);
head_ptr = insert_ptr;
}
void list_insert(node::data_type val, node* prev_ptr) {
node* insert_ptr = new node(val, prev_ptr->get_next());
prev_ptr->set_next(insert_ptr);
}
void head_remove(node* head_ptr) {
node* remove_ptr = head_ptr;
head_ptr = head_ptr->get_next();
delete remove_ptr;
}
void list_remove(node * prev_ptr) {
node* remove_ptr = prev_ptr->get_next();
prev_ptr->set_next(remove_ptr->get_next());
delete remove_ptr;
}
void list_clear(node* head_ptr) {
while (head_ptr != NULL) {
head_remove(head_ptr);
}
}
class queue {
public:
queue() {
size = 0;
front_ptr = NULL;
rear_ptr = NULL;
}
//~queue() {}
bool empty() { return (size == 0);}
void push(node::data_type val) {
if (empty()) {
head_insert(val, front_ptr);
cout << "Here: " << front_ptr->get_data() << endl;
rear_ptr = front_ptr;
}
else {
list_insert(val, rear_ptr);
}
size++;
}
void pop() {
if (!empty()) {
head_remove(front_ptr);
size--;
}
}
private:
node* front_ptr;
node* rear_ptr;
int size;
};
int main() {
cout << "START" << endl;
double testVal = 1;
queue* qList = new queue();
qList->push(testVal);
cout << "END" << endl;
return 0;
}
Any help is greatly appreciated.
Your front_ptr remains null pointer in push, because head_insert accepts it by value. Dereferencing null pointer then crashes the program. Make parameters that you want to be modified by a function reference parameters like void head_insert(node::data_type val, node*& head_ptr).
Also, you can avoid crash of null pointer dereferencing by checking it before, for example like that:
cout << "Here: " << (front_ptr ? front_ptr->get_data() : 0./0.) << endl;
This is in the main function. Using visual studio 2017.
list a;
a.insertAtEnd("i", 1);
a.insertAtEnd("love", 1);
Here in the main when second insert at end is called. The program crashes and says the getnext() is NULL. Even though at the creation of every new node the next pointer is declared NULL.
class node {
public:
node(string value) {
next = NULL;
data = value;
}
void setNext(node *temp) {
next = temp;
}
void setdata(string value) {
data = value;
}
node* getNext() {
return next;
}
Debugger shows this function to be at fault^
string getData() {
return data;
}
void createDetail() {
detail *tmp = new detail();
d = tmp;
}
void setDetail(int lin) {
d->insertAtEnd(lin);
}
void getDetails() {
d->print();
}
private:
node *next;
string data;
detail *d;
};
class list {
public:
list() {
head = NULL;
}
void insertAtEnd(string, int);
void insertAfter(string, string);
void display();
private:
node *head;
};
void list::insertAtEnd(string value, int lin) { //main func being used
if (head == NULL) {
node *temp = new node(value);
temp->createDetail();
temp->setDetail(lin);
head = temp;
}
else {
node *temp2 = head;
while (temp2->getNext() != NULL || temp2->getData()!=value)
{
temp2 = temp2->getNext();
}
if (temp2->getData() == value)
{
temp2->setDetail(lin); //if same line then increment frequency, dont create new detail as word exists
}
else
{
node *temp = new node(value);
temp->createDetail();
temp->setDetail(lin);
temp2->setNext(temp);
}
}
}
In
while (temp2->getNext() != NULL || temp2->getData() != value)
{
temp2 = temp2->getNext();
}
temp2->getNext() != NULL will be be NULL for the last item in the list, and the data in the last item in the list may not match value. In this case,
while (NULL != NULL || "I" != "love") // false or true = true. Enter loop
{
temp2 = NULL;
}
Next iteration fails because
while (NULL->getNext() != NULL || temp2->getData() != value)
{
temp2 = temp2->getNext();
}
Ka-blam.
My solution changes a lot of code. You might not like it. Also note I've removed everything that will not compile because it was left out of the question.
class node
{
friend class list; // list has access to node's private members
public:
node(string value)
{
next = NULL;
data = value;
}
// removed setNext. Only list should ever be allowed to set the next member
void setdata(string value) {
data = value;
}
// same deal for get. Some shmuck could delete link->getNext();, so why let them?
string getData()
{
return data;
}
private:
node *next;
string data;
};
This is a much safer list node. All a user can interact with is the data. The rest is safely locked up and only exposed to list.
class list
{
public:
list()
{
head = NULL;
}
void insertAtEnd(string);
private:
node *head;
};
Unchanged, other than the stuff removed because it supported code not included in the question.
void list::insertAtEnd(string value)
{ //main func being used
node **cur = &head; // double pointer abstracts away need to test for head.
// Now all nodes are equal and we're always pointed at a next.
while (*cur != NULL && (*cur)->getData() != value) // note && not ||
//we loop until out of nodes unless we find a match
{
cur = &(*cur)->next; // get pointer to next next
}
if (*cur != NULL) // pointing at a node. Must have exited because of match
{
// did stuff I ommtted because no MCVE
}
else // not pointing at node. Need a new node.
{
*cur = new node(value);
}
}
Bloodbath. Rather than drop a big ball of explanation here, I commented inline what I was doing and why.
I'm having problems with this code. I'm pretty sure it's in the swapping.
The line: curr->Data() = nextEl.Data() gives me the following error:
"expression must be a modifiable lvalue"
Any help is appreciated. Thank you in advance.
Here is the code for my bubble-sort algorithm:
class Node
{
private:
int data;
Node* next;
public:
Node() {};
void Set(int d) { data = d;};
void NextNum(Node* n) { next = n;};
int Data() {return data;};
Node* Next() {return next;};
};
class LinkedList
{
Node *head;
public:
LinkedList() {head = NULL;};
virtual ~LinkedList() {};
void Print();
void AddToTail(int data);
void SortNodes();
};
void LinkedList::SortNodes()
{
Node *curr = head;
Node *nextEl = curr ->Next();
Node *temp = NULL;
if(curr == NULL)
cout <<"There is nothing to sort..."<< endl;
else if(curr -> Next() == NULL)
cout << curr -> Data() << " - " << "NULL" << endl;
else
{
for(bool swap = true; swap;)
{
swap = false;
for(curr; curr != NULL; curr = curr ->Next())
{
if(curr ->Data() > nextEl ->Data())
{
temp = curr ->Data();
curr ->Data() = nextEl ->Data();
nextEl ->Data() = temp;
swap = true;
}
nextEl = nextEl ->Next();
}
}
}
curr = head;
do
{
cout << curr -> Data() << " - ";
curr = curr -> Next();
}
while ( curr != NULL);
cout <<"NULL"<< endl;
}
You are doing it wrong. You cannot change the value of temp variable returned by a function.
But you can make it work this way..
int& Data() {return data;};
though this is not good practise. Instead just use the setter you have..
curr->Set(nextEl->Data());
The statement
curr->Data() = nextEl.Data();
will never work, you are trying to assign something to the return value of a function. I don't know how you defined Node, but you probably meant something like
curr->Data = nextEl.Data();
i.e., assign something to a member of Node.
change
curr ->Data() = nextEl ->Data();
nextEl ->Data() = temp;
to
curr->Set(nextEl ->Data());
nextEl->Set(temp);