How to manage an array of pointers to a struct - c++

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.

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.

Why does the array length change when an array that contains a pointer is a parameter of a method?

I have a Node class, and when I created an array of Node pointer(Node*) and passed it through the method, I had a different length of the array as the parameter.
Node* hands[4];
Deal(deck,hands,4,"one-at-a-time",13);
void Deal(Node* &deck, Node* hands[], int people, std::string type, int count){
Node*& temp = deck;
for (int i = 0; i < count; ++i) {
for (int j = 0; j < people; ++j) {
append(hands[j], CopyDeck(temp));
temp = temp->after;
}
}
}
When I use Clion debugger to see the value of variables, I found that hands that I create has values of
hands[0] = 0x746365667265700e
hands[1] = NULL
hands[2] = NULL
hands[3] = 0x00007fc44b402430
And when it is passed through the method, in method the hands is
*hands=0x746365667265700e
hands[1]=NULL
hands[2]=NULL
hands[3]=0x00007fc44b402430
hands[4]=0x00007fc44b402570
What does the "*hands" stand for? And why the initial value in hands are not NULL? Actually the minimal example I can have is something like:
class Node{};
void test(Node* list[]){}
int main(int argc, char* argv[]){
Node * temp[4];
test(temp);
}
But it works. And I have already written the same code in other files and works as I thought.
The deck is a simply doubly-linked list of Node. Node has an attribute "after" point to the next Node. My debugger told me before
Node* &temp = deck;
the parameter "hands" already becomes a 5 elements array.
I think I found a possible reason but I can't understand the relationship between. There are two test methods in my main function. The first one is called "SortingTest" and the second one is "DealingTest". When I comment the first test method out, my DealingTest works properly, but after I uncomment it, the DealingTest doesn't work. After SortingTest ends there is no attribute or anything left in the main method. Can anyone explain it to me? Thank you all. Or maybe my clear method is wrong so it not frees the memory correctly?
void DeleteAllCards(Node* root){
Node *current, *next;
current = root;
while (current != nullptr){
next = current->after;
delete current;
current = next;
}
}
The array you created is a C-Style array, which is a fixed size array with 4 elements. In your case, the element type is Node pointer.
C-Arrays do not initialize with default values, unlike many other popular languages. Therefore, the pointer values you are seeing in hands are either a pointer to a Node * or derived type or a garbage memory address with some exceptions to this rule (see below for the edge cases defined by the Standard. For the ones that do say NULL, their memory address is at ox0000...
Update Edit To reflect a comment made by #AlgirdasPreidZius -
For C and C++, there is a standard rule where a standard C-Array shall be populated with default values upon initialization. C++ standard section 6.8.3.2.2 ([basic.start.static]): "If constant initialization is not performed, a variable with static storage duration or thread storage duration is zero-initialized."
As to why your array has those values in them from the function provided, we need more context. A reproducible example is always the best.
Your for loop, judging by the passed in parameters, is an N^2 time complexity loop with 4*4 iterations. The C-Array Node * was also passed in by reference, so when you assign Node *& to deck, the memory address marking the start of the array changes to the location of the deck array. So, it will have the values that the deck C-Array of Node *'s contains, assuming copy is a 1 : 1 copy, deep or shallow

Trying to make a sorted dynamic list ( data structure)

Good evening everyone. The program which I am making right now is related to sorting and data structure. I have already inputted some values in my list. I want to sort them, in order to do so I extract the values present at each individual node into an array and then sort the array. I am having a confusion regarding putting the sorted values into the list.
This is the code which contains the structure, class and function definition ( the full definition of class is not mentioned)
#define max 30
struct data
{
int val,index;
data* next;
};
class exp
{
data* beg;int count;
int arr[max];
public:
exp()
{beg = NULL;
count = 0;}
};
The confusion I am having is because of this:
void exp :: sort_ins()
{
sort();
data* temp = beg;
int i = 0;
do
{
temp -> val = arr[i];
temp = temp -> next;
i++;
}while(i < count && temp != NULL);
}
My question is that after changing the values associated with temp ( which is basically going from beginning to end) will actually change the value of nodes associated with "beg"? I mean to say that after executing this code, if I try to obtain value of nodes attached to "beg", would the values have been updated? Or do I need to add something else? If possible, I would like a link to relevant theory for learning purposes( purely optional).
Thank you for your time..
(I haven't given the full code, I can share if required.)
Yes, changing what temp is pointing at also changes was beg is pointing at because they're both pointing at the same data.
If you copy a pointer, you end up with two pointers pointing at the same thing. And either pointer could be used to change that thing. That's how pointers work.

Linked Lists with vectors

I am trying to perform certain operations through linked lists on vectors.
We have been given a struct type vector
typedef struct{
int *array; // a pointer to vector's storage
int size; // the current number of elements in the vector
int cap; // the current capacity of the vector;
int init_cap; // the initial capacity the vector was initialised with.
} vector;
Now, I want to make a function that takes in a pointer to the vector struct, and initialises it with the given capacity. All the fields are to be initialised. I want to do this using linked list.
Here is my code
#include <iostream>
using namespace std;
typedef struct node {
int *array; // a pointer to the vector's storage
int size; // the current number of elements in the vector
int cap; // the current capacity of the vector
int init_cap; // the initial capacity the vector was initialised with
node *next;
} vector;
node *head = NULL;
Can I make nodes from a vector struct, like I have attempted in the code written above?
void vector_init(vector *v, int capacity){
//initialising the vector with the given capacity
v->size = capacity;
v->cap = capacity;
v->init_cap = capacity;
//linked list with nodes created and values initialised
node *temp, temp2;
temp = head;
temp = new node;
temp->size = capacity;
temp->cap = capacity;
temp->init_cap = capacity;
temp->next = temp2
temp2 = new node;
temp2->size = capacity;
temp2->cap = capacity;
temp2->init_cap = capacity;
temp2->next = NULL;
}
Have I made the linked list, and initialised the values correctly? If we do not create temporary points temp and temp2, and just use v->size etc to initialise the fields, would that make it a linked list?
You have many problems with your code.
Don't use the name vector - there is a structure called std::vector and it is easy to get confused.
If you want to initialize the values of the structure, don't create an external, separate function for that - it's not c++'ish. Create a struct constructor initializing all the values instead.
You don't initialize the array variable anywhere in your code. You should allocate space for it depending on the capacity given in the constructor.
Don't use the name 'array' for the variable. There is a structure called std::array in C++, and it might be confusing.
Your implementaion makes very little sense to me. You have a linked list of arrays right now; if you would like to functionally replace an array of ints with a linked list of ints, each node should contain one int value.
If, for some reason, you would want to stick to this implementation, you also need some kind of update function that would automatically update size and cap variables while adding or removing elements from array. Otherwise you are sure to end up forgetting about it and you're gonna have mess in your structure. Make this function a part of the structure - it shouldn't be an external function.
That typedef struct node doesn't make sense even after changing the word vector to something else - you don't use it anyway in your code.
You are using the same name for two different structures; vector is at first defined as having 4 fields, and in the next lines as having 5 fields.
Technically yes, this is a linked list, but your vector_init() function does not work as it should. Apart from what I've written above:
You should avoid making functions depend on the global variable, in this case head. It could be passed as a parameter.
These two lines:
temp = head;
temp = new node;
don't make sense. The first one makes the variable temp point to head; the second one tells temp to start pointing to the new variable as you're using operator new, which allocates space and return a pointer to the newly created variable. As a result, you don't operate on the variable head, when you do further operations, but on another variable that will be lost after the temp pointer gets invalidated.
You don't need temp and temp2 variables at all. They only bloat the code.
These two lines:
temp->next = temp2;
temp2 = new node;
should switch places since now you assign a pointer that hasn't been yet initialised.
After writing all this stuff I've realised that the function is incorrect in general. For some reason, you first work on the parameter v, and then do something unrelated to it.
Also, your instructor is just not right saying that you can solve all types of problems with the use of linked lists. It may solve some problems in certain situations, or create new problems, depending on the context.
I don't want to be rude, but there seems to be something fundamentally wrong with the concept of the task you have been given itself. I guess someone really hasn't thought it through.

How to implement a compact linked list with array?

Here is the question of exercise CLRS 10.3-4 I am trying to solve
It is often desirable to keep all elements of a doubly linked list compact in storage,
using, for example, the first m index locations in the multiple-array representation.
(This is the case in a paged, virtual-memory computing environment.) Explain how to implement the procedures ALLOCATE OBJECT and FREE OBJECT so that the representation is compact. Assume that there are no pointers to elements of the linked list outside the list itself. (Hint: Use the array implementation of a stack.)
Here is my soln so far
int free;
int allocate()
{
if(free == n+1)
return 0;
int tmp = free;
free = next[free];
return tmp;
}
int deallocate(int pos)
{
for(;pos[next]!=free;pos[next])
{
next[pos] = next[next[pos]];
prev[pos] = prev[next[pos]];
key[pos] = key[next[pos]];
}
int tmp = free;
free = pos;
next[free] = tmp;
}
Now , The problem is , If this is the case , We don't need linked list. If deletion is O(n) we can implement it using normal array. Secondly I have not used the array implementation of stack too . So where is the catch? How should I start?
You don't have to shrink the list right away. Simply leave a hole and link that hole to your free list. Once you've allocated the memory, it's yours. So let's say your page size is 1K. Your initial allocated list size would then be 1K, even if the list is empty. Now you can add and remove items very effectively.
Then introduce another method to pack your list, i.e. remove all holes. Keep in mind that after calling the pack-method, all 'references' become invalid.