How to clean up a Minimum Spanning Tree - c++

I am working on a project that uses an MST using the prim algorithm:
After completing my code and testing I was trying to clean and deallocate my calculated prim function, but I complicated my code with a pointer class to a different pointer.
Here my code with my logic to deallocate the code:
Adj_Node *curr_element;
Adj_Node *next_element;
for (int i = 0; i < n_nodes; i++)
{
curr_element = array_elements[i].head_list;
while (curr_element != nullptr)
{
std::cout << curr_element.dest_node << "\t";
next_element = curr_element[i].next_node;
delete curr_element;
curr_element = next_element;
}
}
Here some variables from my classes:
Adj_List * array_elements; Adj_Node *head_list; Adj_Node *next_node;
Basically, array_elements is an array of head lists, and the head list is an array of the next node.
I followed this tutorial for the Adj_List *: https://www.geeksforgeeks.org/prims-mst-for-adjacency-list-representation-greedy-algo-6/
If you can help me with how to deallocate this element which is the same as the element of the tutorial I will appreciate it (basically a function clean for the given tutorial). Thank you for your help in advance.
Edith:
Thanks for answering my question. I think I forgot to mention I am using classes instead of structures from the tutorial code into different files like adj_node adj_list. The problem is that I try to deallocate two pointers, say (0,1);(1;0), and when I run the code it scapes from the execution program. I am unsure of the reason.
This is what I have for adding, so maybe there is something there that can clarify how I allocate memory
Adj_Node *new_node = new Adj_Node(v, w);
// Store head_list of array_heap elements into head_list
new_node->next_node = array_elements[u].head_list;
// And store array_heap to node
array_elements[u].head_list = new_node;
// Since graph is undirected, add an edge from dest to src also
new_node = new Adj_Node(u, w);
new_node->next_node = array_elements[v].head_list;
array_elements[v].head_list = new_node;
I think the problem is that I create a linkedlist array_elements[v].headlist->array_elements[v].headlist-array_elements[v], but I am unsure. Happy to know what you think.

When the nodes form a connected graph, there will be nodes having multiple references to them. If you walk overlapping paths across the graph "deleting as you go" you will cause multiple deletions to be attempted on the nodes, which will cause a segmentation fault (when attempting to delete a node that is already deleted).
You have a few options:
Maintain a node list whilst building the graph - this has ownership of the nodes and is responsible for deleting them at cleanup. A std::vector can be a good candidate for this. (This option would be my recommendation)
Walk the nodes at cleanup and build a list, avoiding duplicates. When all paths walked, delete everything in the list.
Use smart pointers (std::shared_ptr) for creating the nodes and copy them in the nodes for referencing the "adjacent-node". The references to them can be cleared when walking the paths at cleanup. The nodes are not deleted until all references are reset.
For more information on dangling references and smart pointers, take a look at:
this paper: http://umich.edu/~eecs381/handouts/C++11_smart_ptrs.pdf
this article: https://www.acodersjourney.com/top-10-dumb-mistakes-avoid-c-11-smart-pointers/
Scott Meyer's book: https://www.amazon.co.uk/Effective-Modern-Specific-Ways-Improve/dp/1491903996

Related

Can I delete array element by element?

