How 'memory efficient doubly linked list' works? - c

In Data Structures and Algorithms Made Easy, struct of memory efficient memory list given as follows,
struct LinkNode
{
int data;
struct LinkNode* ptrdiff;
}
In ptrdiff, there will be xoring of previous and next node is done. For example, previous node have address 100 and next node address is 500.
So, in ptrdiff address will be 400. Now how it is possible to move to next or previous node (as we do in doubly link list), just by knowing xoring of their addresses?
Am I missing any step here?

The first node has no previous node, and the last node has no next node ... so think of the address of the node before the first and of the node after the last as 0. That's enough to start a traversal, and as you traverse you always have the address of the preceding node in hand so you can determine the address of the succeeding node. Here's an example of traversing such a list and printing the data ... pass the address of either the first or last node to printxorlist and it will print it either forwards or backwards:
void printxorlist(struct LinkNode* node)
{
struct LinkNode* prev = NULL;
while (node)
{
printf("%d\n", node->data);
struct LinkNode* next = (struct LinkNode*)((intptr_t)node->ptrdiff ^ (intptr_t)prev);
prev = node;
node = next;
}
}
Note that we have to cast node->ptrdiff because it doesn't really have the right type. Better would be to declare it correctly:
struct LinkNode
{
int data;
intptr_t ptrdiff;
}
then
void printxorlist(struct LinkNode* node)
{
struct LinkNode* prev = NULL;
while (node)
{
printf("%d\n", node->data);
struct LinkNode* next = (struct LinkNode*)(node->ptrdiff ^ (intptr_t)prev);
prev = node;
node = next;
}
}

In this type of linked list, you can not traverse from the address of any arbitrary node. Because you need some extra information either about predecessor address or successor address. But traversing from the first node(predecessor address =0 , since not any predecessor) or last node(successor address=0, since not any successor) is possible.

Related

Sentinel Node in a C Linked List

I'm trying to learn more about linked lists in C and I recently stumbled upon the Sentinel Node concept, but I can't wrap my head around it. According to the slides I have, the sentinel node should be the first thing on the list when it's created and the last when other nodes are added. There should be a pointer to permanently point to the Sentinel Node.
All those stuff confuse me and I would love some help with the implementation.
/*this is a simple LL implementation*/
#include <stdio.h>
#include <stdlib.h>
struct List
{
int data;
struct List *next;
};
void ListInsert(int new_data)
{
struct List *p;
p = (struct List *)malloc(sizeof(struct List));
p->data = new_data;
p->next = (head);
head = p;
}
void printList(struct List *q)
{
q = head;
while (q != NULL)
{
printf("%d ", q->data);
q = q->next;
}
printf("\n");
}
int main()
{
ListInsert(5);
ListInsert(7);
ListInsert(6);
ListInsert(4);
ListInsert(2);
printList(head);
return 0;
}
Now, if I want to create the sentinel node, how should I proceed?
According to the slides i have, the sentinel node should be the first
thing on the list when its created and the last when other nodes are
added.There should be a pointer to permanently point to the Sentinel
Node.
Let's start with the most important point: the purpose of a sentinel node, which is to mark the end of the list. There will not be real data associated with a sentinel node, so a list containing only a sentinel node is logically empty.
A few things follow from that, including:
the identity of the sentinel node is a property of the whole list, not of any (other) particular node
list manipulation algorithms need to be written differently for linked lists whose ends are marked by a sentinel than for those whose ends are marked by some other means.
each list need a place to store the sentinel's identity
a list that is expected to have a sentinel is invalid if it does not have one
There are many ways to implement the details, all with their own advantages and disadvantages.
Personally, I would be inclined (in the non-sentinel case, too) to have a structure to represent an overall list, separate from the structure used to represent a list node. A pointer to the list's head node would be a member of this structure, and in the sentinel-terminated-list case, so would be a pointer to the sentinel node. When you create a new list, you create a sentinel node for it, too; initially, the list's head and sentinel pointers will both point to that node. The head pointer may be changed, but the sentinel pointer must not be. When you append to the list, the appended node gets placed just before the sentinel. It is an error to try to delete the sentinel from the list.
It is to your advantage to write the code for this yourself.
Create it. You said "There should be a pointer to permanently point to the Sentinel Node", so create the pointer. Then use the pointer as the terminator of the list instead of NULL.
Sentinel node - Wikipedia
/*this is a simple LL implementation*/
#include <stdio.h>
#include <stdlib.h>
struct List
{
int data;
struct List *next;
};
struct List sentinel_node_instance;
/* a pointer to permanently point to the Sentinel Node */
struct List* const SENTINEL_NODE = &sentinel_node_instance;
/* the sentinel node should be the first thing on the list when it's created */
struct List* head = SENTINEL_NODE;
void ListInsert(int new_data)
{
struct List *p;
p = (struct List *)malloc(sizeof(struct List));
p->data = new_data;
p->next = (head);
head = p;
}
void printList(void)
{
struct List* q = head;
while (q != SENTINEL_NODE)
{
printf("%d ", q->data);
q = q->next;
}
printf("\n");
}
int main()
{
ListInsert(5);
ListInsert(7);
ListInsert(6);
ListInsert(4);
ListInsert(2);
printList();
return 0;
}
Another variation of a sentinel node is for a circular doubly linked list, where it is both a head node and a sentinel node. Visual Studio implements std::list in this manner.
head.next = pointer to first node or to head if empty list
head.prev = pointer to last node or to head if empty list
first.prev = pointer to head node
last.next = pointer to head node

