Reputation: 37
I tried to create several functions for linked lists. Here are all of them, but i have problem with function delete_All
and delete
.
delete_All
need to delete all elements with given value, and delete
only first.
The first mistake that i cannt find is free(_)
(marked in code as "HERE!!!") and also endless program work in delete_All
when we put in head
of Linked List and at the same time in the next element value which needs to be removed.
For example (1,1,2,3,4,) cannt remove first pair "1". delete
and some other functions cannt use free
function.
#include <stdlib.h>
#include <stdio.h>
struct uzel {
int n;
struct uzel *next;
};
struct uzel *
initializef(struct uzel *head)
{
static int c = 0;
int a;
printf("uzel[%d] = ", c);
scanf("%d", &a);
if (a == 0) {
head->next = NULL;
}
else {
c++;
head->n = a;
head->next = malloc(sizeof(struct uzel));
initializef(head->next);
}
return head;
}
void
gprint(struct uzel *head)
{
printf("(");
while (head->next != NULL) {
printf("%d ", head->n);
head = head->next;
}
printf(")\n");
}
struct uzel *
delete(struct uzel *head, int val)
{
if (head->n == val) {
struct uzel *newhead = head;
newhead = head->next;
struct uzel *del = head;
// free(del);//HERE!!!!!!!!!
return newhead;
}
struct uzel *right = head;
struct uzel *left = right;
while (left->next) {
right = left->next;
if (right->next != NULL) {
if (right->n == val) {
struct uzel *del = right;
left->next = right->next;
free(del);
}
} // but here is ok!!!
left = left->next;
}
return head;
}
struct uzel *
delete_all(struct uzel *head, int val)
{
struct uzel *newhead = head;
while (newhead->n == val) {
newhead = head->next;
// struct uzel* del=head;//here!!!!!!!!!!!!!!
// free(del);
if ((newhead->n) == NULL) {
printf("No more elements to work with,ERROR");
return 0;
}
}
struct uzel *right = newhead;
struct uzel *left = newhead;
while (left->next) {
right = right->next;
if (right->n == val) {
struct uzel *tmp = right;
left->next = right->next;
free(tmp);
}
else {
left = left->next;
}
}
return newhead;
}
void
addAfter(int info, int after_what, struct uzel *head)
{
struct uzel *left = head;
struct uzel *right = head;
while ((left->n) != after_what) {
right = right->next;
left = left->next;
}
right = right->next;
struct uzel *newelem = malloc(sizeof(struct uzel));
newelem->n = info;
newelem->next = right;
left->next = newelem;
}
void
addAfterALL(int info, int after_what, struct uzel *head)
{
struct uzel *left = head;
struct uzel *right = head;
while ((right->next)) {
while (((left->n) != after_what)) {
right = right->next;
left = left->next;
}
right = right->next;
struct uzel *newelem = malloc(sizeof(struct uzel));
newelem->n = info;
newelem->next = right;
left->next = newelem;
}
}
int
main()
{
int a, b;
struct uzel head2;
struct uzel *mainhead1 = initializef(&head2);
gprint(mainhead1);
printf("Enter a number to delete: ");
scanf("%d", &a);
printf("Delete all %d ? If yes enter 2 if no enter 1: ", a);
scanf("%d", &b);
if (b == 2) {
struct uzel *mainhead = delete_all(mainhead1, a);
gprint(mainhead);
}
else {
struct uzel *mainhead = delete(mainhead1, a);
gprint(mainhead);
}
printf("Enter after what number u want to insert smth: ");
int r;
scanf("%d", &r);
printf("Enter what number u want to insert: ");
int u;
scanf("%d", &u);
printf("After all numbers %d ? If yes enter 1 If no enter 2:", r);
int g;
scanf("%d", &g);
if (g == 2) {
addAfter(u, r, mainhead1);
gprint(mainhead1);
}
if (g == 1) {
addAfterALL(u, r, mainhead1);
gprint(mainhead1);
}
}
Upvotes: 0
Views: 64
Reputation: 33631
The code for delete
and delete_All
is very similar, so it can be moved to a common function.
Here's a refactored version (with some annotations) that simplifies the delete code and makes it work for both cases.
Note the use of the extra next
variable to prevent "use after free" [which may have been the source of the issues with calling free
].
#include <stdlib.h>
#include <stdio.h>
struct uzel {
int n;
struct uzel *next;
};
struct uzel *
initializef(struct uzel *head)
{
static int c = 0;
int a;
printf("uzel[%d] = ", c);
scanf("%d", &a);
if (a == 0) {
head->next = NULL;
}
else {
c++;
head->n = a;
head->next = malloc(sizeof(struct uzel));
initializef(head->next);
}
return head;
}
void
gprint(struct uzel *head)
{
printf("(");
while (head->next != NULL) {
printf("%d ", head->n);
head = head->next;
}
printf(")\n");
}
struct uzel *
delete_common(struct uzel *head, int val, int allflg)
{
struct uzel *cur;
struct uzel *prev;
struct uzel *next;
prev = NULL;
for (cur = head; cur != NULL; cur = next) {
next = cur->next;
// wait for match
if (cur->n != val) {
prev = cur;
continue;
}
// remove from middle/end of list
if (prev != NULL)
prev->next = next;
// remove from head of list
else
head = next;
// release the storage for the node we're deleting
free(cur);
// stop if we're only deleting the first match
if (! allflg)
break;
}
return head;
}
struct uzel *
delete(struct uzel *head, int val)
{
head = delete_common(head,val,0);
return head;
}
struct uzel *
delete_all(struct uzel *head, int val)
{
head = delete_common(head,val,1);
return head;
}
void
addAfter(int info, int after_what, struct uzel *head)
{
struct uzel *left = head;
struct uzel *right = head;
while ((left->n) != after_what) {
right = right->next;
left = left->next;
}
right = right->next;
struct uzel *newelem = malloc(sizeof(struct uzel));
newelem->n = info;
newelem->next = right;
left->next = newelem;
}
void
addAfterALL(int info, int after_what, struct uzel *head)
{
struct uzel *left = head;
struct uzel *right = head;
while ((right->next)) {
while (((left->n) != after_what)) {
right = right->next;
left = left->next;
}
right = right->next;
struct uzel *newelem = malloc(sizeof(struct uzel));
newelem->n = info;
newelem->next = right;
left->next = newelem;
}
}
int
main()
{
int a, b;
struct uzel head2;
struct uzel *mainhead1 = initializef(&head2);
gprint(mainhead1);
printf("Enter a number to delete: ");
scanf("%d", &a);
printf("Delete all %d ? If yes enter 2 if no enter 1: ", a);
scanf("%d", &b);
if (b == 2) {
struct uzel *mainhead = delete_all(mainhead1, a);
gprint(mainhead);
}
else {
struct uzel *mainhead = delete(mainhead1, a);
gprint(mainhead);
}
printf("Enter after what number u want to insert smth: ");
int r;
scanf("%d", &r);
printf("Enter what number u want to insert: ");
int u;
scanf("%d", &u);
printf("After all numbers %d ? If yes enter 1 If no enter 2:", r);
int g;
scanf("%d", &g);
if (g == 2) {
addAfter(u, r, mainhead1);
gprint(mainhead1);
}
if (g == 1) {
addAfterALL(u, r, mainhead1);
gprint(mainhead1);
}
}
UPDATE:
just checked code, still have problems with
free(cur)
when trying to use it indelete_all
actually the problem appears when delete a head of a list
Okay. There are two ways to have/use a head
of a linked list:
head->next
points to.I coded up the delete functions assuming (1). But, after looking over the code some more, I believe you're using (2).
It is perfectly valid to do either. But, if using (2), it is reusing a node struct as a list struct. This is still valid, but I prefer to add an explicit list struct to make things more clear.
What added to my confusion was doing a return head;
. This is what we'd do for (1). If we do (2), we can eliminate the return
altogether and just update head->next
.
My original fix assumed (1) and that made it incompatible with (2), which is what the other functions were using.
I've refactored the code to use a separate list
struct.
Also, after getting that working, the addAfter
function(s) needed some work because the addAfter
didn't handle the case where we added an element after back to back nodes with the same value. For example, trying to add 9
after 5
with a list of:
1 1 2 3 4 5 5 6 7
Produced:
1 1 2 3 4 5 9 6 7
Instead of:
1 1 2 3 4 5 9 5 6 7
Once again, the two addAfter
functions can be combined and simplified.
Some additional thoughts on the use of the list
struct ...
When we pass a list pointer around, the caller does not have to worry about whether the callee had to adjust the head of the list. The called function just does it.
In the original code, the delete*
functions passed back the [possibly] modified head
. The addAfter*
functions could not [due to their nature] change the head of the list, so they didn't have to return an updated head value. In both cases, the caller of these functions (i.e. main
) had to "know" this.
If we added a similar set of functions to addAfter*
that did the insert before the info
value (e.g. insertBefore*
), they might have to update the head. Again, the caller would have to know about this.
So, using struct list
actually simplifies the code for most functions and allows them to have a more unified set of parameters. (e.g.) The list
is the first argument and the function can fully manipulate it as it chooses without needing to return anything.
As I was doing all that, I noticed the initializef
was recursive. While it is valid to do that, IMO, that's not a good use of recursion. An iterative solution is cleaner.
Anyway, here's the updated code. Hopefully, I tested it for the edge cases a bit more. But, you may want to test it thoroughly to be sure.
#include <stdlib.h>
#include <stdio.h>
struct uzel {
int n;
struct uzel *next;
};
struct list {
struct uzel *head;
};
void
initializef(struct list *list)
{
int c = 0;
int a;
struct uzel *newelem;
struct uzel *prev;
prev = NULL;
while (1) {
printf("uzel[%d] = ", c);
scanf("%d", &a);
if (a == 0)
break;
newelem = malloc(sizeof(*newelem));
newelem->n = a;
newelem->next = NULL;
if (prev != NULL)
prev->next = newelem;
else
list->head = newelem;
prev = newelem;
++c;
}
}
void
gprint(struct list *list)
{
struct uzel *cur;
printf("(");
for (cur = list->head; cur != NULL; cur = cur->next)
printf("%d ",cur->n);
printf(")\n");
}
void
delete_common(struct list *list, int val, int allflg)
{
struct uzel *cur;
struct uzel *prev;
struct uzel *next;
prev = NULL;
for (cur = list->head; cur != NULL; cur = next) {
next = cur->next;
// wait for match
if (cur->n != val) {
prev = cur;
continue;
}
// remove from middle/end of list
if (prev != NULL)
prev->next = next;
// remove from head of list
else
list->head = next;
// release the storage for the node we're deleting
free(cur);
// stop if we're only deleting the first match
if (! allflg)
break;
}
}
void
delete(struct list *list, int val)
{
delete_common(list,val,0);
}
void
delete_all(struct list *list, int val)
{
delete_common(list,val,1);
}
void
addAfter_common(struct list *list, int info, int after_what, int allflg)
{
struct uzel *cur;
struct uzel *newelem;
for (cur = list->head; cur != NULL; cur = cur->next) {
// wait for a match
if (cur->n != after_what)
continue;
// get new element to add
newelem = malloc(sizeof(*newelem));
newelem->n = info;
// insert new element after the match
newelem->next = cur->next;
cur->next = newelem;
// ensure that we don't infinitely add elements if the element value
// we're adding matches the value(s) we're searching for
// (e.g.) if info and after_what are the same
cur = newelem;
// stop unless adding after _all_ matches
if (! allflg)
break;
}
}
void
addAfter(struct list *list, int info, int after_what)
{
addAfter_common(list,info,after_what,0);
}
void
addAfterALL(struct list *list, int info, int after_what)
{
addAfter_common(list,info,after_what,1);
}
int
main(void)
{
int a, b;
struct list listx = { .head = NULL };
struct list *list = &listx;
initializef(list);
gprint(list);
printf("Enter a number to delete: ");
scanf("%d", &a);
printf("Delete all %d ? If yes enter 2 if no enter 1: ", a);
scanf("%d", &b);
if (b == 2) {
delete_all(list, a);
gprint(list);
}
else {
delete(list, a);
gprint(list);
}
printf("Enter after what number you want to insert something: ");
int r;
scanf("%d", &r);
printf("Enter what number you want to insert: ");
int u;
scanf("%d", &u);
printf("After all numbers %d ? If yes enter 1 If no enter 2:", r);
int g;
scanf("%d", &g);
if (g == 2) {
addAfter(list, u, r);
gprint(list);
}
if (g == 1) {
addAfterALL(list, u, r);
gprint(list);
}
return 0;
}
Upvotes: 1