I'm trying to create custom list. This is the node structure:
struct Node {
ListNode* prev;
ListNode* next;
void* data;
};
One of constructors creates list from array. So I decided to allocate nodes in consecutive part of memory to make algorithm a little bit faster.
auto elements = new Node[size];
elements[0] = ...
elements[size - 1] = ...
for (int i = 1; i + 1 < size; i++) {
elements[i].data = array[i];
elements[i].prev = &elements[i - 1];
elements[i].next = &elements[i + 1];
}
head = &elements[0];
tail = &elements[size - 1];
After that I can add new elements:
Node* tmp = new Node;
tmp->prev = tail;
tmp->data = data;
tail = tmp;
Also I can change next and prev.
So I can't distinguish elements(is this element part of array or have been allocated later using new) and in destructor I have to delete elements using delete instead of delete[].
Node* curNode = head;
for (int i = 0; i < size; i++) {
Node* tmp = curNode.next;
delete curNode;
curNode = tmp;
}
This code doesn't delete elements which have been allocated in array(according to Valgrind).
How can I allocate nodes in one array(to decrease number of cache misses) and then successfully delete them element by element?
What you are trying to do is the most hacky implementation of linked list you can possibly think of. For all real life purpose you should stick with STL and it looks like std::vector does what you want. That being said, if you are trying to implement your own linked list to learn how it works, let me start by saying that you are already doing it wrong.
By definition linked list is made out of Nodes where each Node points to the next one and can also point to previous. Physical order of individual nodes in memory plays no role and is irrelevant from functionality point of view. There is potential performance gain related do cache hits if you have bunch of consecutive Nodes in the same page, but it is not something you should be aiming at when implementing linked list. If your goal is to have top tier performance, then pure array list will always beat any linked list implementation you can come up with. And std::vector is already what you should be using 99% of the time.
If you already implement a function that takes collection of elements and build Nodes out of them, you somewhat enforce OS to gets you chunks of memory for Nodes in kind of contiguous fashion. It's not a strong guarantee, but I would consider it good enough.
You can't release individual elements that belongs to a chunk of memory created with new[]. If you want to stick to array as your underlying storage for Nodes you have two options, as already mentioned in comments.
Option 1) Allocate single array for whole list and use indexes as your next and previous pointers in nodes. Note that it would require you to somehow handle situation when your list will be asked to hold more elements than your array can handle. Most likely allocating more arrays or allocating bigger one and copying everything which will bring you to array list with fancy ordering.
Option 2) Add dedicated memory manager that will be allocating chunks of memory in form of arrays and will handle individual entries, which is basically implementing your own memory allocator.

How to manage an array of pointers to a struct

