Changing nodes in linked list - c

This is somewhat mind-boggling
i will try to explain my doubt.
Check this function for example:
void snoc(Lint *l, int val){
Lint i, new;
new = (Lint) malloc(sizeof(Nodo));
new->value = val;
new->next = NULL;
i=(*l);
while(i->next!=NULL){
i=i->next;
}
i->next = new;
}
I understand the concept behind and i have no trouble working with lists, taking in consideration that i can't iterate through the list using the list initial pointer itself (if i do this, i would lose the initial pointer right)
The thing is, making i=(*l) and afterwards iterating through the list using i=i->next, is making the i variable becoming in constant change.
In this particular example, the original list will not change until i find the end of the linked list, and then i make an attribution and voilá! I insert an element at the end.
My doubt is, if by changing the i, and making i->next = new at the end, wouldn't that mean that everytime i make i=i->next, change ALL the nodes in the original list?
Another example would be init:
void init (Lint *l){
Lint i, prev;
i=(*l);
prev=NULL;
while(i!=NULL){
if( (i->next)->next==NULL){
i->next = NULL;
}
i=i->next;
}
}
if i do this, the last element will be removed, by changing the i->next to NULL, at the right moment. But before that, i've been making changes to the i itself again, by telling i=i->next
If i were to make this change to the (*l) itself (by doing (*l)=(*l)->next) i would be ruining the original list .
I really hope you guys can understand my doubt.

Yes, we do understand your confusion, and it comes from not working out the list pointers on a piece of paper so you can visualize what is taking place. Always use a diagram when you are working node link issues. For example in your case with your singly linked-list, current node called i (dubious choice of name) and your new node called new a helpful diagram would be:
Singly Linked-List (non-circular)
Tail Current Head
(list start) i new
+------------+ +------------+ +------------+
| Payload | | Payload | | Payload |
+------------+ +------------+ +------------+
| Next |------->| Next |------->| Next |--->NULL
+------------+ +------------+ +------------+
Now, I bet you can tell what:
changing i, and making i->next = new at the end
would do. This isn't a dig or meant to be condescending, it is really the way you need to work out linked-list issues until you have done it enough you can visualize the pointer wiring in your head. This is even more important when you get to circular or doubly-linked list insertions and deletions. No matter what the problem, if you just write it down and label all the connections that are being made or broken, you can then code it cleanly in your editor.

with some minors modifications your code is like this
void snoc(Lint *l, int val)
{
Lint i, new;
new = (Lint) malloc(sizeof(Nodo));
new->value = val;
new->next = NULL;
i = *l;
if(!(*l))
{
*l = new;
return;
}
while(i->next) i = i->next; // (1)
i->next = new; // (2)
}
and, this is its explanation:
In (1) we walk throught the linked list until its end. We know we are at the end because i->next (which point after each loop step to the next nodo item) is null so in that case, we make i->next point to the newly created nodo item in (2). But l never changed during the function, l is always pointing to the begining of our linked list so the purpose of this function is to add a new element at the end without changing the value of l.
but also we can initialize our linked list with this function...for example:
Lint l = NULL;
snoc(&l, 10); // l will be initialized with a new nodo item with 10 as the value its value member
(0) checks if l is null in that case we make *l point to the newly allocated nodo item and return...so initialization done.
I add another way for the code above
void snoc(Lint *l, int val)
{
Lint now, new, previous;
new = (Lint) malloc(sizeof(Nodo));
new->value = val;
new->next = NULL;
if(!(*l)) // (0)
{
*l = new;
return;
}
for(now = *l; now; now = now->next)previous = now;
previous->next = new;
}

I am not sure I understand your question, but why don't you simply copy the pointer? You can keep a pointer to the initial element and use a second one to traverse though it.

Related

Linked lists and pointers confusion

