I have to create method push_back which will add an item to the end of my list.
But I have one constraint - I can't check if head is empty (if head is null)
I don't have idea how I can do this. Here is my code:
#include <stdio.h>
#include <stdlib.h>
struct node
{
int value;
struct node* next;
};
void print(struct node* head)
{
struct node* iterator = head;
while (iterator != NULL)
{
printf("%d\n", iterator->value);
iterator = iterator->next;
}
printf("\n");
}
void pushBack(struct node** head, int value)
{
struct node* element = (struct node*)malloc(sizeof(struct node));
struct node* iterator = *head;
element->value = value;
element->next = NULL;
if (iterator == NULL) //can't!
{
*head = element;
return;
}
while (iterator->next != NULL)
{
iterator = iterator->next;
}
iterator->next = element;
}
int main(void)
{
struct node* head = NULL;
pushBack(&head, 4);
pushBack(&head, 5);
pushBack(&head, 52);
pushBack(&head, 1);
print(head);
return 0;
}
Any ideas how I can get working push_back method without checking head and without empty nodes?
My teacher asked after classes, where we was discussing about linked list, if it's possible to do this - no one knew how it can be done.
I think you may have misunderstood your instructors wishes. I believe the instructor desired you not check if (head); Instead check if (*head). They're not the same condition.
In your code, head is a pointer to pointer. Though you may pedantically wish to check that someone did not pass you a null pointer to pointer, the fact is all you really care about is whether the pointer it points to is null (thus the dereference).
And that considerably reduces your code lineage.
void pushBack(struct node** head, int value)
{
while (*head)
head = &(*head)->next;
*head = malloc(sizeof(**head));
(*head)->value = value;
(*head)->next = NULL;
}
Yes of course you can do this. Thats a very good use for a pointer-to-a-pointer. You can even use pushBack() like this:
pushBack(&(element->next), 4);
if element is a pointer to the last element!
If you return the new element from pushBack, you can add new elements in constant time O(1), because you don't have to iterate through all the previous elements.
If head points to the last element in the list then you just say
newItem->next = head;
head = newItem;
of course this operations is more often called a "push". I've not heard the term push back.
This sounds like a data structures class problem. The answer should be in the reading assignments.
But anyway. I believe the answer is to use a circular linked list. What this is, is a head pointer to an empty node. The empty node marks the beginning AND end of the list. Doing it this way simplifies all of the list operations, because it completely removes the need to operate on the head pointer.
Related
I am having a problem on assigning char array after I create a node.
I am having trouble on this function I created which it would make a new node and then assign character array into the created node. I don't have any problems when it comes to int but I can't seem to run when I switched to character array.
I get a run time error when I try to run my code.
Any help would be much appreciated.
Thank You!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
struct node{
char data[MAX];
struct node *next;
};
typedef struct node* nodePtr;
void create(nodePtr head, char data[]);
int main()
{
nodePtr head = NULL;
char str[] = "HELLO";
create(head, str);
return 0;
}
void create(nodePtr head, char data[])
{
if (head == NULL)
{
// empty list
head = (nodePtr) malloc(sizeof(struct node)); // create root
strcpy(head->data, data);
head->next = NULL;
}
else
{ // list not empty
nodePtr current = head; // start at the beginning...
while (current->next != NULL)
{
current = current->next; // walk to the end of the list
}
current->next = (nodePtr) malloc(sizeof(struct node));
current = current->next;
strcpy(current->data, data);
}
}
There is more than one problem with your program.
To begin with, when you add elements to a list that already has a head, are not initializing the next member. This will cause multiple list insertions to fail.
current->next = malloc(sizeof(struct node));
current = current->next;
strcpy(current->data, data);
current->next = NULL; //<-- you forgot this
The other big issue is that you are also leaking your entire list, because you pass the head pointer by value into the function. When the list is empty, the function modifies this value but it's a copy so when the function returns, the value of head in main is still NULL.
There are two options. Either you change your function to expect a pointer to the head-pointer, or you use what's called a "dummy head".
Using a dummy head actually simplifies lists a lot. What this means is your head is actually an unused node whose sole responsibility is to store the actual head in its next-pointer:
struct node head = {}; // dummy head node
create(&head, str);
The above will never actually hit the head == NULL part of the create function because dummy heads guarantee the head is always a valid node. So if you set your list up that way, then your create function can be simplified.
If you don't want to do this, then you need to pass a pointer to your head pointer. That means your create function becomes:
void create(nodePtr *head, char data[])
{
if (*head == NULL)
{
*head = malloc(sizeof(struct node));
strcpy((*head)->data, data);
(*head)->next = NULL;
}
else
{
nodePtr current = *head;
while (current->next != NULL) current = current->next;
current->next = malloc(sizeof(struct node));
current = current->next;
if (current != NULL)
{
strcpy(current->data, data);
current->next = NULL;
}
}
}
And you would invoke the above as:
nodePtr head = NULL;
create(&head, str);
One extra thing you might wish to do is make the function return the node that it created. The reason you might want to do this is that currently if you're inserting many items into the list you have to search to the end every time. If instead you pass the last node as the next head, then no searching is necessary:
nodePtr head = NULL;
nodePtr tail = head;
tail = create(head, "goodbye");
tail = create(tail, "cruel");
tail = create(tail, "world");
This would mean a small modification to your function, but I'm sure you can work that out.
struct node * delete_list(struct node * list)
{
struct node* temp = (struct node*)malloc(sizeof(struct node)); // allocates memory for temp
while (list != NULL)
{
temp = list->next;
free(list);
list = temp;
}
free(temp);
return list;
}
I have tried working out my function by hand on paper and it seems to work. I tried an example linked list of length 3 and I have found my function to delete each node and have the list point to null at the end. The actual result does not reflect this.
Could someone give me a push in the right direction?
You're doing more than you need to here. The call to malloc is unnecessary and just leaks memory. And the final call to free is also incorrect, since it will just attempt to free NULL.
All you need is the following:
void delete_list(struct node *list)
{
struct node *temp;
while (list != NULL) {
temp = list->next;
free(list);
list = temp;
}
}
This will free every node in the list, which is all it needs to do.
Note that I changed the return type to void since there's no reason for a constant NULL return value, but if you want to return NULL it's easy enough to do so.
typedef struct node node;
struct node {
int data;
node *next;
};
int insert_asc(node **phead, int data) {
node **traser;
node *newnode = malloc(sizeof(node));
if (newnode == 0)
return 0;
newnode->data = data;
for (traser = phead; *traser != 0; traser = &(*traser)->next)
if (data <= (*traser)->data)
break;
newnode->next = *traser;
*traser = newnode;
return 1;
}
The confusing part for me is when you dereference a double pointer traser.
how come (*traser)->next holds the next node's address?
and what exactly is *traser here?
Double pointers are used in the posted code for two separate purposes:
node **phead: the head of the list is passed by referenced so it can be updated by insert_asc if the new node must be inserted at the head of the list. Passing by reference is not possible in C, the idiomatic way to achieve it is to pass a pointer to the value to be updated by the function, hence a double pointer phead.
node **traser: To avoid making a special case of the empty list and the insertion at the head of the list, the programmer uses a pointer to keep track of the place to insert the new node. traser first points to the head of the list which in this case is the value of phead and is updated to point to the link between nodes, the next member of the current node, when it is determined that the new node must be inserted after the current one. This is an elegant way to implement insertion without a special case. C allows thanks to pointers, it is not possible in java nor javascript because these language do not have generalised pointers.
Note however that the code could be make more readable by use NULL instead of 0 when comparing pointers:
typedef struct node node;
struct node {
int data;
node *next;
};
int insert_asc(node **phead, int data) {
node **traser;
node *newnode = malloc(sizeof(node));
if (newnode == NULL)
return 0;
newnode->data = data;
for (traser = phead; *traser != NULL; traser = &(*traser)->next) {
if (data <= (*traser)->data)
break;
}
newnode->next = *traser;
*traser = newnode;
return 1;
}
Note also that new nodes with a given value of data are inserted before nodes with the same data value. It does not make a difference in this case and may be a little faster for lists with many duplicates, but if the payload was more elaborate, this insertion method would implement a non-stable sort, whereas using < instead of <= would make the sort stable.
For illustration, here is alternative implementation that does not use a double pointer for the insertion point and needs extra tests for the special cases:
int insert_asc(node **phead, int data) {
node *cur;
node *newnode = malloc(sizeof(node));
if (newnode == NULL)
return 0;
newnode->data = data;
cur = *phead;
if (cur == NULL || cur->next == NULL) {
newnode->next = cur;
*phead = newnode;
} else {
while (cur->next != NULL && data < cur->next->data)
cur = cur->next;
newnode->next = cur->next;
cur->next = newnode;
}
return 1;
}
You are using a double pointer here in order to keep track of the head of your list.
If you were using a simple pointer here and exchanged the nodes, you would risk loosing the address of some nodes of your list.
This is because if you were passing a simple pointer to the head of your list, you would then manipulate a copy of you head address in your function, therefore when you exchange positions in your function, the address of your head would still be the same, if you exchanged the head with another node, then the address of all nodes before the old head would be lost after your function modifies your list.
Edit: pythontutor.com is a tool that helped me understanding the behavior of linked list quite easily thanks to its excellent visualization tool, I would highly recommend you to use it.
EDIT: I think my question is completely different from the proposed duplicate. It's asking about the general case whereas my question is asking for a very specific case where the reason for the weird behavior should be traceable given how specific it is.
I have some really weird behavior in my doubly LL implementation.
Basically, if I pop() (from the head) an element and then inject() (add at the tail) some other element, that last tail element now points to the head of the list for seemingly no reason (I guess instead of NULL by default or at least a random address).
I figured out how to fix the problem. When injecting, I wasn't pointing the new node's "next" to NULL.
However, I would still like to understand why the injected node would choose to point to the head without specific direction of where to point.
The effect is that if I travel the list starting from the head (but not starting from the tail), I keep looping forever as the last tail element points back to the head of the list.
EDIT: So I tried printing out the address that the pointer is pointing to just after the call to malloc in inject(), and for some crazy reason the pointer is created already pointing to the head's address; but this only happens if I call pop() before calling inject(). Incredibly weird...
int pop()
{
node* temp = head;
int value = temp->value;
head = temp->next;
free(temp);
head->previous = NULL;
size--;
return value;
}
void inject(int value)
{
if (tail == NULL)
{
tail = malloc(sizeof(node));
tail->value = value;
tail->next = NULL;
tail->previous = NULL;
head = tail;
size++;
}
else
{
node* new_node = malloc(sizeof(node));
printf("pointing to: %p\n", new_node->next);// points to head after pop() call
new_node->value = value;
tail->next = new_node;
new_node->previous = tail;
tail = new_node;
//new_node->next = NULL;
size++;
}
}
The commented out line in inject() solves the problem but still doesn't explain why the tail would point back to the head if I inject after a pop.
Below is the code before main() in case:
typedef struct node{
int value;
struct node* next;
struct node* previous;
}node;
node* head = NULL;
node* tail = NULL;
int head_value();
int tail_value();
void push(int);
int pop();
void inject(int);
int eject();
int size = 0;
node* new_node = malloc(sizeof(node));
printf("pointing to: %p\n", new_node->next);// points to head after pop() call
new_node->next will contain whatever garbage malloc wants to put in there. It might happen to point to head, but you never initialized it, so that printf is trying to find meaning in garbage.
Your code scatters memory management all over the place. Rather than try to fix it, let's rewrite it using my stock advice about structs: always write functions to initialize and destroy them. Always, even if it seems silly and trivial. It avoids scattering that code all over the place, doing it slightly differently every time. It allows you to unit test the basic functions of the struct before trying to use it.It lets you focus on the algorithm, not the memory management.
First, let's make a tweak to your struct. node is a very bad name for a type. It's likely you (or somebody else) is going to want to call a variable node and cause a conflict. I've called it Node, capitalized to avoid confusion with variables and builtins.
typedef struct Node {
int value;
struct Node* next;
struct Node* previous;
} Node;
Now we can write Node_new and Node_destroy.
Node *Node_new() {
Node *node = malloc(sizeof(Node));
node->value = 0;
node->next = NULL;
node->previous = NULL;
return node;
}
void Node_destroy( Node *node ) {
free(node);
}
Node_destroy might seem silly, but it frees you (or anyone else) from having to remember how to destroy a Node. And it lets you change the internal structure of Node without changing the rest of the code (which happened while writing this).
You're using globals. Globals make everything more complicated and restrict what you can do with the code. Instead, wrap things like head, tail, and size into its own structure and pass that around.
typedef struct {
Node *head;
Node *tail;
size_t size;
} LinkedList;
And it needs its own create and destroy functions.
LinkedList *LinkedList_new() {
LinkedList *list = malloc(sizeof(LinkedList));
list->head = NULL;
list->tail = NULL;
list->size = 0;
return list;
}
void LinkedList_destroy( LinkedList *list ) {
for( Node *node = list->head; node != NULL; node = node->next ) {
Node_destroy(list->head);
}
free(list);
}
Note that LinkedList_destroy takes responsibility for cleaning up all its nodes, its one less thing for the user of LinkedList to worry about and potentially screw up.
LinkedList_destroy can call Node_destroy without knowing anything about how Node works. This is how we immediately benefit from the encapsulation and abstraction of Node. But don't use recursion, the list can be arbitrarily long and recursion risks a stack overflow.
Now we can write push and pop assured that things are properly created and destroyed. Note that they take a LinkedList rather than using globals.
void LinkedList_push(LinkedList *list, int value)
{
Node *node = Node_new();
node->value = value;
switch( list->size ) {
/* The list is empty, this is the first node */
case 0:
list->head = list->tail = node;
break;
default:
list->tail->next = node;
node->previous = list->tail;
list->tail = node;
break;
}
list->size++;
}
int LinkedList_pop( LinkedList *list ) {
Node *popped = list->tail;
switch( list->size ) {
/* The list is empty, nothing to pop */
case 0:
fprintf(stderr, "LinkedList was empty when popped.\n");
exit(1);
break;
/* Popped the last node */
case 1:
list->head = list->tail = NULL;
break;
/* Only one node left, it's both the head and tail */
case 2:
list->tail = list->head;
list->tail->previous = list->tail->next = NULL;
break;
default:
list->tail = popped->previous;
list->tail->next = NULL;
break;
}
/* Have to do this at the end because size_t is unsigned
it can't go negative */
list->size--;
int value = popped->value;
Node_destroy(popped);
return value;
}
I've used a switch so I can clearly demarcate all the special cases.
I'm not saying this is the best implementation of push and pop, or that it's even bug free, but they can be written without worrying about whether the structs have been properly initialized or freed. You can focus on the logic, not the memory management.
And then to demonstrate it all works...
void LinkedList_print( LinkedList *list ) {
for( Node *node = list->head; node != NULL; node = node->next) {
printf("%d\n", node->value);
}
}
int main() {
LinkedList *list = LinkedList_new();
for( int i = 0; i < 3; i++ ) {
LinkedList_push(list, i);
}
while( list->size != 0 ) {
printf("list->size: %zu\n", list->size);
LinkedList_print(list);
LinkedList_pop(list);
}
LinkedList_destroy(list);
}
$ ./test
list->size: 3
0
1
2
list->size: 2
0
1
list->size: 1
0
I'm having trouble comprehending linked lists in general. I understand how they work on paper, but once I get to coding them, I never seem to accomplish anything.
Here's my code:
header file:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct List {
int data;
struct List * next;
} List;
implementation file:
#include "test.h"
void addToFront(int data, List * head);
int main(void) {
List * list;
list = malloc(sizeof(List));
list->next = NULL;
List * head;
head = NULL;
addToFront(5,head);
printf("%d",head->data); //print first element
printf("%d",list->data); //print first element
}
void addToFront(int data, List * head) {
if(head == NULL) {
List * newNode = malloc(sizeof(List));
newNode->data = data;
head = newNode;
}
else {
List * newNode = malloc(sizeof(List));
newNode->data = data;
newNode->next = head;
head = newNode;
}
}
I know that for a linked list to be empty, the header is NULL, so I have that checked there. The issue arises as I get a segfault saying that the header is not initialized, well obviously it's not, if I do initializes, I can't keep track of if the list is empty or not, hence the use of a header node.
What can I do now? I don't want to use double pointers, as for my class no one else is using them at any point so far yet (Please don't make me use double pointers), and I'm completely lost on how to proceed here.
I was thinking of trying this without a header node. As such I could have a counter that keeps track of the items in the list, check if its zero and then just add the basic element to the front, otherwise do the same thing I'm doing in my else statement?
The problem is in your addFront function, you only pass the pointer to the head, in order to change what the head points to you need to pass the address of the head:
void addToFront(int data, List ** head)
then
*head = newHead
When you pass only the pointer you are just passing a copy of the pointer to the function so any changes to the pointer you do inside the function are lost once you leave the function scope.
Similar in concept to:
void f(int n)
{
n = 53;
}
To avoid double pointer you can return the new head:
List* addToFront(int data, List* head)
{
...
return newNode;
}
...
head = addToFront(data, head);
...
If you don't want to use double pointer, you can use the header node as a sentinel node, which act solely as a placeholder, so that the first element is linked by head->next, and to check if the list is empty, check head->next == NULL. Try this:
int main(void) {
List *head = malloc(sizeof(List));
head->data = 0;
head->next = NULL;
addToFront(5, head);
List *first = head->next;
if (first) { // safe guard for first element
printf("%d", first->data); //print first element
printf("%d", first->data); //print first element
}
}
void addToFront(int data, List * head) {
List * newNode = malloc(sizeof(List));
newNode->data = data;
newNode->next = head->next;
head->next = newNode;
}