I have the following struct:
struct Item
{
Item* nextPtr;
int intKey;
int intValueLength;
};
Based of such a struct I need to maintain several linked lists, which means I need to keep track of one head pointer for each one. I have thought about using an array (HEADS) which will contain a head pointer for each list. The number of lists is variable and will be calculated at run time so I am defining the array dynamically as follows:
int t = 10;
Item* HEADS = new Item[t];
Firstly, I need to initialize each head pointer to NULL because the linked lists are empty when the program runs. How do I do this initialization?
for (int i = 0; i <= t - 1; i++)
// Initialize each element of HEADS to NULL.
And, of course, I will also need to update each element of HEADS with the proper pointer to a linked list (when inserting and deleting items) and also to get the value of each head pointer to display the elements of each list.
I have seen other posts similar to this one in the forum but I am still confused, that is why I am asking my specific situation.
Is this a good approach?
I will very much appreciate your advice.
Respectfully,
Jorge Maldonado
In C++ the common way to write the initialization for loop would be
for (int i = 0; i < t ; i++)
HEADS[i] = NULL;
Or you could write
for (int i = 0 ; i < t ; HEADS[i++] = NULL);
which is slightly more compact.
As to the question of whether an array of pointers is a good idea or not - if you're going to have a variable number of lists, perhaps you should use a linked list of pointers to other linked lists.
I do wonder about your data structure, though. In it you have a pointer to the next element in the list, a key value, and a the length of the value, but you don't appear to have a reference to a value - unless the "key" is really the value, in which case you have mixed terminology - that is, you refer to something in one place as a "key" and in another as a "value. Perhaps you need a pointer to a "value"? But I don't know what you're trying to do here so I just thought I'd note that issue.
Best of luck.
Good approach? That's a very, very dependent on things. Good for a student starting to learn C, maybe. Good for a real C++ programmer? Absolutely not. If you really want to create a linked-list, you should make a class that encompasses each element of these, and dynamically add elements. This is how std::list, for example, works. (std::list is doubly-linked list, and way more complicated).
Here's a sample class of how this should look like (off the top of my head; haven't compiled it, but should work):
struct LinkedList
{
Item* list;
int size = 0;
LinkedList() //constructor of this class/struct, it's the function that will be called once you create an object of LinkedList
{
list = nullptr; //don't use NULL, it's deprecated (this is for C++11, or change it back to NULL if you insist on using C++98)
}
addItem(const int& key)
{
Item item; //construct a new item
item.intKey = key; //fill the value in the item
Item* newList = new Item[size+1]; //create the new list with one more element
for(int i = 0; i < size; i++) //copy the old list to the new list
{
newList[i] = list[i]; //copy element by element
}
list[size] = item; //fill in the new item
if(size > 0)
{
list[size - 1].nextPtr = &list[size]; //assign "next pointer" for previous element
}
size = size+1; //increase the size of the list
}
~linkedList()
{
if(list != nullptr)
{
delete[] list;
}
}
}
Now this is better, but it's still far from optimal. However, this is how C++ should be used. You create objects and deal with them. What you did above is more like C, not C++.
To my code, you have to call:
LinkedList myList;
myList.addItem(55);
There are many things to do here to make this optimal. I'll mention a few:
In my code, every time you add an item, a new array is allocated. This is bad! std::vector solves this problem by allocating a bigger size than needed (for example, you add 1 item, it reserves 10, but uses only 1, and doesn't tell you that). Once you need more than 10, say 11, it reserves 20, maybe. This optimizes performance.
Try to read my code and understand it. You'll learn so much. Ask questions; I'll try to answer. And my recommendation is: get a C++ book, and start reading.

Understanding linked list with C++?

I get the idea of linked list being some kind of way of navigating through some kind of array/list/vector of sorts. I don't think i have the full idea of the concept. I've seen some charts explaining them but I don't seem to understand how they work. So I thought actually looking at the code might help. (My professor doesn't really go over the code that much)
I copied this code from some video.
#include<iostream>
using namespace std;
struct node
{
int num;
node *link;
}*p; // i'm not sure how this pointer was declared
void main()
{
node *root;
root = new node;
root -> num = 5;
root -> link = p;
p = root;
node *q;
for (q = p; q != NULL; q = q -> link)
{
cout << q -> num << endl;
}
}
I guess I don't know what the -> operator does exactly. I know it has something to do with pointers and I'm not sure how that for loop is working.
If someone could take me through the code or if they may have a better example to show me and also if there is any more info that I should know please tell. It would be appreciated.
I think the code is slightly incorrect (or maybe just bad style since it relies on the fact that the pointer is NULL after initialization) as is but I will try to explain this in detail.
Firstly the -> operator evaluates to a dereference followed by a dot ., so root->num is equivalent to (*root).num.
A linked list (in your case a singly linked list) is a structure like follows
NODE{1} --> NODE{3} --> NULL
Here a node is a struct object and has a pointer to another object. These objects together constitute the linked list. As you can see you need some sort of pointer to point to the first element in the linked list. In the example above that element would be the node with the 1 stored in it. This is the root pointer that you have in your code above.
The new is an allocation. You need to place the objects of your linked list somewhere in memory and the new operator helps you find some place in memory (particularly somewhere on the heap) you can store your object. And then it returns a pointer to this location. The best way to learn more about the heap is to research this online. This is an introductory concept (with a lot of "upper level" concepts at the implementation level but you do not have to worry about that at the moment) that is explained very well online. This will likely be better than reading my explanation so I will not explain more about the heap and the stack here. However I think the following links should be helpful
http://www.cplusplus.com/doc/tutorial/dynamic/
http://gribblelab.org/CBootcamp/7_Memory_Stack_vs_Heap.html
http://www.tutorialspoint.com/cplusplus/cpp_dynamic_memory.htm
You also need to know where to stop in the linked list, i.e. you need to know which element is the last element. In the case above the last element is the node with the 3 in it. So this node will not point to another Node object but will rather point to NULL, 0 or the preferred nullptr in C++11 and C++14 (soon also C++17). So you could construct the linked list above with the following code.
#include <iostream>
using std::cout;
using std::endl;
struct Node {
int element;
Node* next;
};
int main() {
Node* root = new Node;
root->element = 1;
root->next = new Node;
root->next->element = 3;
root->next->next = NULL;
for (auto i = root; i != NULL; i = i->next) {
cout << i->element << endl;
}
return 0;
}
Does that make sense?

