通过链接列表迭代似乎对我来说有点棘手(正如我正在学习的那样)。
我一直在递归地做事,但我想迭代地做这件事。
如果其值大于l
,则下一个函数会将列表mx
中的值添加到列表x
中:
typedef struct slist *Lint;
typedef struct slist {
int value;
Lint next;
} Nodo;
void split(Lint l, int x, Lint *mx){
Lint mxtmp=NULL;
while (l!=NULL){
if(l->value > x){
*mx = (Lint) calloc(1,sizeof(Nodo));
(*mx)->value = l->value;
(*mx)->next = NULL;
mxtmp = *mx;
*mx = (*mx)->next;
}
l = l->next;
}
*mx = mxtmp;
}
打印*mx
而不是列表会给我一个值(使用适当的循环来浏览列表)。
*mx
开头的指针,这是我要更改的列表。
我该怎么办?
我对此函数的另一个疑问是,我怎么能不将值归因于新列表,而是创建新的mx
列表,只需指向主列表l
中的结构?
答案 0 :(得分:1)
typedef struct slist *Lint;
typedef struct slist {
int value;
Lint next;
} Nodo;
void split(Lint l, int x, Lint *mx){
Lint mxtmp=NULL;
int i = 0;
while (l!=NULL){
if(l->value > x){
*mx = (Lint) calloc(1,sizeof(Nodo));
(*mx)->value = l->value;
(*mx)->next = NULL;
if(!i) //mxtmp will keep track of the first allocated Nodo item
{
mxtmp = *mx;
i++;
}
mx = &((*mx)->next); // with this, next( so *mx) will be initialized to point to a new Nodo item in the next loop
}
l = l->next;
}
mx = &mxtmp; //finally we make mx point to the first allocated Nodo item
}
我做了以下代码来测试它:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
typedef struct slist *Lint;
typedef struct slist {
int value;
Lint next;
} Nodo;
void recurs_free(Lint f)
{
if(!f)return;
if(f->next)recurs_free(f->next);
free(f);
}
void recurs_printf(Lint f)
{
if(!f)return;
printf("%d\n", f->value);
if(f->next)recurs_printf(f->next);
}
void split(Lint l, int x, Lint *mx){
Lint mxtmp=NULL;
int i = 0;
while (l!=NULL){
if(l->value > x){
*mx = (Lint) calloc(1,sizeof(Nodo));
(*mx)->value = l->value;
(*mx)->next = NULL;
if(!i) //mxtmp will keep track of the first allocated Nodo item
{
mxtmp = *mx;
i++;
}
mx = &((*mx)->next); // with this, next( so *mx) will be initialized to point to a new Nodo item in the next loop
}
l = l->next;
}
mx = &mxtmp; //finally we make mx point to the first allocated Nodo item
}
int main()
{
void recurs_printf(Lint f);
void recurs_free(Lint f);
void split(Lint l, int x, Lint *mx);
Lint t = NULL;
Nodo a = {1, NULL}, b = {2, &a}, c = {3, &b}, d = {4, &c}, e = {5, &d};
split(&e, 3, &t);
recurs_printf(t);
recurs_free(t);
return 0;
}
答案 1 :(得分:1)
您的问题出在以下代码中:
void split(Lint l, int x, Lint *mx){
Lint mxtmp=NULL;
while (l!=NULL){
if(l->value > x){
*mx = (Lint) calloc(1,sizeof(Nodo));
您对*mx
的作业意味着您丢失了原始指针,无法正确更新您的列表。很少有平台使用calloc()
不能将指针设置为null,但是当代码显式设置结构内容时,使用calloc()
毫无意义。
您可能需要更多类似的内容:
void split(Lint l, int x, Lint *mx)
{
Lint mxtmp = NULL;
while (l != NULL)
{
if (l->value > x)
{
mxtmp = (Lint) malloc(sizeof(*mxtmp));
if (mxtmp == 0)
…report error and do not continue…
mxtmp->value = l->value;
mxtmp->next = *mx;
*mx = mxtmp;
}
l = l->next;
}
}
这将*mx
上的项目按照它们在l
中出现的顺序插入,因为它始终插入列表的前面。如果您愿意,可以安排附加到*mx
的末尾。
如果您需要从源列表l
中删除条目,并将其转移到*mx
,则需要将Lint *l
传递给该函数,以便列表的头部如果l
中的第一项(或带有修订签名的*l
)需要转移到*mx
,则可以更新。
这很好地完成了这项工作。你介意解释
*mx
会发生什么吗?因为我似乎不明白*mx
是如何增加的#34 ;;似乎*mx
始终处于相同的指针值。
我感到宽慰,因为我没有机会在此次更新之前对其进行测试。
我们假设我们接到这样的电话:
Lint list1 = 0;
…populate list1…
Lint list2 = 0;
split(list1, 10, &list2);
指针list2
有自己的地址。它包含一个值,最初是空指针。 list2
指针的地址传递给split()
,这意味着split()
可以修改指针内容(就像你有int i = 0; some_func(&i);
一样,然后是some_func()
可以修改i
)中的值。
当split()
遍历列表l
时,当它找到需要复制的值时,它会创建mxtmp
指向的新节点,并填入该值。它使新节点的next
指针指向当前位于列表头部的内容(mxtmp->next = *mx;
),然后它修改*mx
- 有效list1
的地址{1}}在示例调用中的调用函数中 - 指向它包含新节点的地址(*mx = mxtmp;
)。
这是我提出的测试代码,它在valgrind
下完全运行:
#include <stdlib.h>
#include <stdio.h>
typedef struct slist *Lint;
typedef struct slist
{
int value;
Lint next;
} Nodo;
static void error(const char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
static void split(Lint l, int x, Lint *mx)
{
// Note minor change compared to original suggested code.
while (l != NULL)
{
if (l->value > x)
{
Lint mxtmp = (Lint) malloc(sizeof(*mxtmp));
if (mxtmp == 0)
error("Out of memory");
mxtmp->value = l->value;
mxtmp->next = *mx;
*mx = mxtmp;
}
l = l->next;
}
}
static void print_list(const char *tag, Lint list)
{
printf("%s:", tag);
while (list != NULL)
{
printf(" --> %d", list->value);
list = list->next;
}
putchar('\n');
}
static void insert(Lint *list, int value)
{
Lint node = malloc(sizeof(*node));
if (node == 0)
error("Out of memory");
node->value = value;
node->next = *list;
*list = node;
}
static void destroy(Lint list)
{
while (list != NULL)
{
Lint next = list->next;
free(list);
list = next;
}
}
int main(void)
{
Lint list1 = NULL;
insert(&list1, 2);
insert(&list1, 10);
insert(&list1, 11);
insert(&list1, 23);
print_list("List 1", list1);
Lint list2 = NULL;
split(list1, 10, &list2);
print_list("List 1", list1);
print_list("List 2", list2);
destroy(list1);
destroy(list2);
return 0;
}
源文件名为nodo.c
,因此程序名为nodo
,我配置了makefile
,并启用了许多编译器警告选项。没有警告(来自Mac OS X 10.10.3上的GCC 5.1.0):
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror nodo.c -o nodo
$
我认为这是最低要求;我通常甚至不会运行代码,直到它安静地编译。
List 1: --> 23 --> 11 --> 10 --> 2
List 1: --> 23 --> 11 --> 10 --> 2
List 2: --> 11 --> 23
打印list1
两次是偏执狂,检查它是否在split
操作期间没有损坏。请注意,应编写split()
函数以使用insert()
函数。
调试打印还会打印节点的地址和下一个指针:
static void print_list(const char *tag, Lint list)
{
printf("%s:\n", tag);
while (list != NULL)
{
printf("--> %3d (%p, next %p)\n", list->value, (void *)list, (void *)list->next);
list = list->next;
}
putchar('\n');
}
屈服,例如:
List 1:
--> 23 (0x10081f410, next 0x10081f3c0)
--> 11 (0x10081f3c0, next 0x10081f370)
--> 10 (0x10081f370, next 0x10081f320)
--> 2 (0x10081f320, next 0x0)
List 1:
--> 23 (0x10081f410, next 0x10081f3c0)
--> 11 (0x10081f3c0, next 0x10081f370)
--> 10 (0x10081f370, next 0x10081f320)
--> 2 (0x10081f320, next 0x0)
List 2:
--> 11 (0x10081f4b0, next 0x10081f460)
--> 23 (0x10081f460, next 0x0)
我没有重新组织数据类型,但留给我自己的设备我可能会使用类似的东西:
typedef struct List List;
struct List
{
int value;
List *next;
};
然后通过List *
和List **
。这使用标记名称作为类型名称,这是C ++在没有显式typedef
的情况下自动执行的操作。