I'm still learning linked lists and have been trying to create a method to insert into a linked list.
I just want to know if this is the correct way of inserting? Also, how would I print the entire linked list, so it prints something like abc.
Here's what I have:
struct node {
char value;
struct node *next;
};
typedef struct node item;
void main() {
InsertChar('a');
InsertChar('b');
InsertChar('c');
}
void InsertChar(char s) {
item *curr, *head;
head = NULL;
curr = (item *)malloc(sizeof(item));
curr->value = s;
curr->next = head;
head = curr;
while(curr) {
printf("%c\n", curr->value);
curr = curr->next;
}
}
First of all, your function InsertChar is overwriting the value of head each time (head = curr), so you'll end up with a list of one item.
You need to declare something that will store head.
struct list
{
struct node *head;
};
Now you can easily print your list by going through each node.
void PrintList(struct list* list)
{
struct node *curr = list->head;
while (curr != NULL)
{
printf("%c\n", curr->value);
curr = curr->next;
}
}
Now you need to modify InsertChar so that the last item in the list (how will you find it?) points to your new item. I'll leave that to you :)
As you may have noticed, you have no means to access the list (if there was any) from outside InsertChar(). You don't use a global variable, nor do you input or output it.
A better implementation:
item * InsertChar(item ** phead, char s) {
item * curr;
// First, allocate a new item and fill it.
curr = malloc(sizeof(item)); // no need to cast here
if (curr) { // only if malloc() succeeds
curr->value = s;
curr->next = *phead;
*phead = curr;
}
return curr;
}
// InsertChar() is only supposed to insert, not to print.
void PrintList(item * head) {
item * curr = head;
while(curr) {
printf("%c", curr->value); // omit the line break as you want abc
curr = curr->next;
}
printf("\n"); // now the line break
return;
// alternative implementation for while loop:
for(curr=head; curr; curr=curr->next) {
printf("%c\n", curr->value);
}
}
void FreeList(item * head) {
item * curr = head;
while(curr) {
item * next = curr->next; // read it out before freeing.
free(curr);
curr = next;
}
}
so that you can do now
int main() {
item * list = NULL; // empty for now, no contents.
char success = 1;
success = success && InsertChar(&list, 'a');
success = success && InsertChar(&list, 'b');
success = success && InsertChar(&list, 'c');
if (!success) {
printf("Oops?");
FreeList(list);
return 1;
}
PrintList(list);
FreeList(list); // clean up.
}
Oops? I didn't test it, but it seems to me that it prints "cba". Why does it so? Well, InsertChar() puts everything to the start.
How to get around of this?
Either we can create an AppendChar() function. But this bears the danger that we into the trap of Schlemiel the Painter's algorithm: to start searching for the right place always from the start. Thus, I'll point out another approach:
int main() {
item * list = NULL; // empty for now, no contents.
item * cursor = InsertChar(&list, 'a');
if (!cursor) goto error;
// cursor now contains our first entry.
// We put the next one to cursor->next:
cursor = InsertChar(&cursor->next, 'b');
if (!cursor) goto error;
cursor = InsertChar(&cursor->next, 'c');
if (!cursor) goto error;
PrintList(list);
FreeList(list); // clean up.
return 0;
error:
printf("Oops?");
FreeList(list);
return 1;
}
I am not sure if I am right (didn't test it), but this should be the way to go.
If you are one of these who were taught that goto is evil under all circumstances, feel free to implement the error handling in a different way.
Related
I have to make a function which swaps the neighbor nodes in a linked list with sentinel. Something like this: 1-2-3-4-5 -> 2-1-4-3-5, but I don't know how to do that. Can somebody help me?
#include <stdio.h>
#include <stdlib.h>
typedef struct _listelem {
int a;
struct _listelem* next;
} listelem;
void reverse_pairs(listelem* a)
{
listelem* head = NULL;
listelem* tail = NULL;
head = a->next;
tail = a->next;
while (head->next != NULL)
{
head = head->next->next;
tail = head;
}
return head;
}
You did not show how the list with a sentinel node is built.
I suppose that the sentinel node is the first node in the list pointed to by the pointer head.
In this case the function can look the following way.
void reverse_pairs( listelem *head )
{
if (head)
{
for (; head->next && head->next->next; head = head->next->next)
{
listelem *tmp = head->next;
head->next = head->next->next;
tmp->next = head->next->next;
head->next->next = tmp;
}
}
}
As for your function implementation then it is incorrect at least because a function with the return type void may not have a statement like this
return head;
Also within this while loop
while (head->next != NULL)
{
head = head->next->next;
tail = head;
}
you are changing the local variables head and tail. Such changes do not influence on the original list.
If you have a circular list when the data member next of the last node points to the head (sentinel) node then the function can look the following way.
void reverse_pairs( listelem *head )
{
if (head)
{
for ( listelem *current = head;
current->next != head && current->next->next != head;
current = current->next->next)
{
listelem *tmp = current->next;
current->next = current->next->next;
tmp->next = current->next->next;
current->next->next = tmp;
}
}
}
While the answer from #VladFromMoscow shows the proper approach for swapping nodes in the list to accomplish your objective, if you are stuck passing a single pointer, and the function return type is fixed at void, then there is another way to go about it.
Instead of swapping nodes, you simply swap the int member value between nodes. Approaching the problem that way, the address of the first node never changes, so there is no need to pass the address of the list as a parameter.
The approach is simple. Take the current node, swap the integer value between the current and next node and advance past the nodes holding the swapped integers. To do so, you advance from the current node to the next and check if the node that follows next is NULL (marking the end of the list). If it is NULL, you are done. If it is not NULL, advance again and repeat. You can write the function either with a while() loop, e.g.
void reverse_pairs (listelem *head)
{
while (head && head->next) {
int tmp = head->a;
head->a = head->next->a;
head->next->a = tmp;
head = head->next;
if (head->next)
head = head->next;
}
}
Or, slightly less readable, using a for() loop and a ternary, e.g.
void reverse_pairs (listelem *head)
{
for (; head && head->next;
head = head->next->next ? head->next->next : NULL) {
int tmp = head->a;
head->a = head->next->a;
head->next->a = tmp;
}
}
Example Use/Output
With a normal list where the last pointer is NULL, your output printing the original list, calling reverse_pairs(), and then outputting the modified list would look as follows:
$ ./bin/lls_revpairs
1 2 3 4 5
2 1 4 3 5
Complete Test Code
The complete test code is included below. Compiling as normal will use the for() loop above, or adding the define -DUSEWHILE, to your compile string will cause the while() loop form of the function to be used:
#include <stdio.h>
#include <stdlib.h>
typedef struct _listelem {
int a;
struct _listelem* next;
} listelem;
/** add node at end of list, update tail to end */
listelem *add (listelem **head, int v)
{
listelem **ppn = head, /* pointer to pointer to head */
*pn = *head, /* pointer to head */
*node = malloc (sizeof *node); /* allocate new node */
if (!node) { /* validate allocation */
perror ("malloc-node");
return NULL;
}
node->a = v; /* initialize members values */
node->next = NULL;
while (pn) {
ppn = &pn->next;
pn = pn->next;
}
return *ppn = node; /* add & return new node */
}
#ifdef USEWHILE
/** reverse node pairs in list - while loop */
void reverse_pairs (listelem *head)
{
while (head && head->next) {
int tmp = head->a;
head->a = head->next->a;
head->next->a = tmp;
head = head->next;
if (head->next)
head = head->next;
}
}
#else
/** reverse node pairs in list - for loop + ternary */
void reverse_pairs (listelem *head)
{
for (; head && head->next;
head = head->next->next ? head->next->next : NULL) {
int tmp = head->a;
head->a = head->next->a;
head->next->a = tmp;
}
}
#endif
/** print all nodes in list */
void prn_list (listelem *l)
{
if (!l) {
puts ("list-empty");
return;
}
size_t i = 0;
for (listelem *n = l; n && i < 10; n = n->next, i++)
printf (" %d", n->a);
putchar ('\n');
}
int main (void) {
listelem *list = NULL;
for (int i = 1; i <= 5; i++)
add (&list, i);
prn_list (list);
reverse_pairs (list);
prn_list (list);
}
I wrote code to remove the node at the tail of a linked list. The code works properly in different testcases, but I think I made my code a bit cumbersome. However, I don't see what I can do differently?
node_t *remove_t (node_t *l){
if (l==NULL){
return l;
}
else {
node_t *curr=l;
node_t *ret=l;
if (curr->next==NULL){
l=NULL;
return l;
}
else {
while (curr->next->next!=NULL){
curr=curr->next;
}
curr->next=NULL;
free(curr->next);
return ret;
}
}
}
It is much, much easier if you keep a pointer-to-pointer to node, then iterate to the end of list and simply free the last pointer and set it NULL, e.g.
/** delete last node */
void del_last_node (node_t **l)
{
node_t **ppn = l; /* pointer to pointer */
node_t *pn = *l; /* pointer to node */
for (; pn->next; ppn = &pn->next, pn = pn->next) { } /* iterate to last */
free (*ppn); /* free node */
*ppn = NULL; /* set pointer NULL */
}
I'm not sure that you can change the logic much - as your approach of 3 different cases (empty list, list with 1 item, and list with >1 items) is reasonable. You can format the code for easier reading: Something like:
node_t *remove_t (node_t *l){
// case 1: Empty list
if (l==NULL){
return l;
} ;
// case 2: List with one item. Return empty list.
node_t *curr=l;
if (curr->next==NULL){
// Remember to free this element.
free(curr) ;
l=NULL;
return l;
} ;
// case 3: list > one item
// Move curr to last item
while (curr->next->next!=NULL){
curr=curr->next;
}
// Note order of free/null setting.
free(curr->next);
curr->next=NULL;
return l;
}
You didn't seem to free the tail node.
curr->next=NULL;
free(curr->next);
You won't be able to free curr->next if you already make it NULL.
My implementation:
void remove_tail(node_t *l) {
if (l == NULL) return;
if (l->next == NULL) {
free(l);
l = NULL;
return;
}
node_t *prev = l;
node_t *curr = l->next;
while (curr->next != NULL) {
prev = curr;
curr = curr->next;
}
prev->next = NULL;
free(curr);
}
As the title implies. Deleting the first node in the linked list works like a charm. It's those other dang nodes! Ok, so here is my code. I think I am deleting properly, but I am not linking correctly.
Here is what the structs and typedefs look like:
typedef struct List *ListP;
typedef struct Entry *EntryP;
typedef char *ListItemP;
struct List
{
int sizeL;
struct Entry *head;
};
struct Entry
{
ListItemP content;
struct Entry *next;
int sizeL;
};
The way that the removeItemList function works in main.c is is by passing a list pointer (ListP thisList) and a string (ListItemP thisItem). Once the parameters have been passed, the function searches for the same string as a node in the list (via strcmp()) and when it finds it removes it. NewEntry initialized Entry struct and has an input that passes a character to newEntry->content. Anyways, here is the function :
void removeItemList(ListP thisList, ListItemP thisItem)
{
EntryP temp = newEntry(0x00);
EntryP temp2 = newEntry(0x00);
temp->next = thisList->head;
temp2->next = thisList->head;
if (strcmp(temp->next->content, thisItem) == 0)
{
thisList->head = temp->next->next;
}
else {
while(temp->next != 0x00)
{
if(strcmp(temp->next->content,thisItem) == 0) {
break;
}
temp->next = temp->next->next;
temp->sizeL++;
}
if (temp->next == 0x00) {
}
else {
int i = 0;
for(i = 0; i < temp->sizeL - 1 ; i++)
{
temp2->next = temp2->next->next;
printf("%s \n", temp2->content);
}
temp2->next = temp->next->next;
free(temp2->next);
}
}
thisList->sizeL--;
}
I think that the node before the node being removed winds up being pointed to null. I am having trouble figuring out how to fix that, though. I guess that's why I'm here! Anything will be greatly appreciated, thank you very much!
EDIT: UPDATED CODE, PLUS DIPLAY LIST CODE
Updated removeItemList()
void removeItemList(ListP thisList, ListItemP thisItem)
{
EntryP current = newEntry(0x00);
EntryP prev = newEntry(0x00);
prev = 0x00;
current = thisList->head;
if (strcmp(current->content, thisItem) == 0)
{
thisList->head = current->next->next;
}
else {
while (current->next != 0x00)
{
prev = current;
current = current->next;
if (strcmp(current->content, thisItem) == 0)
{
prev->next = current->next;
free(current);
}
}
}
}
DisplayList():
void displayList(ListP thisList)
{
EntryP temp = newEntry(0x00);
temp->next = thisList->head;
while(temp->next != 0x00)
{
printf("%s \n", temp->next->content);
temp->next = temp->next->next;
}
free(temp);
}
If I were to just use printf() statements, I can access everything just fine and the node seems deleted. However, if I'm trying to print them with displayList(), I get a segfault after printing the node before the one being deleted. So it seems as if I'm not linking the nodes correctly.
For deleting any intermediate nodes, maintain two pointers to the list, prev and cur. Initialize prev to null and cur to the head of the list. Now traverse the list until you meet the node to be deleted. Before moving to the next node in the analysis, reassign prev to cur and cur to cur->next.
When the required node is reached, do
prev->next = cur->next;
free(cur);
return;// If necessary.
Entire pseudocode:
prev = null;
cur = list->head;
while(cur!=null)
{
if(//This is the node to be deleted)
{
prev->next = cur->next;
free(cur);
return;
}
prev = cur;
cur = cur->next;
}
//Node not found case
Now integrate this scenario into your code, and you should be fine. Before this, just check explicitly for the first node though, as this won't fit into the above use case.
UPDATE: I noticed a few bugs in your function DisplayList(). Why are you analysing with temp->next during traversal? Seems like a roundabout method to me. Just analyse with temp.
void displayList(ListP thisList)
{
EntryP temp = thisList->head;
while(temp!= 0x00)
{
printf("%s \n", temp->content);
temp = temp->next;
}
}
And why were you freeing the temp node during the list display? If you just want to display the list contents, freeing the node doesn't make any sense acc. to me.
I'm having some trouble copying a single linklist over to a double-linklist. Not sure what is going on but I'm having a gdb debugging error:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000000
0x0000000100000d86 in doublify (list=0x1001008c0) at lists.c:62
62 newDlist->item = curr->item;
The one-directional link list is working,
My .h Header File:
typedef struct _node {
Item item;
link next;
} node;
typedef struct _dnode *dlink;
typedef struct _dnode {
Item item;
dlink prev;
dlink next;
} dnode;
My Code:
link newLink (Item item){
link createLink = malloc(sizeof(node));
createLink->item = item;
return createLink;
}
link fromTo (int start, int end) {
link newList = NULL;
if(start <= end ){
newList = newLink(start);
newList->next = fromTo(start+1, end);
}
return newList;
}
//Heres where it isn't able to copy the linklist item over into the new dlist.
dlink doublify (link list) {
dlink newDlist = malloc (sizeof (dnode));
link curr = list;
while(curr != NULL){
newDlist->item = curr->item;
curr = curr->next;
newDlist = newDlist->next;
}
return newDlist;
}
In your doublify function, you only allocate enough space for the first dnode in your doubly-linked list. As you walk along the singly-linked list using curr, you'll need to allocate space for a new dnode at each loop iteration, one for each element in the singly-linked list.
You receive the error because on the second iteration you're trying to access newDlist->next, which is an uninitialized memory address.
Note that while creating the doubly-linked list you'll also need to keep a tmp pointer to the previous dnode in order to set the prev/next pointers correctly.
The problem happens on the second iteration of the while loop. When you assign newDlist to the 'next' item, the 'next' field might contain any value. The thing is that malloc simply gives you a pointer to a data region where any values might be (it doesn't clean it or init in some way).
You simply need to allocate a new node on each iteration and setup the pointers appropriately. Here is one of the ways how that can be done.
dlink prevNode = NULL;
// Insert the first item (if present) to simplify the loop
if ( curr != NULL )
{
dlink newDlist = malloc(sizeof(dnode));
newDlist->item = curr->item;
newDlist->next = NULL;
newDlist->prev = NULL;
prevNode = newDlist;
curr = curr->next;
}
while ( curr != NULL )
{
dlink newDlist = malloc(sizeof(dnode));
newDlist->item = curr->item;
prevNode->next = newDlist;
newDlist->prev = prevNode;
newDlist->next = NULL;
prevNode = newDlist;
curr = curr->next;
}
Maksim has a good answer that helped me, but to simplify the answer if you have...
void DisplayList(topNode* nodeType) {
node* ptrNode = nodeType;
while (ptrNode->next != NULL) {
// DO WORK
}
}
...then you need to change it to...
void DisplayList(topNode* nodeType) {
node* ptrNode = new node; // THIS IS THE KEY...ALLOCATE THE MEMORY
if (ptrNode == NULL)
cout << "Could not allocate memory -- exiting application!" << endl;
else {
ptrNode = nodeType; // THEN COPY THE TOP NODE POINTER
while (ptrNode->next != NULL) {
// DO WORK
}
}
}
I ran into this same problem in my class, so maybe this will save someone some effort in the future!
was implementing a singular linked list in C.
struct node
{
int data;
struct node *next;
};
struct list_el {
int val;
struct list_el * next;
};
typedef struct list_el item;
void main() {
item * curr, * head,*track;
int i;
head = NULL;
for(i=1;i<=10;i++) {
curr = (item *)malloc(sizeof(item));
curr->val = i;
curr->next=0;
if(head!=NULL)
head->next = curr;
head = curr;
}
curr = curr-10;
while(curr) {
printf("%d\n", curr->val);
curr = curr->next ;
}
}
As there are 10 elements in the list, so to make the pointer point to the first element, I tried decreasing curr (pointer to struct) by 10, but this got me half way through the list, the values printed were 5,6,7,8,9,10.
The size of the struct is 4, whereas the size of the pointer is 2, it seems the pointer is decreased by 2*10=20 bytes instead of 40, is this normal? (as I read that pointer increments/decrements according to the size of its type)
You cannot use pointer arithmetic on a linked list: the items are allocated separately (with malloc) and so they will not be necessarily adjacent in memory. That approach would only work with an array.
There are several problems.
First of all, the following insertion code isn't correct:
if(head!=NULL) head->next = curr;
head = curr;
Basically, the element pointed to by head is irrevocably lost.
Secondly, the behaviour of the following code is undefined:
curr = curr-10;
You cannot move across several malloc()ed blocks using pointer arithmetic.
Once you fix the insertion logic, it will become possible to traverse the list like so:
for (curr = head; curr != NULL; curr = curr->next) {
....
}
Your code curr = curr-10 will not bring you back to the head of the linklist.
As Viruzzo pointed out in a comment, you cannot use pointer arithmetic on elements of a linked list. As the word "linked" implies, there are only pointers linking the items together, they're not required to be located at adjacent addresses.
The pointer arithmetic will simply decrease the pointer by a fixed number of bytes, it will not follow pointers. Your list, being singly-linked, doesn't even have previous-element pointers to follow.
curr = curr-10; is wrong. It does not perform the operation that you think it does!
To print the contents of your linked list, you need to start from the head and go through each and every node until you hit NULL (assuming its not a circular list).
void display()
{
NODE * current = head;
if (current == NULL) {
printf("Empty list \n");
return;
}
while(current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
return;
}
And to add new node in the front, you can use the following code snippet.
void addfront(int data)
{
NODE *newnode = NULL;
if ((newnode = malloc(sizeof(NODE))) != NULL) {
newnode->data = data;
newnode->next = NULL;
} else {
printf("Couldn't allocate space for new element \n");
return;
}
if (head == NULL) {
// empty list
head = newnode;
tail = newnode;
} else {
newnode->next = head;
head = newnode;
}
return;
}
To add new node at the rear, you can use the following code snippet.
void addrear(int data)
{
NODE * newnode = NULL;
if ((newnode = (NODE *) malloc(sizeof(NODE))) != NULL) {
newnode->data = data;
newnode->next = NULL;
} else {
printf("unalbe to allocate memory to the new element - %d \n", data);
return;
}
if (tail == NULL) {
assert(head == NULL && tail == NULL);
head = tail = newnode;
} else {
tail->next = newnode;
tail = newnode;
}
return;
}
All the above mentioned code snippet assumes, you have head and tail as global variables.
Hope this helps!