Changing a line in linked list reversal causes error

This question is probably one of the most popular questions around and while searching for a solution, I've found many but the code below was the most suitable for me.
What it actually does is that it creates another list and iterates through the old list and add the element always to the head of the new list.
Node *reverseList(Node *oldList)
{
Node* newList=NULL;
while(oldList!=NULL)
{
Node *temp=oldList;
oldList=oldList->next;
temp->next=newList;
newList=temp;
}
return newList;
}
However when I decided to re-implement this idea without looking at this code I've changed place of the oldList=oldList->next; and put it after the newList=temp.
My question is does it really make a difference? I couldn't comprehend the reason because after all you are iterating through the oldList. Why would it require to be done immediately after the *temp declaration?
After doing
Node *temp = oldList;
both pointers point at the same place. Since
temp->next = newList;
will overwrite the next pointer of oldList (because it points to the same thing as temp at this stage), you need to update oldList from its next pointer first.

Delete on a very deep tree

I am building a suffix trie (unfortunately, no time to properly implement a suffix tree) for a 10 character set. The strings I wish to parse are going to be rather long (up to 1M characters). The tree is constructed without any problems, however, I run into some when I try to free the memory after being done with it.
In particularly, if I set up my constructor and destructor to be as such (where CNode.child is a pointer to an array of 10 pointers to other CNodes, and count is a simple unsigned int):
CNode::CNode(){
count = 0;
child = new CNode* [10];
memset(child, 0, sizeof(CNode*) * 10);
}
CNode::~CNode(){
for (int i=0; i<10; i++)
delete child[i];
}
I get a stack overflow when trying to delete the root node. I might be wrong, but I am fairly certain that this is due to too many destructor calls (each destructor calls up to 10 other destructors). I know this is suboptimal both space, and time-wise, however, this is supposed to be a quick-and-dirty solution to a the repeated substring problem.
tl;dr: how would one go about freeing the memory occupied by a very deep tree?
Thank you for your time.
One option is to allocate from a large buffer then deallocate that buffer all at once.
For example (untested):
class CNodeBuffer {
private:
std::vector<CNode *> nodes;
public:
~CNodeBuffer() {
empty();
}
CNode *get(...) {
CNode *node = new CNode(...);
nodes.push_back(node);
return node;
}
void empty() {
for(std::vector<CNode *>::iterator *i = nodes.begin(); i != nodes.end(); ++i) {
delete *i;
}
nodes = std::vector<CNode *>();
}
};
If pointers to a std::vector's elements are stable, you can make things a bit simplier and just use a std::vector<CNode>. This requires testing.
Do you initialize the memory for the nodes themselves? From what I can see, your code only allocates memory for the pointers, not the actual nodes.
As far as your question goes, try to iterate over the tree in an iterative manner, not recursively. Recursion is bad, it's nice only when it's on the paper, not in the code, unfortunately.
Have you considered just increasing your stack size?
In visual studio you do it with /FNUMBER where NUMBER is stack size in bytes. You might also need to specify /STACK:reserve[,commit].
You're going to do quite a few deletes. That will take a lot of time, because you will access memory in a very haphazard way. However, at that point you don't need the tree structure anymore. Hence, I would make two passes. In the first pass, create a std::vector<CNode*>, and reserve() enough space for all nodes in your tree. Now recurse over the tree and copy all CNode*'s to your vector. In the second step, sort them (!). Then, in the third step, delete all of them. The second step is technically optional but likely makes the third step a lot faster. If not, try sorting in reverse order.
I think in this case a breadth-first cleanup might help, by putting all the back-tracking information into a deque rather than on the OS provided stack. It still won't pleasantly solve the problem of making it happen in the destructor though.
Pseudocode:
void CNode::cleanup()
{
std::deque<CNode*> nodes;
nodes.push_back(this);
while(!nodes.empty())
{
// Get and remove front node from deque.
// From that node, put all non-null children at end of deque.
// Delete front node.
}
}