Understanding the logic behind building linked list using local reference

Below is the code for creation of linked list using local reference logic.
Not able to understand the code inside the for loop especially the 2nd line. (see // HERE)
Can somebody please elaborate how this logic is working.
void push(struct Node** head_ref, int new_data)
{
struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->data = new_data;
newNode->next = *head_ref;
*head_ref = newNode;
return;
}
struct Node* buildWithLocalRef()
{
int i=0;
struct Node *head = NULL;
struct Node **lastptrRef = &head;
for(i=1;i<6;i++)
{
push(lastptrRef,i);
lastptrRef = &((*lastptrRef)->next); // HERE
}
return head;
}
int main()
{
struct Node* head;
head = buildWithLocalRef();
printList(head);
return 0;
}
The technique you're seeing is building a linked list by forward-chaining. It is the most direct, and sensible way to build an ordered list from beginning to end, where the list does not have a tail pointer (and yours does not).
There are no "references" here. This isn't C++. This is using a pointer to pointer. The variable name is dreadfully named, btw. How it works is this:
Initially the list is empty, head is NULL
A pointer to pointer, lastptrRef will always hold the address of (not the address in; there is a difference) the next pointer to populate with a new dynamic node allocation. Initially that pointer-to-pointer holds the address of the head pointer, which is initially NULL (makes sense, that is where you would want the first node hung).
As you iterate the loop a new node is allocated in push . That node's next pointer is set to whatever value is in the pointer pointed to by lastptrRef (passed as head_ref in the function), then the pointer pointed to by lastptrRef is updated to the new node value.
Finally, lastptrRef is given the address of the next member in the node just added, and the process repeats.
In each case, lastptrRef hold the address of a pointer containing NULL on entry into push. This push function makes this harder to understand. (more on that later). Forward chaining is much easier to understand when done directly, and in this case, it would make it much, much easier to understand
struct Node* buildWithLocalRef()
{
struct Node *head = NULL;
struct Node **pp = &head;
for (int i = 1; i < 6; i++)
{
*pp = malloc(sizeof **pp);
(*pp)->data = i;
pp = &(*pp)->next;
}
*pp = NULL;
return head;
}
Here, pp always holds the address of the next pointer we'll populate with a new node allocation. Initially, it holds the address of head. As each node is inserted pp is set to the address of the next pointer within the latest node inserted, thereby giving you the ability to continue the chain on the next iteration. When the loop is done, pp holds the address of the next pointer in the last node in the list (or the address of head of nothing was inserted; consider what happens if we just pull the loop out entirely). We want that to be NULL to terminate the list, so the final *pp = NULL; is performed.
The code you posted does the same thing, but in a more convoluted manner because push was designed to push items into the front of a list (apparently). The function always sets the pointer pointed to by head_ref to the new node added, and the node's next is always set to the old value in *head_ref first. Therefor, one can build a stack by doing this:
struct Node* buildStack()
{
struct Node *head = NULL;
for (int i = 1; i < 6; i++)
push(&head, i);
return head;
}
Now if you print the resulting linked list, the number will be in reverse order of input. Indeed, push lives up to its name here. Dual-purposing it to build a forward-chained list is creative, I'll grant that, but in the end it makes it somewhat confusing.

Segfault when accessing next node in singly linked list

I'm trying to just reverse a singly linked list, but with a bit of a twist. Rather than having the pointer to the next node be the actual next node, it points to the pointer in that next node.
struct _Node
{
union
{
int n;
char c;
} val;
void *ptr; /* points to ptr variable in next node, not beginning */
int var;
};
typedef struct _Node Node;
I know how to reverse a normal singly linked list and I think I have the general idea of how to go about solving this one, but I'm getting a segfault when I'm trying to access head->ptrand I don't know why.
Node *reverse(Node *head)
{
Node * temp;
Node * prev = NULL;
while(head != NULL)
{
temp = head->ptr + 4; /* add 4 to pass union and get beginning of next node */
head->ptr = prev;
prev = head;
head = temp;
}
return prev;
}
Even if I try and access head->ptr without adding 4, I get a segfault.
The driver that I have for this code is only an object file, so I can't see how things are being called or anything of the sort. I'm either missing something blatantly obvious or there is an issue in the driver.
First, I'll show you a major problem in your code:
while (head) // is shorter than while(head != NULL)
{
// Where does the 4 come from?
// And even if: You have to substract it.
// so, definitively a bug:
// temp = head->ptr + 4; /* add 4 to pass union and get beginning of next node */
size_t offset_ptr = (char*)head->ptr - (char*)head;
// the line above should be moved out of the while loop.
temp = head->ptr - offset_ptr;
Anyways, your algorithm probably won't work as written. If you want to reverse stuff, you are gonna have to work backwards (which is non-trivial in single linked lists). There are two options:
count the elements, allocate an array, remember the pointers in that array and then reassign the next pointers.
create a temporary double linked list (actually you only need another single reversely linked list, because both lists together form a double linked list). Then walk again to copy the next pointer from your temporary list to the old list. Remember to free the temporary list prior to returning.
I tried your code and did some tweaking, well in my opinion your code had some logical error. Your pointers were overwritten again and again (jumping from one node to another and back: 1->2 , 2->1) which were leading to suspected memory leaks. Here, a working version of your code...
Node *reverse(Node *head)
{
Node *temp = 0;
//Re-ordering of your assignment statements
while (head) //No need for explicit head != NULL
{
//Here this line ensures that pointers are not overwritten
Node *next = (Node *)head->ptr; //Type casting from void * to Node *
head->ptr = temp;
temp = head;
head = next;
}
return temp;
}

c: linked list: how to write a node swapping function?

here's my function:
void switchnodes(NODE **A)
{
if((*A)->next)
{
NODE *first = pop(A);
NODE *second = pop(A);
push(A,first);
push(A,second);
}
}
push and pop:
void push(NODE **top,NODE *nnew)
{
nnew->next=*top;
*top=nnew;
}
NODE * pop(NODE **top)
{
NODE *remove = *top;
*top=(*top)->next;
remove->next=0;
return remove;
}
my confusion:
NODE:
typedef struct node {
int value;
struct node *next;
} NODE;
NODE * top my linked list holding values 1...10
NODE * holder is another pointer pointing to the 5th and 4th node of the linked list, respectively
switchnodes(&holder); holder pointing to the 5th node. The 6th node gets lost
switchnodes(&(holder->next));holder pointing the 4th node. 5th and 6th nodes successfully swapped.
why does that happen?
and how do I write this function so I don't have to pass pointer->next? I can't do that in every instance, like if its the top node I need to swap.
That's the way your list works: You can only swap nodes via next pointers; holder is a pointer outside the list. It may get updated, but the list won't know.
Say holder points to the 4th node. You swap it, it now points to the new 4th node, the former 5th node. But what's important: The next pointer of the list's third node still points to the 4th node - 4th before swapping that is.
You can make your list two-way by having a haed and a tail and an additional prev pointer for each node. Then you can use that information to update the swapped nodes' neighbours, if any.
You have to update first's "next" at switchnodes function. Here is a working function:
void switchnodes(NODE **A)
{
if((*A)->next)
{
NODE *first = pop(A);
NODE *second = pop(A);
push(A,first);
first->next = second->next; //the missing line
push(A,second);
}
}

What more does the code need to delete a node from a linked list successfully?

I want to delete a given node from a linked list by the node's index number (serial number). So what I tried to do in my function is that, first I have taken the user input of the index number. Then I used two node type pointers temp and current. I started traversing the list with current and when the index number of the node matches with the user input, I tried to delete the node. So far it is correct. I am facing problem with the deletion logic. Here is the code I tried:
void delete_node(struct node **start,int index_no)
{
int counter=0;
struct node *temp, *current;
temp=(struct node *)malloc(sizeof(struct node));
current=(struct node *)malloc(sizeof(struct node));
current=*start;
while(current->next!=NULL)
{
counter++;
if(counter==index_no)
{
temp= current->next;
free(current);
/*I guess some code is missing here. Help me finding the logic.*/
}
else
{
printf("\n The index number is invalid!!");
}
}
}
The commented portion lacks the deletion logic.
Also, I have a feeling that this code is not space and time-efficient. If it is so, please suggest to a way to make it more compact.
Why are you allocating two nodes in the delete function, then leaking their memory? It seems they should be initialized to start or one of its successors.
You also need to update the next pointer in the previous element and potentially also the start (head) of the list if the removed element was the first (ie. index_no == 1).
You also have an off-by-one error where the final node can never be deleted, because only a node with a ->next pointer will be considered for deletion.
Suggested reading: A Tutorial on Pointers and Arrays in C.
Deleting from a linked list is actually:
find the pointer that points to us
(if found) make it point to our .next pointer instead
delete our node.
In order to change the pointer that points to us, we need a pointer to it: a pointer to pointer. Luckily the first argument already is a pointer to pointer, it presumably points to the head pointer that points to the first list item.
struct node
{
struct node *next;
int num;
} ;
void delete(struct node **pp, int num) {
struct node *del;
int counter;
for (counter=0; *pp; pp= &(*pp)->next) {
if(counter++ == num) break;
}
if (!*pp) { printf("Couldn't find the node(%d)\n", num); return; }
/* if we get here, *pp points to the pointer that points to our current node */
del = *pp;
*pp = del->next;
free(del);
}

Resources