my head pointer is supposed to be null, because I don't want
it to have any value when I make my linked list.
I know that you can't dereference something that is null,
but I just want to point it's next node to something new.
can someone explain how I could point the head node pointer?
void dlist::push_front(int value) {
node *p = new node();
node *tempH = head();
tempH->next = p; //break
/***********************************************************
my head pointer is suposed to be null, because I don't want
it to have any value when I make my linked list.
I know that you can't dereference something that is null,
but I just want to point it's next node to something new.
can someone explane how I could point the head node pointer?
************************************************************/
p->value = value;
p->next = tempH->next;
p->prev = tempH;
p->next->prev = p;
p->prev->next = p;
}
#pragma once
#include <ostream>
class dlist {
public:
dlist() {}
// Implement the destructor, to delete all the nodes
//~dlist();
struct node {
int value;
node* next;
node* prev;
};
node* head() const { return _head; }
node* tail() const { return _tail; }
void push_front(int value);
private:
node* _head = nullptr;
node* _tail = nullptr;
};
in your list constructor, simply set the head pointer to null.
dlist::dlist() {
_head = nullptr;
}
Further, if you end up removing the LAST item in your list, you will need to also make _head = nullptr;
Be sure to check if the head is null before dereferencing.
if(_head == nullptr){
_head = new node(...);
}
Your insert function will be responsible for assigning the first node to the head, in the event that you're adding to an uninitialized list.
If your list needs to be sorted, you will need to account for the head changing in the event that the new node should precede the head node.
The most practical solution here is to just use sentinel nodes for your head and tail. Or, just one sentinel node, that stands in for both. The sentinel nodes' elements can just be left uninitialised, you only need those nodes for the next and prev pointers they contain. To test if you've reached the end of the list, instead of testing for a null pointer, you test whether the pointer points to the sentinel node.
You can just use normal nodes as your sentinels if you expect your list elements to be small, or your lists to be very large. You waste a bit of memory on space for elements that won't be used, but it's probably not a big deal. If you really care about memory efficiency (say, you're writing a library), you can have something like this:
template<typename T> class dlist {
struct node_header {
node_header* next;
node_header* prev;
};
struct node : public node_header {
T element;
};
// Convert a node_header pointer to a node pointer
node* node_from_header(node_header* p) {
return static_cast<node*>(p);
}
};
With this approach, your sentinel node is a node_header and all your actual, element-containing nodes are nodes. Your internal algorithms all work on node_headers, until you need to actually retrieve the element of a node, at which point you use node_from_header() to retrieve the full, element-containing node.
If you absolutely want to not use sentinel nodes, you'll have to rewrite your code to directly use the head pointer, rather than retrieving it through a function, and add special-case code for handling a null head pointer. It's not a pretty option.
Related
Doing a self-impl queue in C++. The question is about adding the first element. I know there's an obvious way of ckecking if head is NULL then we also change head, if no then we don't touch it. But I was told there's another way which I didn't understand. The example was like this:
first = (QueueNode*)&last;
then I should assign last element and no if-check required. But it actually doesn't work, so is there a way to implement what I'm talking about and what did I get wrong?
struct QueueNode
{
T data;
QueueNode* next;
} *first, *last;
edit:
The usual way we implement Enqueue operation is
void Enqueue(T data)
{
QueueNode* node = new QueueNode();
node->data = data;
node->next = last;
last = node;
if (first == NULL) first = last; // <- THIS is what I want to get rid off
}
The error is here:
first = (QueueNode*)&last;
where :
struct QueueNode
{
T data;
QueueNode* next;
} *first, *last;
you already declare last as pointer, then when you assign it to first you dereference again this pointer.
So you have a pointer to pointer to the QueueNode struct.
In many occasions, we need to modify a linked list drastically so we will sometimes create another linked list and pass it to the old one. For example,
struct node { //let's say we have a linked list storing integers
int data;
node* next;
};
and suppose we already have a linked list storing integers 1,2,3.
node* head; //suppose we already store 1,2,3 in this linked list
node* new_head ; //suppose we make a temporary linked list storing 4,5,6,7
head = new_head; //modifying the original linked list
My Question
If I delete head (the old linked list) before the assignment then the whole program will crash.
Conversely, if I do not delete it, then there will be a memory leak.
Therefore, I am looking for a way to modify the linked list without memory leak.
My attempt
I tried to make a helper function similar to strcpy to do my work.
void passing_node(node*& head1, node* head2){ //copy head2 and paste to head1
node* ptr1 = head1;
for (node* ptr2 = head; ptr2 != nullptr; ptr2 = ptr2->next)
{
if (ptr1 == nullptr){
ptr1 = new node;
}
ptr1->data = ptr2->data;
ptr1 = ptr1->next;
}
}
// note that we assume that the linked list head2 is always longer than head1.
However, I still got a crash in the program and I cannot think of any other way to modify this. Any help would be appreciated.
Easier way to avoid memory leak is to avoid raw owning pointers.
You might use std::unique_ptr (or rewrite your own version):
struct node {
int data = 0;
std::unique_ptr<node> next;
};
You can move nodes.
You can no longer copy nodes (with possible double free issue).
so deep_copy might look like:
std::unique_ptr<Node> deep_copy(const Node* node)
{
if (node == nullptr) return nullptr;
auto res = std::make_unique<Node>();
res->data = node->data;
res->next = deep_copy(node->next.get());
return res;
}
I would suggest preallocating the linked list so it's easy to delete every node in one call. The nodes would then just reference somewhere inside this preallocated memory. For example:
struct Node
{
int value;
Node* next;
};
struct LinkedList
{
Node* elements;
Node* first;
Node* last;
Node* free_list;
LinkedList(size_t size)
{
first = nullptr;
last = nullptr;
elements = new Node[size]{0};
free_list = elements;
for (size_t i = 0; i < size-1; ++i)
free_list[i].next = &free_list[i+1];
free_list[count-1].next = nullptr;
}
~LinkedList()
{
delete[] elements;
}
void Add(int value)
{
if (free_list == nullptr)
// Reallocate or raise error.
// Take node from free_list and update free_list to
// point to the next node in its list.
// Update last node to the new node.
// Update the first node if it's the first to be added.
}
void Free(Node* node)
{
// Search for the node and update the previous and
// next's pointers.
// Update first or last if the node is either of them.
// Add the node to the last place in the free_list
}
};
From here you'll have many strategies to add or remove nodes. As long as you make sure to only add nodes to the allocated elements array, you'll never have any memory leak. Before adding, you must check if the array have the capacity to add one more node. If it doesn't, you either have to raise an error, or reallocate a new the LinkedList, copy over all values, and delete the old one.
It becomes a bit more complicated when the array becomes fragmented. You can use a 'free list' to keep track of the deleted nodes. Basically, a LinkedList of all nodes that are deleted.
Just take notice that my code is incomplete. The basic approach is to create an allocator of some sort from which you can allocate a bulk, use segments of it, and then delete in bulk.
I want to know whether this code deletes the first node correctly or should I necessarily pass list's head as a pointer?
void List::deleteFirst()
{
temp = head;
head = head->next;
delete temp;
}
This is the class List
class List
{
private:
struct node
{
int data;
node * next;
};
node * head;
node * curr;
node * temp;
public:
//List();
//void AddNode(int addData);
//void DeleteNode(int delData);
void deleteFirst();
//void PrintList();
};
This will work, bit only if:
Your Nodes are allocated using new
You ensure that head always points to a valid node (the list is not empty).
Otherwise, you will cause undefined behavior.
But you really shouldn't store temp as a member variable, it should be a local variable instead.
The same goes for the curr variable, make sure it really needs to be a member.
Also, if you delete an object (e.g. your node), all remaining pointers to it become invalid, so be careful that you don't try to access it afterwards, for ex. through the curr* pointer.
Yup that will delete the the data pointed to by the original value of head.
I'm working on a project and I was given this function to complete
void addToEnd(node*& head, string newVal)
Effect: adds new node to tail end of list
Precondition: head is a pointer to first node in the list (list MAY be empty)
Postcondition: list contains one more node
My question is what is the string newVal for?
The value_type of this class is of type DOUBLE so I'm confused what string newVal is for. So I can't set the newVal in the node because it is of two different types.
This is what I have so far. I'm not sure if im going in the right direction.
node *temp = new node;
temp = head;
while(temp->link() != NULL){
temp = temp->link();
}
head->set_link(temp);
I'm not even sure where to use the string in this block of code.
link() returns the member variable node* link_field
set_link() sets the new link to the link_field
Well, we're guessing that they somehow expect you to turn a string into a double with a function like std::stod.
As for your list manipulation code, there's a few problems:
node *temp = new node;
temp = head;
This creates a new node, puts its pointer in temp, then immediately overwrites temp with head, losing (leaking) the new node. Don't do that.
while(temp->link() != NULL){
temp = temp->link();
}
This is close, but might not work. The problem is that you need to keep track of the real node pointer, not a copy.
Normally, in a linked list API using pointers instead of references, the "add node" function looks like:
void addToEnd(node** head, string newVal)
{
while(*head)
head = &((*head)->next);
*head = new node;
(*head)->value = newVal;
(*head)->next = 0;
}
Note that if the list is empty, the passed-in head pointer is altered to point to the new node. If the list is not empty, the last next pointer is altered instead.
The API you're given (i.e. the link and set_link methods) doesn't allow this, because the head pointer is not a node and those functions require a node. So you've got to do it a little differently, namely you have to handle the empty list case separately.
void addToEnd(node*& head, string newVal)
{
// Create the node.
node* newNode = new node;
newNode->value = std::stod(newVal);
newNode->set_link(0);
if(!head) // Empty list?
{
head = newNode;
return;
}
// Find last node.
node* item = head;
while(item->link())
item = item->link();
item->set_link(newNode);
}
// Variables
typedef struct node
{
int value;
struct node *next;
}mynode;
// Globals (not required, though).
mynode *head, *tail, *temp;
// Functions
void add(int value);
// Function to add new nodes to the linked list
void add(int value)
{
temp = (mynode *) malloc(sizeof(struct node));
temp->next=(mynode *)0;
temp->value=value;
if(head==(mynode *)0)
{
head=temp;
tail=temp;
}
else
{
tail->next=temp;
tail=temp;
}
}
// The main() function
int main()
{
head=(mynode *)0;
// Construct the linked list.
add(1);
add(2);
add(3);
return(0);
}
If I only have a pointer to a node, whose value is 3(The Last node as seen in the aforementioned code) , Can we delete it and make a node whose value is 2(aforementioned code) as the last node.
No you can not. Unless you have some reference to previous node. like head pointer. If you have other reference than its pretty much easier. In fact if you don't have any pointers you will loose the list itself
No, but if you know what you are doing, you can modify the last node in-place. Deleting the last node requires access to the second-to-last node, and specifically its link to the last node.
The answer is no.
You can call free on that pointer to the last node, but that just means that the memory occupied by that node is no longer claimed. The data will most likely stay there unchanged for a while. And that means that the next-to-last node's pointer to it is still valid, even though it should not be.
To delete the node in a way that is meaningful to the list, that pointer contained in the next-to-last node has to be nullified. And that can't be done unless that next-to-last node can be accessed, either by a direct pointer to it, or by traversing the list from a preceding node.
You can use a doubly linked list to access the previous node. Or iterate through the entire list.
Yes you can.. Try the following code:
void deleteNode()
{
mynode *temp1;
for(temp1 = head; temp->next!= tail; temp1 = temp1->next);
tail = temp1;
free(tail->next);
}
It will delete the last node.