I'm working on my final project and I was introduced to linked lists, which I must use.
I'm incredibly frustrated after trying to understand how the code works. The concept to me makes complete sense. The code i'm given as an example though, doesn't.
typedef struct node_s {
char name[20];
int age;
struct node_s *listp;
} node;
while (!feof(inp)) {
temp = (node *)malloc(sizeof(node)); // creation of memory
fscanf(inp, "%s%d", temp->name, &temp->age);
if (head == NULL)
head = temp; // setting the head of the list
else {
tail->listp = temp; // else connecting to previous element
}
tail = temp; // updating the current element
tail->listp = NULL; // setting pointer to null.
}
I'm confused at how tail->listp will point to the second element, when each time it's set to be NULL. To further illustrate my confusion, in the else statement tail->listp will point to the new element, which is understandable.
But at the end we point tail->listp to NULL which just disregard the else statement. Yet the code works just fine, and here I am, extremely confused.
You're missing the statement before, which is
tail = temp; // updating the current element
In a loop, you create a new element temp, and link it onto the list. If it's the first element, you start the list by setting it to both the head and the tail, essentially. If it's not the first element, you link it onto the end of the list.
tail->listp = temp;
Then, you set tail=temp to update the pointer to the end of the list, and make sure that the element at the end of the list is pointing to null
tail->listp = NULL;
You could also do
temp->listp = NULL;
tail=temp;
which would be equivalent, if my eyes don't fail me.

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;
}

reverse a linked list using recursion error

I am understanding recursion and so I tried writing reverse a linked list program. I have written the below function but it says segmentation error (core dumped).
void reverse(){
if (head -> next == NULL){
return;
}
reverse (head -> next);
struct node *q = (struct node*) malloc (sizeof(struct node));
q = head -> next;
q -> next = head;
head -> next = NULL;
}
Please can someone guide me. Thank you.
Shouldn't reverse take an argument? And please be aware that you cannot change a pointer in a function and have that be a lasting change. That is, in a C function, the only lasting changes are those that use *var = something.
Recursion is a way of thinking that's gained by practice. So congratulations on your attempt. It's not correct, but don't be discouraged.
Here are two ways to go about the problem.
Your goal is to subdivide it into a smaller version of itself plus a (hopefully easy and fast to compute) incremental step that takes a solution to the smaller version to a complete solution. This is the essence of recursive thinking.
First try: Think of the list as a head element plus the "rest of the list." I.e.,
L = empty or
= h . R
where h is the head element R is the rest of the list, and dot . is joining a new element to the list. Reversing this list consists of reversing R, then appending h on the end:
rev(L) = empty if L is empty
= rev(R) . h otherwise
This is a recursive solution because we can call the reverse function recursively to solve the slightly smaller problem of reversing R, then add a little work to append h, and that gives us the complete solution.
The problem with this formulation is that appending h is more expensive than you'd like. Since we have a singly linked list with only a head pointer, it's time consuming: traverse the whole list. But it will work fine. In C it would be:
NODE *rev(NODE *head) {
return head ? append(head, rev(head->next)) : NULL;
}
NODE *append(NODE *node, NODE *lst) {
node->next = NULL;
if (lst) {
NODE *p;
for (p = lst; p->next; p = p->next) /* skip */ ;
p->next = node;
return lst;
}
return node;
}
So how to get rid of the bad performance? It's frequently the case that different recursive formulations of a problem have different efficiencies. So some trial and error is often involved.
Next try: Think about the computation in terms of dividing the list into two sublists: L = H T, so rev(L) = rev(T) + rev(H). Here plus + is list concatenation. The key is that if I know rev(H) and want to add a new element at its head, the element to add is the first element in T. If this seems fuzzy, let H = [a, b, c] and T = [d, e]. Then if I already know rev(H) = [c, b, a] and want to prepend the next element at the head, I want d, which is the first element of T. In our little notation, you can write this observation just so:
rev(H + (d . T)) = rev(T) + ( d . rev(H) )
So this looks very good. In both cases (getting the head of T and moving it to the head of rev(H)), I'm only interested in the head of the list, which is very efficient to access.
Of course if T is empty, then rev(H) = rev(L). This is the answer!
Writing this as recursive procedure.
NODE *rev(NODE *t, NODE *rev_h) {
if (t) { // if t has some elements
NODE *tail = t->next; // save the tail of T
t->next = rev_h; // prepend the head to rev(H)
return rev(tail, t); // recur to solve the rest of the problem
}
return rev_h; // otherwise T is empty, so the answer is rev(H)
}
At the start, we don't know anything at all about rev(H), so T is the whole list:
NODE *reversed_list = rev(list, NULL);
The next thing to note is that this function is tail recursive: the recursive call is executed just before the function returns. This is good! It means we can easily rewrite it as a loop:
NODE *rev(NODE *t, NODE *rev_h) {
recur:
if (t) { // if t has some elements
NODE *tail = t->next; // save the tail of T
t->next = rev_h; // prepend the head to rev(H)
rev_h = t; // "simulate" the recursive call
t = tail; // by setting both args
goto recur; // and going back to the start
}
return rev_h; // otherwise T is empty, so the answer is rev(H)
}
You can always do this transformation with tail-recursive calls. You should think hard about why this works.
Now the goto is easily rewritten as a while loop, and we can make rev_h a local variable initialized to NULL, since that's all the initial call does:
NODE *rev(NODE *t) {
NODE *rev_h = NULL;
while (t) { // while t has some elements
NODE *tail = t->next; // save the tail of T
t->next = rev_h; // prepend the head to rev(H)
rev_h = t; // "simulate" the recursive call
t = tail; // by setting both args
}
return rev_h; // otherwise T is empty, so the answer is rev(H)
}
An in-place linked list reverser that needs only a small constant amount of space!
And look! We never had to draw funny box and arrow diagrams or think about pointers. It "just happened" by careful reasoning about how to subdivide the problem into smaller instances of itself, the essence of recursion. It's also a nice way to see that loops are just a special kind of recursion. Cool, no?
I am assuming that you have something like the followings predefined in your .c file
typedef struct node node_t;
struct node {
int some_data;
node_t *next;
};
/* Your linked list here */
typedef struct {
node_t *head;
node_t *foot; /* to keep track of the last element */
} list_t;
In your function, there are a few mistakes you made
not providing any input arguments
access head->next when the program has no idea where to find head
Hence, resulting in the most frustrating error in C -- segmentation fault!
Instead, you should try the following:
void reverse(list_t *mylinkedlist){
if (mylinkedlist->head->next == NULL) {
return;
}
/* do something */
}

