So I have a stack with the typical Push and Pop functions that it allows. I'm having a hard time wrapping my head around how it all actually works code-wise. I saw this post here,
Picture/Diagram in the best answer that shows how the list is "pushed" down and you point at the newest element. I have a
node* stack;
which is hooked to a struct "node"
struct node
{
ItemType data;
node* next;
};
How do I incorporate a push and pull with a "node* next;" ? The hard part to wrap my head around is how I'm going to actually do it. I know it initially points at null, and then if I were to push a 2,4,6 it would be 6,4,2,#. Grasping how to actually do it with pointers in a linked list throws me for a loop. I can do it without pointers but the pointers are what get me. Thank you for any help, I really wanna work through this. I'm here to comment back too quickly. Thanks!
EDIT 1:
solved - my push is working
EDIT 2:
I'm trying to pop now. Would that mean I have to just aim my pointer at the next value? What do I do to the old top node? delete it since I new'd it?
It looks like a C question.
Functions push can be defined in C++ the following way
void push( node * &stack, const ItemType &item )
{
node *tmp = new node { item. stack };
stack = tmp;
}
in C it can look like
int push( struct node * *stack, const ItemType *item )
{
node *tmp = malloc( sizeof( struct node ) );
if ( tmp )
{
tmp->data = *item;
tmp->next = *stack;
*stack = tmp;
}
return tmp != NULL;
}
EDIT: After you edited your post I also edited my post.
It seems that pointer stack is a data member of class StackClass. In this case the member function can look like
void StackClass::Push( ItemType newItem )
// I would declare the parameter as const ItemType &newItem
{
node* temp = new node;
temp->data = newItem;
temp->next = stack;
stack = temp;
}
Related
I've started to learn linked lists today, and I am trying to delete nodes.
void deleteEnd(Node* refNode) {
Node* lastNode;
lastNode = new Node;
while((refNode->next)->next != NULL) {
refNode = refNode->next;
}
lastNode = refNode->next;
refNode->next = NULL;
delete lastNode;
}
void deleteIndex(Node* refNode, int index) {
Node *prev, *next, *deleted;
prev = new Node;
next = new Node;
deleted = new Node;
for(int i=1; i < index; i++) {
refNode = refNode->next;
}
prev = refNode;
deleted = prev->next;
next = deleted->next;
prev->next = next;
free(deleted);
}
I can use delete in the first one, but when I try to use it in the second, it doesn't work. The terminal doesn't give any error messages.
I found some information on the Internet, but I couldn't really understand it.
This is my linked list:
class Node {
public:
int data;
Node *next;
};
As pointed out by the comments, there are several things wrong with this code. All issues are from the comments, none are found by me, all credit goes to François Andrieux, Jesper Juhl, Sven Nilsonn, Avi Berger, and Thomas Matthews.
First, the code probably doesn't work because you mixed new and free. new is a C++ API function, while free is from C. Whenever you construct an object with new, which should not be that often with C++'s automatic memory management, you must free it with delete.
Second, when looping through a list, always start at 0. The only reason otherwise would be to start at the second item.
Third, in this passage:
prev = new Node;
...
prev = refNode;
...
prev->next = next;
When you set prev, it is overwriting the previous value. If this is a pointer, as it is, then this causes a memory leak. Always delete it before overwriting.
Finally, in deleteEnd, as pointed out by Thomas Matthews, you are trying to dereference, or get the value from, the pointers, without checking if it is nullptr. If one is, it will cause undefined behavior, and can crash the program.
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'm trying to learn C++ and there is a small confusion I have.
The text which I am learning from tells me that if I want to delete a node of type const T& I should first create a new pointer of that node type, then delete it using the inbuilt C++ delete[]. However, what happens if I just set the link from the to-be-deleted node's previous element to the to-be-deleted node's next element? Something like:
*p = node.previous;
p-> next = node.next;
Or will this cause a memory leak?
I'm confused because I read somewhere else to never, ever delete pointers willy-nilly, but the example code I am working with has something along the lines of:
Node<T> *p = node-to-be-deleted;
delete p;
What is the best way to delete the node?
Assuming your node looks like this:
struct Node
{
Node* previous;
Node* next;
SomeType data;
};
Then:
*p = node.previous;
p-> next = node.next;
Then YES. This will cause a memory leak.
It also leaves p->next->prev pointing at the wrong node.
I'm confused because I read somewhere else to never, ever delete pointers willy-nilly, but the example code I am working with has something along the lines of:
Yes the best way is to "never delete pointers". But this has to go along with some context. You should not be deleting pointers manually because pointers should be managed by an objects that control their lifespan. The simplest of these objects are smart pointers or containers. But for this situation that would be overkill (as you are creating the container).
As you are creating the container (a list) you will need to do the management yourself (Note C++ already has a couple of lost types std::list for a list of values of type t or boost::ptr_list for a list of pointers to T). But it is a good exercise to try and do it yourself.
Here is an example on code review of a beginner making a list and the comments it generated:
http://codereview.stackexchange.com: Linked list in C++
I hope this helps in explains on how to create and delete objects.
Node* p = new Node; // This is how you allocate a node
delete p; // This is how you delete it
The delete[] operator should be used on dynamically allocated arrays:
Node* nodelist = new Node[ 4 ]; // nodelist is now a (dynamically allocated) array with 4 items.
delete[] nodelist; // Will delete all 4 elements (which is actually just one chunk of memory)
Deleting a Node directly only makes sense if Node implements a destructor to update the previous and next pointers of the surrounding Node instances, eg:
Node::~Node()
{
if (previous) previous->next = next;
if (next) next->previous = previous;
}
Node *p = node-to-be-deleted;
delete p;
Otherwise, you have to update the Node pointers before then deleting the Node in question, eg:
Node *p = node-to-be-deleted;
if (p->previous) p->previous->next = p->next;
if (p->next) p->next->previous = p->previous;
delete p;
With that said, the best approach is to no implement a linked list manually to begin with. In C++, use a std::list container instead, and let it handle these details for you.
void deleteNode( Node * p )
{
Node * temp = p->next;
p->data = p->next->data;
p->next = temp->next;
free(temp);
}
Heres something i did a few months ago.
template <class T>
T LinkedList<T>::remove(int pos)
{
if (pos < 1 || pos > size)
{
throw pos;
}
ListNode * temp;
if (pos == 1)
{
temp=head;
head = head->next;
}
else
{
int i=1;
ListNode * prev = head;
while(i<pos-1)
{
i++;
prev=prev->next;
}
temp = prev->next;
prev->next = (prev->next)->next;
}
--size;
return temp->item;
}
recently, while reading former's code in my current project, I encounter the problems below:
while implementing the Queue, my former wrote codes like this:
while(uq->pHead)
{
char *tmp = uq->pHead;
uq->pHead = *(char **)tmp;
//...
}
the uq->pHead has definition like:
typedef struct {
char* pHead;
//...
} Queue;
Well, I'm quite confused about the usage that "uq->pHead = *(char**)tmp" , could anyone explain it to me in detail?
if we assume that *(uq->pHead) = 32(i.e. ' ') , *(char**)tmp would translate this into pointer-form, but...how could it make sense?
Thanks a lot.
Let's suppose that we're implementing your Queue as a linked list. We might have:
struct data_type;
struct node
{
node *next;
data_type item;
};
struct linked_list
{
node *pHead;
// ...
};
To empty the linked list, we might write:
linked_list *uq=...;
while (uq->pHead)
{
// unlink the first node from the list
node *tmp = uq->pHead;
uq->pHead = tmp->next;
// do something with that node
// ...
// deallocate the node
free(tmp);
}
Now suppose we don't really care about maintainable code, or are otherwise being lazy. We might instead just figure any pointer would do, and keep the structure for 'node' in our head, and write:
linked_list *uq=...;
while (uq->pHead)
{
// unlink the first node
char *tmp = uq -> pHead; // tmp points to the first 'node'
uq -> pHead = *(char**)tmp; // The first thing in a 'node' is a pointer to
// the next node.
// do something with 'tmp', the now unlinked node
data_type *item=(data_type*) ( ((char**)tmp) + 1 ); // after the 'next' pointer
// is the real data.
// ...
// free up the node
free(tmp);
}
The Queue struct is likely to be... a queue. And it seems its first element is a pointer to the next or previous item of the queue. It sounds like the coder was not confortable with having to use the type he is being creating - Queue - inside the Queue itself.
A solution for instance was
typedef struct Queue {
struct Queue *pHead;
//...
} Queue;
Back to your question,
char *tmp = uq->pHead;
set tmp to the current Queue item (save it for later use)
uq->pHead = *(char **)tmp;
set the uq->pHead pointer value to the pHead of the current item. Since the coder didn't declare appropriately the pHead ( char * instead of struct Queue * ) it casts the structure pointer ( uq->pHead == tmp ) to char ** and then *(char **) it to retrieve the the first pointer of the structure, that is pHead.
Using my above declaration, the code could have been
while(uq->pHead)
{
Queue *tmp = uq->pHead;
uq->pHead = tmp->pHead; // or uq->pHead = uq->pHead->pHead
//...
}
In this queue pHead is pointer to another pHead.More appropriately it can be written as:
void *pHead;
Also tmp can be written as:
void *tmp;
tmp = uq->pHead;
saves the current pHead pointer to tmp variable.
Now, tmp is casted as (void **) so that tmp is seen as pointing to another pointer.
*(void **) tmp; is the value at tmp, which is also seen as pointer.
uq->pHead = *(void **) tmp;
So, this increments the pHead to next element.
This statement can also be written as:
uq->pHead = uq->pHead->pHead;
I am sorry, if i confused you.
Assume your Queue struct has a object named qa, the address of qa's first data is the same as qa's. In C++, you have many ways to invoke data such as,".","->" . But they are all really use offset just like
`#include<cstdio>
using namespace std;
class A
{
public:
int a;
int b;
};
int main()
{
A a;
printf("%p\n",&a);
printf("%p\n",& A::a);
printf("%p\n",& A::b);
printf("%p\n",&(a.a));
printf("%p\n",&(a.b));
return 0;
}`
You can get what you want from this code.