I recently saw an implementation of deleting a node from a single linked list using a double pointer . Other than making the code more beautiful does this implementation have any benefits efficiency wise . Also how can I implement a similar approach towards inserting a node to a linked list ( without keeping track of previous Node ). I am really curious if there is any better algorithm out there to achieve this
Node* Delete(Node *head, int value)
{
Node **pp = &head; /* pointer to a pointer */
Node *entry = head;
while (entry )
{
if (entry->value == value)
{
*pp = entry->next;
}
pp = &entry->next;
entry = entry->next;
}
return head;
}
For insertion to the back of a list storing only the head, no tail (which would imply a small list where linear-time insertion is acceptable), you can do this by introducing the extra pointer indirection to eliminate special cases:
Simple Version (Pointers to Pointers to Nodes)
void List::push_back(int value)
{
// Point to the node link (pointer to pointer to node),
// not to the node.
Node** link = &head;
// While the link is not null, point to the next link.
while (*link)
link = &(*link)->next;
// Set the link to the new node.
*link = new Node(value, nullptr);
}
... which you can reduce to just:
void List::push_back(int value)
{
Node** link = &head;
for (; *link; link = &(*link)->next) {}
*link = new Node(value, nullptr);
}
As opposed to, say:
Complex Version (Pointers to Nodes)
void List::push_back(int value)
{
if (head)
{
// If the list is not empty, walk to the back and
// insert there.
Node* node = head;
while (node->next)
node = node->next;
node->next = new Node(value, nullptr);
}
else
{
// If the list is empty, set the head to the new node.
head = new Node(value, nullptr);
}
}
Or to be fair and remove comments:
void List::push_back(int value)
{
if (head)
{
Node* node = head;
for (; node->next; node = node->next) {}
node->next = new Node(value, nullptr);
}
else
head = new Node(value, nullptr);
}
No Special Case for Simple Version
The main reason the first version doesn't have to special case empty lists is because if we imagine head is null:
Node** link = &head; // pointer to pointer to null
for (; *link; link = &(*link)->next) {}
*link = new Node(value, nullptr);
Then the for loop condition is immediately false and we then assign the new node to the head. We don't have to check for that case separately outside the loop when we use pointers to pointers.
Insertion Sort
If you want to do an insertion sort instead of simply inserting to the back, then this:
void List::insert_sorted(int value)
{
Node** link = &head;
for (; *link && (*link)->value < value; link = &(*link)->next) {}
// New node will be inserted to the back of the list
// or before the first node whose value >= 'value'.
*link = new Node(value, *link);
}
Performance
As for performance, not sure it makes much difference to eliminate the extra branch, but it definitely makes the code tighter and reduces its cyclomatic complexity. The reason Linus considers this style to be "good taste" is because in C, you often have to write linked list logic often since it's not so easy and necessarily worthwhile to generalize linked lists since we have no class templates there, e.g., so it's handy to favor a smaller, more elegant, less error-prone way to write this stuff. Plus it demonstrates that you understand pointers quite well.
Other than making the code more beautiful does this implementation
have any benefits efficiency wise.
Don't have anything to compare this to so hard to say but this is about as efficient as you can remove a node from a linked list. Note that the function name Delete would be more accurate as Remove since it does not actually clean up the node it removes from the list.
Also how can I implement a similar approach towards inserting a node
to a linked list ( without keeping track of previous Node ).
One way is to look ahead. Best shown in an example following the format of your Delete function.
void insert(Node *head, int value)
{
Node *entry = head;
while (entry)
{
if (entry->next == NULL)
{
entry->next = new Node(NULL, value);
return;
}
else if (value < entry->next->value)
{
entry->next = new Node(entry->next, value);
return;
}
entry = entry->next;
}
}
Related
So i am trying to delete from a specific node in a linked list but the problem doesn't come from deleting from the node to end ,it comes from trying to delete from the last node to a specific node while trying to use a recursion.
This is what I currently have (deleting from the node to last node)
void rLL<T> :: recursiveDelete(item<T> * node)
{
if (node != nullptr)
{
item<T> * nodeptr = node -> next;
delete node;
size--;
recursiveDelete(nodeptr);
}
}
Now I have to try and switch it around.And I have no idea how to do that.
This is for a school project so please ,if possible, try to keep it simple.
edit:Let's say the list consist of 1,2,3,4,5,6,7 and node 5 was put in to the parameter node then 7 ,6 ,5 should be deleted in that specific order (to clear things up a bit)
The answer to your question is to simply perform the recursive call before calling delete.
However, even if you were able to delete the specified node without failure, the code you presented would still have a major flaw - it does not update the next field of the node prior to the specified node, so you would end up leaving the list in an invalid state, as that prior node would become the new tail node but have a non-null next pointer that does not terminate the list properly.
For a single-linked list, you would have to iterate from the front of the list in order to discover that prior node to update. But once you reach the specified node, and know its previous node, then you can use a recursive algorithm for the rest of the list, eg:
template<typename T>
void rLL<T>::recursiveDelete(item<T> *node, item<T> *previous)
{
if (!node) return;
recursiveDelete(node->next, node);
if (previous) previous->next = nullptr;
--size;
delete node;
}
void rLL<T>::deleteToEnd(item<T> *startNode)
{
if (!startNode) return;
item<T> *node = head;
item<T> *previous = nullptr;
while (node)
{
if (node == startNode)
{
recursiveDelete(node, previous);
return;
}
previous = node;
node = node->next;
}
}
Live Demo
That being said, a double-linked list is better suited for this task, as you don't need to iterate from the front of the list at all, you can start right at the specified node, eg:
void rLL<T>::deleteToEnd(item<T> *startNode)
{
if (!startNode) return;
deleteToEnd(startNode->next);
item<T> *previous = startNode->previous;
if (previous) previous->next = nullptr;
if (head == startNode) head = nullptr;
tail = previous;
--size;
delete startNode;
}
Live Demo
If I understand this correctly, you want to delete a node that points to your specific node. If I were doing this I would have 2 nodes, currentNode and prevNode. When you iterate forward you move both of them and that way when currentNode meets the specifications, you can delete prevNode. If I don't understand the question right let me know.
I am writing some code in visual studio with c++, it's an ordered linked list but I am having a bit of trouble with the pointers.
I have three different methods / functions that carry out this task.
/*
* insert_head: Insert a node at the beginning of the list.
*/
book *inserta_head(book *head, book *newNode){
newNode->next = head;
return newNode;
}
/*
* insert_after: Insert a new node after another one.
*/
void insert_after(book *node, book *newNode){
newNode->next = node->next;
node->next = newNode;
}
/*
* insert: Adds a new node (ordered by code) to the list.
*/
void insert(book* head, int code, char name[40], char lastName[40], char title[40], int year, int lend) {
book* newNode = crear_libro(code, name, lastName, title, year, lend);
book* aux = head;
// If the list is empty.
if (head == NULL){
head = insert_head(head, newNode);
} else {
// If the new node goes before the head.
if (aux->code > newNode->code){
head = insert_head(head,newNode);
} else {
while (aux != nullptr && aux->code < newNode->code)
aux = aux->next;
// Verify the code isn't repeated
if (aux != nullptr && aux->code == newNode->code){
printf("Error: Verify the code. \n");
} else {
insert_after(aux,newNode);
}
}
}
}
I've tried running the code. Every time I try to print the list it says it's empty. I've checked my printing method and the method that creates nodes, both of them are working so I'm pretty sure it is related to the pointers but I can't find the error.
Your insert function changes the head pointer. But that pointer is a copy of the head pointer you called the function with. So the head pointer outside the insert function is unchanged. That's why nothing gets added to the list.
One simple fix is to make the head parameter a reference.
void insert(book*& head, int code, ...
The problem is how you handle the head.
After this line:
head = insert_head(head, newNode);
head in the function should be correct (double check with a debugger).
However, the head in the caller will remain unchanged. This is because you don't change the data in the existing head, but you create a new one.
A simple fix is to take the pointer to the pointer to head. book** head This way you can change the pointer in the caller as well (after fixing all the compilation errors).
I've been doing this as an exercise on my own to get better at C++ (messing around with a linked list I wrote). What I want to do is to reverse the list by twisting the pointers around, rather than just 'printing' the data out in reverse (which is relatively straightforward).
I have an array of pointers-to-pointers, each pointing to a node in a linked list. But this is less a question about linked-list dynamics (which I understand), and more about pointer magick.
A node looks like this,
template<class T>
struct node {
T data;
node *next;
node(T value) : data(value), next(nullptr) {}
};
And the code in question,
node<T> **reverseArr[listLength];
node<T> *parser = root;
for (auto i : reverseArr) {
i = &parser;
parser = parser->next;
}
root = *(reverseArr[listLength - 1]);
for (int ppi = listLength - 1; ppi >= 0; --ppi) {
if (ppi == 0) {
(*reverseArr[ppi])->next = nullptr;
//std::cout << "ppi is zero!" << "\t";
}
else {
(*reverseArr[ppi])->next = (*reverseArr[ppi - 1]);
//std::cout << "ppi, 'tis not zero!" << "\t";
}
}
My logic:
The new root is the last element of the list,
Iterate through the array in reverse,
Set the current node's next pointer to the previous one by setting the current node's nextNode to the next node in the loop.
What's happening:
If I leave the debug print statements commented, nothing. The function's called but the linked list remains unchanged (not reversed)
If I uncomment the debug prints, the program seg-faults (which doesn't make a whole lot of sense to me but seems to indicate a flaw in my code)
I suspect there's something I'm missing that a fresh pair of eyes might catch. Am I, perhaps, mishandling the array (not accounting for the decay to a pointer or something)?
You're overthinking the problem. The correct way to reverse a single-linked list is much simpler than you think, and does not involve arrays at all.
All you need to do is walk through the list setting each node's next pointer to the head of the list, then set the head of the list to that node. Essentially, you are unlinking each node and inserting it at the start of the list. Once you reach the end, your list is reversed.
It just requires a bit of care, because the order that you do things is important. Something like this should do it:
template <class T>
node<T> * reverse( node<T> * head )
{
node<T> *current = head;
head = NULL;
while( current != NULL )
{
// store remainder of list
node<T> *remain = current->next;
// re-link current node at the head
current->next = head;
head = current;
// continue iterating remainder of list
current = remain;
}
return head;
}
The operation has a linear time complexity. You would invoke it by passing your list's head node as follows:
root = reverse( root );
It should go without saying that it would be a bad idea to call this function with any node that is not the head of a list, or to pass in a list that contains cycles.
I want to make a queue using linked lists.
There are numerous algorithms out there for that. But what i'm curious in is how to make a relative priority queue.
Maybe there is a special name for this type of queue, but i don't know it, and i haven't had any luck googling for the solution.
Anyways, let's say i have this struct, which will represent the Node of my list.
struct Node {
int value;
Node* next;
}
if i want to create a priority queue (where the element with the least value is first), when i insert for example 5 7 1 8 2, my list should look like this:
1 -> 2 -> 5 -> 7 -> 8
It's not really hard to implement that.
What i want to do is - when i insert the first element, other elements should have value relative to the previous element. So, in my example, the list/queue would contain the following values:
1 -> 1 -> 3 -> 2 -> 1
I'm not really sure how i would implement that? Would the following idea be applicable:
in the Node struct i add another field, which would represent the original value.
i find the position of the node i'm inserting the same way i would do when creating an ordinary linked list, and then i just say
temp->value = temp->originalValue - previous->originalValue;
You need to store extra data in each node, either the relative priority, or a "previous" pointer. Since the next node's relative priority needs to updated whenever a node is removed (how to do that without a prev pointer?), I suggest the "previous" pointer:
struct Node {
int value;
Node* next;
Node* prev;
}
Then a function can evaluate the relative priority:
int relative_priority(Node* node) {
if (node == NULL)
return 0;
if (node->prev == NULL)
return node->value;
return node->value - node->prev->value;
}
Note that I'm using C, you'll need to replace NULL with 0 for C++
You first have to identify where to insert the new node. This involves decrements on the target value, adjusting its relative value in relation to the current in the list. At the point of insertion, you have to point the previous node to the new node, and then adjust the node ahead of the new node with a new relative value.
Node * create_node (int value, Node *next) { /* ... */ }
void insert_relative_priority_queue (Node **head, int value) {
Node **prev = head, *cur;
if (*head) {
cur = *head;
while (value > cur->value) {
value -= cur->value;
prev = &cur->next;
cur = cur->next;
if (cur == 0) break;
}
*prev = create_node(value, cur);
if (cur) {
cur->value -= value;
}
} else {
*head = create_node(value, 0);
}
}
When you remove from the front of the list, you adjust the value of the new head:
void remove_relative_priority_queue (Node **head) {
if (*head) {
Node *cur = *head;
*head = cur->next;
if (*head) {
(*head)->value += cur->value;
}
free(cur);
}
}
This question already has answers here:
An interesting C linked list idiom
(11 answers)
Closed 5 years ago.
Ten years ago, I was shown a technique for traversing a linked list: instead of using a single pointer, you used a double pointer (pointer-to-pointer).
The technique yielded smaller, more elegant code by eliminating the need to check for certain boundary/edge cases.
Does anyone know what this technique actually is?
I think you mean double pointer as in "pointer to a pointer" which is very efficient for inserting at the end of a singly linked list or a tree structure. The idea is that you don't need a special case or a "trailing pointer" to follow your traversal pointer once you find the end (a NULL pointer). Since you can just dereference your pointer to a pointer (it points to the last node's next pointer!) to insert. Something like this:
T **p = &list_start;
while (*p) {
p = &(*p)->next;
}
*p = new T;
instead of something like this:
T *p = list_start;
if (p == NULL) {
list_start = new T;
} else {
while (p->next) {
p = p->next;
}
p->next = new T;
}
NOTE: It is also useful for making efficient removal code for a singly linked list. At any point doing *p = (*p)->next will remove the node you are "looking at" (of course you still need to clean up the node's storage).
By "double-pointer", I think you mean "pointer-to-pointer". This is useful because it allows you to eliminate special cases for either the head or tail pointers. For example, given this list:
struct node {
struct node *next;
int key;
/* ... */
};
struct node *head;
If you want to search for a node and remove it from the list, the single-pointer method would look like:
if (head->key == search_key)
{
removed = head;
head = head->next;
}
else
{
struct node *cur;
for (cur = head; cur->next != NULL; cur = cur->next)
{
if (cur->next->key == search_key)
{
removed = cur->next;
cur->next = cur->next->next;
break;
}
}
}
Whereas the pointer-to-pointer method is much simpler:
struct node **cur;
for (cur = &head; *cur != NULL; cur = &(*cur)->next)
{
if ((*cur)->key == search_key)
{
removed = *cur;
*cur = (*cur)->next;
break;
}
}
I think you mean doubly-linked lists where a node is something like:
struct Node {
(..) data // The data being stored in the node, it can be of any data type
Node *next; // A pointer to the next node; null for last node
Node *prev; // A pointer to the previous node; null for first node
}
I agree with the comments about using the STL containers for handling your list dirty work. However, this being Stack Overflow, we're all here to learn something.
Here's how you would normally insert into a list:
typedef struct _Node {
void * data;
Node * next;
} Node;
Node * insert( Node * root, void * data ) {
Node * list = root;
Node * listSave = root;
while ( list != null ) {
if ( data < list->data ) {
break;
}
listSave = list;
list = list->next;
}
Node * newNode = (Node*)malloc( sizeof(Node) );
newNode->data = data;
/* Insert at the beginning of the list */
if ( listSave == list ) {
newNode->next = list;
list = newNode;
}
/* Insert at the end of the list */
else if ( list == null ) {
listSave->next = newNode;
newNode->next = null;
list = root;
}
/* Insert at the middle of the list */
else {
listSave->next = newNode;
newNode->next = list;
list = root;
}
return list;
}
Notice all the extra checking you have to do depending on whether the insertion occurs at the beginning, end or middle of the list. Contrast this with the double pointer method:
void insert( Node ** proot, void * data ) {
Node ** plist = proot;
while ( *plist != null ) {
if ( data < (*plist)->data ) {
break;
}
plist = &(*plist)->next;
}
Node * newNode = (Node *)malloc( sizeof(Node) );
newNode->data = data;
newNode->next = *plist;
*plist = newNode;
}
As Evan Teran indicated, this works well for singly linked lists, but when it's doubly linked, you end up going through just as many if not more manipulations as the single pointer case. The other draw back is that you're going through two pointer dereferences for each traversal. While the code looks cleaner, it probably doesn't run as quickly as the single pointer code.
You probably mean a doubly-linked list, with one of the pointers going forward and the other going backward. This allows you to get to the next and previous nodes for a given node without having to remember the last one or two nodes encountered (as in a singly-linked list).
But the one thing I discovered which made the code even more elegant was to always have two dummy elements in the list at all times, the first and the last. This gets rid of the edge cases for insertion and deletion since you're always acting on a node in the middle of the list.
For example, an empty list is created:
first = new node
last = new node
first.next = last
first.prev = null
last.next = null
last.prev = first
// null <- first <-> last -> null
Obviously, traversing the list is slightly modified (forward version shown only):
curr = first.next
while curr <> last:
do something with curr
curr = curr.next
The insertions are much simpler since you don't have to concern yourself with whether you're inserting at the start or end of the list. To insert before the current point:
if curr = first:
raise error
add = new node
add.next = curr
add.prev = curr.prev
curr.prev.next = add
curr.prev = add
Deletions are also simpler, avoiding the edge cases:
if curr = first or curr = last:
raise error
curr.prev.next = curr.next
curr.next.prev = curr.prev
delete curr
All very much cleaner code and at the cost of only having to maintain two extra nodes per list, not a great burden in today's huge memory space environments.
Caveat 1: If you're doing embedded programming where space still might matter, this may not be a viable solution (though some embedded environments are also pretty grunty these days).
Caveat 2: If you're using a language that already provides linked list capabilities, it's probably better to do that rather than roll your own (other than for very specific circumstances).