understanding linked list-like structure in c

I'm having trouble understanding a piece of C code that represents a linked list structure. The skeleton of the struct looks like this:
struct r{
r *next;
r **prev;
data *d;
}
struct r *rlist;
rlist can be filled by calling the following function: (skeleton only)
r* rcreate(data *d){
struct r *a = xmalloc(sizeof(*r))
a->d = d;
a->next = rlist;
a->prev = &rlist;
if (rlist)
rlist->prev = &a->next;
rlist = a;
return a;
}
How do I go about using this data structure? e.g. how to traverse rlist ?
Edit: here is the function for deleting a node in the linked list
void rdestroy(struct r *a){
if (a->next){
a->next->prev = a->prev;
}
*a->prev = a->next;
destroy(a->d); /* destroy is defined elsewhere */
}
Double prev pointer seems to allow traversing list in one direction only, while allowing easy deletion (because even though you can't access the previous element (easily), you can access the next pointer of previous element, and set it to new correct value when deleting a node.
Without seeing other related functions, it's hard to see why it is done this way. I've not seen this done, and can't immediately think of any really useful benefit.
I think this allows having simpler node deletion code, because node does not need to care if it first or not, because node's prev pointer will always have non-NULL value to a pointer it needs to modify when deleting itself. And same simplicity for insertion before a current node. If these operations are what dominate the use pattern, then this could be seen as minor optimization, I suppose, especially in older CPUs where branches might have been much more expensive.
How to traverse list
This was the question, right? You can only traverse it forward, in a very simple manner, here's a for loop to traverse entire list:
struct r *node;
for (node = rlist ; node ; node = node->next) {
// assert that prev points to pointer, which should point to this node
assert(*(node->prev) == node);
// use node
printf("node at %p with data at %p\n", node, node->d);
}
Example insertion function
This example insertion function demonstrates how insertion before a node needs no branches (untested):
struct r *rinsert(struct r *nextnode, data *d) {
// create and initialize new node
struct r *newnode = xmalloc(sizeof(struct r));
newnode->d = d;
newnode->next = nextnode;
newnode->prev = nextnode->prev;
// set next pointer of preceding node (or rlist) to point to newnode
*(newnode->prev) = newnode;
// set prev pointer of nextnode to point to next pointer of newnode
nextnode->prev = &(newnode->next);
return newnode;
}
There's no good reason to have r ** next in that structure. It's for a double linked list.
So if this thing is created you have it assigned
thisList = rcreate("my data")
now you could start with traversing it
while (thisList->next)
thisList = thisList->next.
...
Your code has many syntactical errors in it, probably because (as you say) it is a "skeleton," so it is hard to parse what the author (whether it was you or someone else) actually intended this code to do.
A simple (doubly) linked list structure looks like this:
struct node {
struct node *next, *prev; // pointers to the adjacent list entries
int data; // use whatever datatype you want
};
struct node *list = NULL; // the list starts empty
void add_entry(int new_data) {
struct node *new_entry = malloc(sizeof(struct node));
// note that in the above line you need sizeof the whole struct, not a pointer
new_entry->data = new_data;
new_entry->next = list; // will be added to the beginning of the list
new_entry->prev = NULL; // no entries currently front of this one
// in general a NULL pointer denotes an end (front or back) of the list
list->prev = new_entry;
list = new_entry; // now list points to this entry
// also, this entry's "next" pointer points to what used to
// be the start of the list
}
Edit: I'll say that if you want us to help you understand some code that is part of a larger program, that you did not write and can't modify, then please post the relevant code in a format that is at least syntactical. As others have said, for example, the use of prev in the code you posted is indecipherable, and it isn't clear (because there are other similarly confusing syntactical problems) whether that was in the original code or whether it is an error introduced in transcription.
Yang, I am not sure how comfortable you are with pointers in general. I suggest taking a look at few other linked-list implementations, it might just do the trick.
Take at look at this Generic Linked List Implementation.

How to add a node in a specific location?

I have been working with a doubly linked list. Everything works OK with the exemption of the function that should add a copy of 'who' before 'whereX' [see code bellow]. Why is the function not working?
void addNodeAt(Node *whereX, Node *who)
{
//copy
Node *temp = (Node*)malloc(sizeof(Node));
temp->count = who->count;
strcpy(temp->word,who->word);
temp->before = whereX->before;
temp->after = whereX;
//paste
if(whereX->after == who)
whereX->after = who->after;
whereX->before = temp;
}
EDIT:
In response to user326404 who said:
'Note: Your function does suffer a flaw that prevents it from inserting who as the new head of the list. It will insert, but you never return the new head node so the list is lost.'
what if I have a Node *head as a global variable. How can I reasign the head without returning it?
You are not letting the existing links know about the newly created temp node. Add the following code to the end of the function to let the preceding portion of the chain point to the newly created node.
if (whereX->before != NULL)
whereX->before->after = temp;
Note: Your function does suffer a flaw that prevents it from inserting who as the new head of the list. It will insert, but you never return the new head node so the list is lost.
Let's say you have this list:
[Node1] <-> [WhereX] <-> [Node2]
From these assignments:
Node *temp = (Node*)malloc(sizeof(Node));
temp->count = who->count;
strcpy(temp->word,who->word);
temp->before = whereX->before;
temp->after = whereX;
and from this:
whereX->before = temp;
you will have:
[Node1] <- [temp] <-> [WhereX] <-> [Node2]
| ^
----------------------
but Node1's after pointer is still looking at WhereX, so
you should also add this assignment:
whereX->before->after = temp;
What you are doing needs some changes. You have correctly allocated memory to duplicate.
But problem statement is not very clear.
Assuming you want to add a node before whereX, you have to do the following:
Point "after" pointer of temp to whereX
Point "before" pointer of temp to "before" pointer of whereX
Point "before->after" pointer of whereX to temp
Point "before" pointer of whereX to temp
Hope this helps.
EDIT:
Also do the appropriate NULL checks

Resources