链表函数的几个问题 [英] Several problems with Linked Lists functions
问题描述
我尝试为链表创建几个函数.这是所有这些,但我对函数 delete_All
和 delete
有问题.
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
需要删除所有给定值的元素,delete
只需要先删除.
delete_All
need to delete all elements with given value, and delete
only first.
我找不到的第一个错误是 free(_)
(在代码中标记为HERE!!!")以及 delete_All
中无休止的程序工作当我们放入链表的head
,同时放入下一个需要删除的元素值.
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.
例如 (1,1,2,3,4,) 不能删除第一对1".delete
和其他一些功能不能使用free
功能.
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);
}
}
推荐答案
delete
和 delete_All
的代码非常相似,所以可以移到一个通用函数.
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.
注意使用额外的 next
变量来防止释放后使用";[这可能是调用 free
时出现问题的根源].
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);
}
}
更新:
刚刚检查了代码,在delete_all
中尝试使用free(cur)
时仍然有问题实际上是在删除一个列表的头部时出现的问题
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
好的.有两种方法可以拥有/使用链表的head
:
Okay. There are two ways to have/use a head
of a linked list:
- 它可以是第一个有效节点(带有数据).
- 它可以是一个假人";节点(我们忽略数据的地方),第一个有效的数据节点是
head->next
指向的节点.
- It can be the first valid node (with data).
- It can be a "dummy" node (where we ignore the data) and the first valid data node is what
head->next
points to.
我编写了假设 (1) 的删除函数.但是,在查看更多代码后,我相信您正在使用(2).
I coded up the delete functions assuming (1). But, after looking over the code some more, I believe you're using (2).
做任何一个都完全有效.但是,如果使用 (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.
让我更困惑的是做一个return head;
.这就是我们为 (1) 所做的.如果我们做 (2),我们可以完全消除 return
并只更新 head->next
.
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
.
我最初的修复假设是 (1),这使它与其他函数使用的 (2) 不兼容.
My original fix assumed (1) and that made it incompatible with (2), which is what the other functions were using.
我已重构代码以使用单独的 list
结构.
I've refactored the code to use a separate list
struct.
此外,在完成工作后,addAfter
函数需要一些工作,因为 addAfter
没有处理我们在返回后添加元素的情况返回具有相同值的节点.例如,尝试在 5
之后添加 9
列表:
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
制作:
1 1 2 3 4 5 9 6 7
代替:
1 1 2 3 4 5 9 5 6 7
再一次,两个addAfter
函数可以组合和简化.
Once again, the two addAfter
functions can be combined and simplified.
关于使用 list
结构的一些额外想法...
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.
在原始代码中,delete*
函数传回了[可能]修改的head
.addAfter*
函数不能 [由于它们的性质] 更改列表的头部,因此它们不必返回更新的头部值.在这两种情况下,这些函数的调用者(即main
)必须知道"这个.
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.
如果我们向 addAfter*
添加了一组类似的函数,该函数在 before 中插入了 info
值(例如 insertBefore*
),他们可能必须更新头部.同样,调用者必须知道这一点.
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.
因此,使用struct list
实际上简化了大多数函数的代码,并允许它们具有更统一的参数集.(例如)list
是第一个参数,函数可以按照它的选择完全操作它而无需返回任何内容.
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.
在我做这一切时,我注意到initializef
是递归.虽然这样做 是有效的,但 IMO,这 不是很好地使用递归.迭代解决方案更简洁.
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;
}
这篇关于链表函数的几个问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!