Understanding linked list with C++? - 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?

Related

How to clean up a Minimum Spanning Tree

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

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.

Problems dereferencing node defined by struct

This is for a homework assignment, so explanations (and not direct code) are what I need.
We recently started learned about copy constructors/assignment = operators and such. In the handout we got in class our professor showed us how if you want to deep copy pointers you have to dereference them and copy the values directly.
Eg: (from handout)
class IntCellFixed {
public:
IntCellFixed(int initialValue = 0) { storedValue = new
int(initialValue); }
//v This bit here v
IntCellFixed(const IntCellFixed &source) {
storedValue = new int();
*storedValue = *source.storedValue;
}
//^ This bit here ^
(...)
private:
int *storedValue;
};
This makes sense to me. You have a pointer, and you want to have the value it points to be equal to the value of the pointer you're copying from. So you do * to have it change the value at the address it's pointing at, and not the address itself. That makes sense to me.
So when we went to apply that in lab, I tried a similar thing, but with linked lists instead of just pointers to an integer. Even with the TA's help (with the TA looking at and tweaking my code until it was the "correct" thing), it still did not work and just gave me a segfault. I did not have a chance to ask what exactly the code was supposed to be, and the solution hasn't been posted yet.
We're doing almost the same thing in our homework. Here is the structure of our node (in a binary search tree):
struct Node {
int data;
int count;
Node *left;
Node *right;
};
In my void BinarySearchTree::insert(Node *node,Node *parent, int value) function, I have this bit of code:
if (node == NULL) {
node = new Node();
*node->data = value;
*node->count = 1;
node->left = NULL;
node->right = NULL;
}
When I try to do this, however, it gives me the error: invalid type argument of unary β€˜*’ (have β€˜int’).
When I take off the *, it runs fine, but doesn't actually save the data for the node outside of the insert function. (ie: when trying to insert multiple values, it always starts with an empty tree, even after I've supposedly inserted values already).
As far as I understand it, I WOULD need to dereference the node because I don't want the address it's pointing to to change, I want what's AT the address to change.
Am I just completely misunderstanding how/when to dereference? If I am, could somebody explain when I would need to, and why I wouldn't need to in this case? Or, if it's a syntax error, could somebody tell me what the syntax should be?

Accessing Vector of Nodes

Just a quick C++ question. I've been trying to save a node inside a vector to the right child of a node.
For example,
I have a struct called node that has a pointer leading to a left child and a right child.
So:
struct node{
node *left;
node *right;
};
My vector of nodes,
vector<node> nodeVec;
consists of nodes as well.
The goal is to then take a node out of my vector and save as the right and left child of a new node.
So:
node *tree = new node();
tree->left = *nodeVec.at(0);
tree->right = *nodeVec.at(1);
But it throws an error saying that it doesn't recognize the '*' operator. Trying just
tree->left = nodeVec.at(0)
It says that I can't convert a node to a node*.
But if I use,
tree->left = &nodeVec.at(0)
It succesfully saves the address inside my left child.
I took a look at a couple of sites and answers and I think the one found here,
Dereference vector pointer to access element,
might've been the most relevant. I gave it a shot and threw so many errors, I didn't quite understand.
In short, from what i've read, I need to dereference the node inside my vector. But if it doesn't accept the '*' operator, how would one do that?
Thanks in advance!
You can access nodes in the vector like this:
node* p_left = nodeDev.at(0).left;
node copy_constructed_node(*(nodeDev.at(0).right));
You can also use your tree:
node* p_left = tree->left; // your code set this to &nodeVec[0]
More generally, I suggest you do some background reading on pointers (maybe here), or - better yet - consider whether a Standard container will satisfy your needs - e.g. std::map (tutorial here).

linked list first member variable of node structure always next node

Suppose you have a linked list of nodes as defined below:
C++ code
struct node {
node *next;
int i ;
};
Is there any benefit in making the next pointer as the first member variable of the structure ?
I think that people try this via the above approach (I may be wrong here)
node n, m;
*n=&m;
If above is right, is it right to code like above. What's the right way?
Is there any benefit in making the next pointer as the first member
variable of the structure ?
A very small performance benefit can be reached to reduce the assembly instruction size in loads from and writes to zero offset members, but only in classes without virtual table (vtbl is a omitted first member).
If you want prebuild a scope/global allocated list, its elements can be initialized as mentioned.
You can try it:
struct node {
struct node* next;
int i;
};
node z = {0}, c={&z}, b={&c}, a={&b};
node * stack = &a;
you can find very useful information about liked list searching for 'linux kernel linked list':
Linux Kernel Linked List Explained
How does the kernel implements Linked Lists?
I working now in my own design of 'intrusive node' general purpose containers using c++ templates, perhaps this question might seem interesting.
node n, m;
*n = &m;
is not legal code, perhaps you mean
node n, m;
n.next = &m;
but normally this would be done with dynamic allocation
node* n = new node;
n->next = new node;
because normally you would use node to create a variable length list. Since the length of the list varies there is no way to declare the right number of variables, instead you must allocate the nodes dynamcally.