I'm making a C++ Maze program using disjoint sets and the Union/Find operations.
I have a MakeSet(int x) function which creates a new Node for every integer element x in the maze. (i.e 1 to 16 for a 4x4 maze). Thus initially every element is in its own set.
My MakeSet looks like this
void Maze::MakeSet(int x)
{
Node *root = new Node;
root->label = x;
root->parent = NULL;
}
But in the CreateMaze() function I have to call MakeSet many times to get all the elements into their own set initially. Thus, the root will keep being overwritten. How do I dynamically allocate many different nodes? Should I keep them separated in an array?
You already allocate a new Node, you are just not keeping it. You need to change the function to return Node*, and store the results in an array or a vector.
Node* Maze::MakeSet(int x) {
Node *root = new Node;
root->label = x;
root->parent = NULL;
return root;
}
Node *nodes[16];
for (int i = 0 ; i != 16 ; i++) {
nodes[i] = Maze::MakeSet(i);
}
An even better approach would be to do Node initialization in a constructor:
Node::Node(int x) : label(x), parent(NULL) {
}
You can now create all sets in a loop, and store pointers to them in an array:
Node *nodes[16];
for (int i = 0 ; i != 16 ; i++) {
nodes[i] = new Node(i);
}
You can have a std::vector of Node objects or pointers as a class member:
class Maze
{
std::vector<Node> nodes;
};
or, if you must
class Maze
{
std::vector<Node*> nodes;
}
and add the created nodes:
void Maze::MakeSet(int x)
{
Node *root = new Node;
root->label = x;
root->parent = NULL;
nodes.push_back(root);
}
Note that you'll have to implement a destructor to clean up the memory when you're done. This also means you should have a copy constructor and assignment operator for Maze.
Related
I am trying to learn constructors in c++. I am working on a list that I defined. I managed to get the copy constructor working, but I have problems with the array transfer constructor. Any help will be appreciated. Thanks!
The array transfer constructor supposedly should take in an array and a size(int) and output a list with that size.
ex: input: data = {1,3,5,6};int = 5;output = {1,3,5,6,0}
edit: change n to i
#include <iostream>
using namespace std;
class list_element{
public:
list_element(int n = 0,list_element* ptr = nullptr):
d(n),next(ptr){}
int d;
list_element* next;
};
class List{
public :
List():head(nullptr),cursor(nullptr){}
List(const int* arr, int n); // copy array transfer data
List(const List& lst); //copy constructor
void prepend(int n);
int get_element()
{
return cursor->d;
}
void advance() { cursor = cursor->next; }
void print();
~List(); //delete
private:
list_element* head;
list_element* cursor;
};
//transfer array
List::List(const int* arr, int n) {
List temp;
int i = 0;
while (i < n)
{
head = new list_element(arr[i], head);
++i;
}
}
//delete
List::~List(){
for (cursor = head; cursor != 0;)
{
cursor = head->next;
delete head;
head = cursor;
}
}
//deep copy constructor
List::List(const List& lst) {
if (lst.head == nullptr)
{
head = nullptr; cursor = nullptr;
}
else
{
cursor = lst.head;
list_element* h = new list_element();
list_element* previous;
head = h;
h->d = lst.head->d;
previous = h;
for (cursor = lst.head; cursor != 0;)
{
h = new list_element();
h->d = cursor->d;
previous->next = h;
cursor = cursor->next;
previous = h;
}
cursor = head;
}
}
void List::prepend(int n)
{
if (head == nullptr)
cursor = head = new list_element(n, head);
else
head = new list_element(n, head);
}
void List::print()
{
list_element* h = head;
while (h != 0)
{
cout << h->d << ',';
h = h->next;
}
cout << "###" << endl;
}
int main()
{
List a, b;
//change size
int data[10] = { 1,3,5,7};
List d(data, 10);
d.print();
return 0;
}
Main Question
With regards to your 'from_array' constructor, you have a temporary List variable that you are not using and is also unnecessary.
Second you are assigning the head pointer each time meaning that by the end of the constructor call, head now points to the last element you constructed.
Third your list_element constructor is taking the old head pointer which points to the previous element meaning the list is tries to advance from the bottom element upwards through the list, causing the reversed read you mentioned in a comment.
You can fix this two ways. First, you could reverse the order you read the input array so it constructs your linked-list back to front.
List::List(const int* arr, int n)
{
int i = n - 1;
list_element* it = new list_element(arr[i], nullptr); ///< Last element
--i;
while (i > -1)
{
it = new list_element(arr[i], it);
--i;
}
head = it; ///< assign head to the last instance of it.
it = nullptr;
}
However, there is a better why that expresses the linking of the elements more intuitively. First you need to pre-construct the next element and give it some default values (I didn't bother implementing a default constructor for list_element but you might want to.) and assign it to next, then assign head to a new list_element passing in the pointer to next. increment i so that you can assign next's value to the second value in the array. Finally incremenet i again so we can loop through the rest of the array. Finally, in the while loop, copy next into a variable called prev. Assign next to a new list_element with the value from the array and a nullptr. Finally assign prev->next to the new next pointer and increment i.
list_element::list_element()
List::List(const int* arr, int n)
{
int i = 0;
list_element* next = new list_element(0, nullptr);
head = new list_element(arr[i], next);
++i;
next->d = arr[i];
++i;
while (i < n)
{
list_element* prev = next;
next = new list_element(arr[i], nullptr);
prev->next = next;
++i;
}
}
Side Notes
Because you stated you are tying to learn about C++ constructors (and I'm assuming data structures) I would suggest starting with a static array type similar to std::array as it's constructors a bit more trivial to implement or even just start with simple classes/struct that just hold simple data like a few ints or whatnot as you can get an idea for the semantics around the various constructors in C++.
Also, the C++ standard library has two linked list types (std::list and std::foward_list)
Finally, you might be better off using a std::initializer_list instead of a raw array as this give you iterators to the data you want to copy which is a bit nicer to use.
Best of luck in your learning journey.
Try to avoid using variable names like 'n', which could be very confusing.
In your copy constructor for transferring the array, you should not access the array using 'n', which is the desired size of the transferred array, nor increment it.
Additionally, sizeof(arr) doesn't work as you would expect. You are querying the size of the pointer.
I am new to C++, and I'm sure this question must be easy to handle but I think I miss some main concepts. In the code, a Binary Tree is implemented, and we are asked to determine the number of nodes in that Binary Tree. As you can see in Node class, a Node has two member pointers: left that points to the Node at left, right that points to the Node at right. The input is the root node. Anyway, I found a solution like this:
Make a vector of Nodes, which I named nodes in the code, and append the root Node, and set the number of nodes (howManyNodes in the code) to 1.
While nodes is not empty (at the beginning we add the root node), check if it has a left and a right pointer. If they are available (i.e., not nullptr) , add those pointers to the vector of nodes (I use insert). At the same time, increase the number of nodes (howManyNodes in the code) by one.
After checking a if specific node has a left and right pointers, I remove that node from the list, for which I use pop_back() function.
At the end, the vector will be empty and I will obtain howManyNodes as the answer.
Here is the code, and I only implemented the count function. The rest is from the template.
#include <iostream>
#include <vector>
class Node {
public:
Node *left, *right;
Node() { left = right = nullptr; }
~Node() {
delete left;
left = nullptr;
delete right;
right = nullptr;
}
};
// I implement this part
int count(Node *n) {
int howManyNodes = 1;
std::vector<Node> *nodes = new std::vector<Node>;
nodes->push_back(*n);
while (!nodes->empty()){
Node* trial = new Node(nodes->back());
if (trial->left){
std::cout << trial->left << std::endl;
nodes->insert(nodes->begin(), *trial->left);
howManyNodes += 1;
}
if (trial->right){
std::cout << trial->right << std::endl;
nodes->insert(nodes->begin(), *trial->right);
howManyNodes += 1;
}
nodes->pop_back();
}
return howManyNodes;
}
// I implement this part.
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
// This should print a count of six nodes
std::cout << count(n) << std::endl;
// Deleting n is sufficient to delete the entire tree
// because this will trigger the recursively-defined
// destructor of the Node class.
delete n;
n = nullptr;
return 0;
}
The problem is, I can never get rid of segmentation fault. As I searched, it happens when the code is trying to access a memory it is not supposed to. My code might be easy to fix, but I have the following questions:
If std::vector uses heap memory for its members, why do I need to define a new vector here? In the main function (which I didn't write) everything is written by new, then I assumed I should also use new whenever possible but I don't understand the logic behind.
In my code, I want to use references, because I want to access only the pointers of Nodes and not modifying them - I learned that using the object itself requires making copies and slows down the process, so not preferable -. What part of my code is trying to modify any pointers?
Now that I defined a new vector, should I also delete it and make it equal to nullptr, before returning the value?
Thanks in advance!
The problem with your Node class is that it doesn't follow the rule of 3/5/0 and whenever you make a copy of a Node, and you are making a lot of copies, you have a trouble, because the left right nodes will be deleted once the copied object goes out of scope, and then again when you call delete yourself.
Short summary of bugs in your count implementation:
int count(Node *n) {
int howManyNodes = 1;
// doesn't make sense to allocate vector dynamically
// also you forgot to delete it
std::vector<Node> *nodes = new std::vector<Node>;
// keeping nodes as value will copy them, which leeds to double free
nodes->push_back(*n);
while (!nodes->empty()){
// another copy - thus another problem, but this time "only" a memory leak since you don't delete it
Node* trial = new Node(nodes->back());
if (trial->left){
std::cout << trial->left << std::endl;
// another copy
nodes->insert(nodes->begin(), *trial->left);
howManyNodes += 1;
}
if (trial->right){
std::cout << trial->right << std::endl;
// another copy
nodes->insert(nodes->begin(), *trial->right);
howManyNodes += 1;
}
nodes->pop_back();
}
return howManyNodes;
}
How it might be implemented without copying any objects:
void count(Node* n, int& numNodes)
{
numNodes++;
Node* left = n->left;
Node* right = n->right;
if (left) count(left, numNodes);
if (right) count(right, numNodes);
}
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
int numNodes{0};
count(n, numNodes);
std::cout << numNodes << std::endl;
delete n;
return 0;
}
It is a recursive approach, so maybe not the best. If you really need to implement some kind of a tree by hand, use std::unique_ptr, delete explicitly the copy constructor/assignment, and implement the method count inside your Tree class, if you plan to have sth like that.
As #pptaszni explained, in my code I was making copies of the instances and that was causing problems. The recursive approach suggested by #pptaszni is much easier and preferable, which I couldn't think of before. I also corrected my approach by passing pointers instead of values. This works:
#include <iostream>
#include <vector>
class Node {
public:
Node *left, *right;
Node() { left = right = nullptr; }
~Node() {
delete left;
left = nullptr;
delete right;
right = nullptr;
}
};
int count(Node *n) {
int howManyNodes = 1;
std::vector<Node*> nodes = {};
nodes.push_back(n);
while (!nodes.empty()){
Node* trial = nodes.back();
if (trial->left){
nodes.insert(nodes.begin(), trial->left);
howManyNodes += 1;
}
if (trial->right){
nodes.insert(nodes.begin(), trial->right);
howManyNodes += 1;
}
nodes.pop_back();
}
// Implement count() here.
return howManyNodes;
}
int main() {
Node *n = new Node();
n->left = new Node();
n->right = new Node();
n->right->left = new Node();
n->right->right = new Node();
n->right->right->right = new Node();
// This should print a count of six nodes
std::cout << count(n) << std::endl;
// Deleting n is sufficient to delete the entire tree
// because this will trigger the recursively-defined
// destructor of the Node class.
delete n;
n = nullptr;
return 0;
}
I have created an Octree data structure but it's not perfect yet. I'm struggled with the copy constructor and the destructor.
Here is my header file:
class Octree
{
public:
static int lastbranch;
static bool utolsoelotti;
struct node
{
int value;
node *child[8];
};
Octree();
~Octree();
Octree(const Octree& oct);
void clear(node* node);
node* searchandset(int dec, int value);
node* search(int dec);
node* step(node *node, int k);
node* copy(node *n);
void Print(node *n)const;
void deletebranch(int branch);
node *root;
};
Constructor,destructor,copy contrsuctor
Octree::Octree()
{
root = new node;
root->value = 0;
for (int i = 0; i < 8; i++)
root->child[i] = 0;
}
Octree::~Octree()
{
clear(root);
}
Octree::Octree(const Octree& oct) {
root = copy(oct.root);
}
void Octree::clear(node *node){
for (int i = 0; i < 8; i++)
if (node->child[i])
clear(node->child[i]);
delete node;
}
Octree::node*Octree::copy(node *n) {
node* n2 = new node;
if (n) {
for (int i = 0; i < 8; i++) {
n2->child[i] = copy(n->child[i]);
}
}
return n2;
}
And here is how I created objects in the main:
int main() {
Octree tree;
Octree tree2(tree);
tree.searchandset(8, 2);
tree2.Print(tree2.search(8));
return 0;
}
In the searchandset function I'm giving a value for node number 8 at the first tree. After that I'm calling the copy constructor and print the 8th node of the second tree. The value is the same what I gave for the first tree, but when the desctructor called I always got this exception:
Exception thrown: read access violation.
node was 0xDDDDDDDD.
As I know it means that I tried to delete the nodes which I have already deleted. The object 'tree2' is a different object from 'tree' with the same values and nodes isn't it? Then I don't understand that exception above.
I'm new in c++ and I know it's basic stuff, so if somebody would direct me into the right direction I would very appreciate it.
The problem lies the in copy function. Let's go it through step by step:
node* n2 = new node;
if (n) {
for (int i = 0; i < 8; i++)
n2->child[i] = copy(n->child[i]);
}
return n2;
For an empty Octree oct, constructed with the default constructor, and copied to another Octree:
A new node is created, n2
n is the root of oct, so the condition if true
child[i] of n2 has a value of a copy of the corresponding child, so call copy again
A new node n2 is created
n is nullptr (because the children where all nullptr in oct), so don't execute condition
Return n2
Repeat steps 3 to 6 8 times
Return the root n2
Assign new pointer (n2) to root of the copied object
But wait! Did you notice that in step 6, you are returning a new pointer, even though the child is supposed to be nullptr!
That's the problem, because then, in clear, you will loop through each child. That's still ok, right? But then, you try to access the children, which are uninitialized (they have random value, the condition will evaluate to true), so you get a Read access violation, because it's not your memory.
So the solution? Only allocate memory for n2 if n is not nullptr.
So I've been trying to create a class that handles 1000 linked lists, and initially declares pointers to them.
This is the code that deals directly with my issues:
struct node
{
char name[40];
char numb[12];
node * next;
};
class hashTable
{
public:
//Creates a table of 1000 pointers to linked-list nodes
node * table[1000];
//Functions
void addNode(char name[40], char numb[12])
{
node * temp; //Initializes temp node as pointer
temp = new node; //Points temp node to a new node
int hash = h(g(name)); //The hash of the key (name) used to check nodes
temp = table[hash]; //sets the temporary node to the first node of the list
while (temp->next != 0)
{
//...
Right at the while loop is where I get the error "Access violation reading location 0xcccccd00"
I'm not sure why it can't access the table member, unless perhaps it is because these values have not been initialized or anything?
You're likely not doing two things. First make sure your hash table is properly initialized to contain all-NULL-pointers. Secondly, make sure any pointer retrieved from the hash table is valid prior to dereferencing it:
For the first issue:
hashTable::hashTable() : table()
{
}
Also, you want to make sure this thing cleans up properly
hashTable::~hashTable()
{
for (size_t i=0;i<sizeof(table)/sizeof(table[0]); ++i)
{
node *temp = table[i];
while (temp)
{
node *victim = temp;
temp = temp->next;
delete victim;
}
}
}
For the second issue:
void addNode(const char *name, const char *numb)
{
int hash = h(g(name)); //The hash of the key (name) used to check nodes
node *temp = table[hash]; //sets the temporary node to the first node of the list
if (temp)
{
// preexisting entry. walk that list looking for matching key.
node **pp = &temp->next;
while (temp)
{
if (0 == strcmp(temp->name, name))
break;
pp = &temp->next;
temp = temp->next;
}
// link to last node if not found in list
if (!temp)
*pp = new node(name, numb);
}
else
{ // no prior entry. create a new one and store it at table[hash].
table[hash] = new node(name, numb);
}
}
Note: the above code assumes the node class is implemented as
struct node
{
char name[40];
char numb[12];
node * next;
node(const char* name_, const char *numb_)
: next()
{
strncpy(name, name_, sizeof(name)/sizeof(name[0])-1);
name[ sizeof(name)/sizeof(name[0])-1 ] = 0;
strncpy(numb, numb_, sizeof(numb)/sizeof(numb[0])-1);
numb[ sizeof(numb)/sizeof(numb[0])-1 ] = 0;
}
};
Personally, I'd use std::string
If the value of hash is greater than (or equal to) 1000, temp will point to an invalid area.
And you are leaking the memory allocated by new node since you are overwriting the temp variable.
So I want to copy a whole linked list classes, I have trouble figuring it out how to do so,
class list{
public:
list(const list &t);
private:
struct Node{
int x;
Node *next;
}*p;
I started with something like this:
list::list(const list &t){
Node* q;
q=new Node;
while (p!=NULL){
q->x= p->x;}
}
but I'm not sure if I am on the right track or what. I also have trouble how should I test such a copy constructor? For example I have list l1, then i insert couple integers into a list and then how I can copy it?
In your example it never will work if you initialized p or will work forever if p != NULL. You must allocate new nodes while traversing through t list:
p = NULL;
Node* copy = l.p;
Node* insert = p;
Node* inserted_el = NULL;
while (copy){
insert = new Node();
insert->x = copy->x;
insert->next = NULL;
if (inserted_el) {
inserted_el->next = insert; //copy memory pointer to next element
} else {
p = insert; //copy memory pointer to list head
}
copy = copy->next;
inserted_el = insert;
}
This is basic idea. Also don't forget to implement assign operator and destructor.
Usage:
list t1;
//insert nodes
list t2(t1);
The biggest trouble in your code is that you do not duplicate each node of the list while you need to do so.
Here is the code of the ctor:
list::list(const list &t)
{
p = NULL; // Init the head of the list this is vital important.
// Loop over the elements of the passed list if any.
Node *pt = t.p;
Node *last_local_element = NULL;
while (pt != NULL)
{
// Allocate a new node and set the fields there.
Node *q = new Node;
q->x= pt->x;
q->next = NULL;
// Add new node to the local list.
if (last_local_element != NULL) {
last_local_element->next = q;
} else {
p = q;
}
last_local_element = q;
// Shift the loop variable along the passed list.
pt = pt->next;
}
}
There are 2 most often cases when the copy ctor is called:
list my_list1;
list my_list2(my_listl); // Explicit call.
list my_list3 = my_listl; // Assignment in the definition statement.
With your design of class, you need to be careful with memory management. This is the code:
list::list(const list& t) {
Node* n = t.p;
Node* m = p;
while (n) {
if (!m) {
m = new Node(); // Allocate memory.
if (!p) p = m;
}
m->x = n->x;
m = m->next;
n = n->next;
}
if (m) { // Original list is longer, delete the rest of the list.
Node * tmp = m;
m = m->next;
delete tmp;
}
}