我正在编写一个程序,它在输入中获取整数列表,并根据整数执行以下操作:
如果输入中的值为负
如果数字为正数且均匀,则在列表顶部添加
如果数字是正数和奇数,请将其添加到列表的尾部
如果数字等于零,请结束程序并打印列表。
我的问题是pop_el函数,它导致列表上的无限循环,所以当我打印列表时,程序进入无限循环。 这是我的代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct ll_node_S * ll_node_ptr;
struct ll_node_S
{
int v;
ll_node_ptr next;
};
typedef struct ll_node_S ll_node;
ll_node_ptr push_tail(ll_node_ptr head, int v)
{
ll_node_ptr backup = head;
ll_node_ptr current = head;
while(current->next != NULL)
{
current = current->next;
}
current->next = (ll_node_ptr) malloc(sizeof(ll_node));
current->v = v;
return backup;
}
ll_node_ptr push_head(ll_node_ptr head, int v)
{
ll_node_ptr new_head = (ll_node_ptr)malloc(sizeof(ll_node));
new_head->v = v;
new_head->next = head;
return new_head;
}
ll_node_ptr pop_el(ll_node_ptr head, int el)
{
ll_node_ptr backup = head;
ll_node_ptr current = head;
ll_node_ptr previous = NULL;
int found = 0;
while(current != NULL && !found)
{
if(current->v == el)
{
if(previous == NULL)
{
backup = current->next;
free(current);
current = backup;
previous = current;
}
else
{
previous->next = current ->next;
free(current);
current = current->next;
}
found = 1;
}
else
{
previous = current;
current = current->next;
}
}
return backup;
}
void print(ll_node_ptr head)
{
ll_node_ptr current = head;
printf("%d\n", head->v);
while(current->next != NULL)
{
current = current->next;
printf("%d\n", current->v);
}
}
int isPair(int n)
{
return ((n % 2) == 0);
}
int main(int argc, char** argv)
{
int n = 1;
ll_node_ptr list = NULL;
while(n != 0)
{
scanf("%d", &n);
if(n < 0)
{
list = pop_el(list, -n);
}
else
{
if(isPair(n))
{
list = push_head(list, n);
}
else
{
list = push_tail(list, n);
}
}
}
print(list);
//should free the list
return 0;
}
这是测试用例(在输入中传递)我正在测试代码:
4
5
2
-4
-5
-3
9
2
0
应产生以下输出:
2
2
9
任何线索?
答案 0 :(得分:1)
有几件事,
在pop_el
,
1.如果previous
为NULL
,那么您只需要将head
ptr移动到下一个节点。这样它就会变成新的head
。
if(previous == NULL)
{
backup = current->next;
free(current);
//current = backup; ---> Not needed.
//previous = current; ---> Not needed.
break; // ---> No need of setting found flag. You can remove it
}
2.如果之前不是NULL
,那么您只需要将previous
节点下一个ptr指向current
节点的下一个节点。
else
{
previous->next = current ->next;
free(current);
//current = current->next; ---> Not needed.
break; // ---> No need of setting found flag. You can remove it
}
3.在push_tail
中,您要为current->next
节点分配内存,在下一行中,您要将v
添加到当前节点的v
。那是错的。检查以下内容,
ll_node_ptr push_tail(ll_node_ptr head, int v)
{
ll_node_ptr backup = head;
ll_node_ptr current = head;
ll_node_ptr new = NULL; // Created new pointer
while(current->next != NULL)
{
current = current->next;
}
//current->next = (ll_node_ptr) malloc(sizeof(ll_node));
new = (ll_node_ptr) malloc(sizeof(ll_node));
//current->v = v; ----> incorrect. new Value is actually replacing the old value.
new->v = v; // New value is added in the newly created node.
new->next = NULL;
current->next = new;
return backup;
}
4.您可以改善print
逻辑
void print(ll_node_ptr head)
{
ll_node_ptr current = head;
//printf("%d\n", head->v);
while(current != NULL)
{
printf("%d\n", current->v);
current = current->next;
}
}
答案 1 :(得分:0)
正如@Vitek所指出的,修复push_tail()
函数可以解决您的直接问题。原始代码不仅将值存储在错误的节点中,而且还无法将新分配的尾节点的next
字段设置为NULL
。此函数还有一个问题:push_head()
和push_tail()
函数都需要在使用NULL
head
指针调用时创建并返回节点指针。原始push_head()
函数执行此操作,但push_tail()
函数不执行此操作。如果用户输入的第一个数字是奇数,则第一个节点将通过push_tail()
函数添加到列表中,从而导致未定义的行为(很可能是分段错误)。
更重要的是,您应该努力简化代码。这将使编写更容易,也更容易调试。例如,您的print()
功能可以缩减为三行:
void print(ll_node_ptr current)
{
while(current) {
printf("%d\n", current->v);
current = current->next;
}
}
可以采取一些措施来改进push_tail()
功能。这是一个新版本:
ll_node_ptr push_tail(ll_node_ptr head, int v)
{
ll_node_ptr current = head;
ll_node_ptr prev = NULL;
ll_node_ptr tail = malloc(sizeof(ll_node));
if (tail == NULL) {
fprintf(stderr, "Tail node allocation error\n");
exit(EXIT_FAILURE);
}
tail->v = v;
tail->next = NULL;
while (current) {
prev = current;
current = current->next;
}
if (head) {
prev->next = tail;
} else {
head = tail;
}
return head;
}
您无需投射malloc()
的结果。 Opinions differ on whether or not you should do so,但没有演员的写作简化了代码。此外,您应该检查以确保malloc()
已成功分配所请求的内存。如果您使用realloc()
,这一点尤其重要,因为如果不这样做会导致内存泄漏。在此处创建新尾节点后,会立即设置v
和next
字段。
通过查看current
节点而不是查看current->next
节点来迭代链表通常似乎更简单。使用prev
节点指针有助于实现此目的。新函数在列表上迭代,直到current == NULL
,此时prev
指向列表的最后一个节点。如果head
作为NULL
指针传入(即列表为空,如果用户输入的第一个数字为奇数,则会发生这种情况),跳过迭代循环,因为{ {1}}已初始化为current
。循环后的代码将最后一个节点的head
字段设置为指向新创建的尾节点,如果为该函数提供了非空列表,否则next
成为新的尾节点。最后,head
返回到调用函数。
可以对head
函数和许多多余的代码应用很多简化。这个功能应该完全重新设计。你在哪里:
pop_el()
previous->next = current ->next;
free(current);
current = current->next;
引用的内存为free
,然后尝试取消引用指向此内存的指针。致电current
后,您不再拥有此内存,而这是undefined behavior。相反,你想要:
free()
但这并不重要,因为current = previous->next;
接下来设置为found
,循环终止,而函数1
s return
,不再使用backup
。您应该将上述作业删除至current
,因为不需要。
您应该能够使用此信息来改进程序中的其余代码。您可能还需要查看其他问题。 current
指针通常是一种不好的做法 - 这只会使代码混淆并增加编程错误的可能性。您提供的规范并不清楚如果有多个节点包含相同的值会发生什么,即是否应删除一个或全部?并且,如果用户输入的第一个数字是typedef
?
答案 2 :(得分:-1)
首先,我建议在使用列表时使用递归函数。它更容易调试和理解。
我想我发现你的push_tail函数存在问题:
current->next = (ll_node_ptr) malloc(sizeof(ll_node));
current->v = v;
您为current-&gt; next分配内存,但将值分配给当前节点。 这应该是
current->next = (ll_node_ptr) malloc(sizeof(ll_node));
current->next->v = v;
current->next->next = NULL;
这可能与您的问题有关。