跟踪链表的初始值
Keeping track of initial value of linked list
遍历链表有时对我来说似乎很棘手(正如我正在学习的那样)。
我一直在递归地做事情,但我想迭代地做这件事。
下一个函数将列表 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
?
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;
}
诊断
您的问题出在这段代码中:
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;
}
}
这会以与 l
中出现的顺序相反的顺序插入 *mx
中的项目,因为它始终插入列表的前面。如果您愿意,可以安排附加到 *mx
的末尾。
如果您需要从源列表中删除条目,l
,并将它们传输到 *mx
,那么您需要将 Lint *l
传递给函数,以便如果需要将 l
中的第一项(或具有修改后的签名的 *l
)转移到 *mx
.
,则可以更新列表
说明
This does the job very well. Do you mind explaining what happens with the *mx
? Because I don't seem to understand how the *mx
is "increased"; it seems that the *mx
is always in the same pointer value.
我对它的工作感到欣慰,因为我在这次更新之前没有机会测试它。
假设我们接到这样的电话:
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
在示例调用的调用函数中 — 指向使其包含新节点的地址 (*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
.
的情况下自动执行的操作
遍历链表有时对我来说似乎很棘手(正如我正在学习的那样)。
我一直在递归地做事情,但我想迭代地做这件事。
下一个函数将列表 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
?
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;
}
诊断
您的问题出在这段代码中:
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;
}
}
这会以与 l
中出现的顺序相反的顺序插入 *mx
中的项目,因为它始终插入列表的前面。如果您愿意,可以安排附加到 *mx
的末尾。
如果您需要从源列表中删除条目,l
,并将它们传输到 *mx
,那么您需要将 Lint *l
传递给函数,以便如果需要将 l
中的第一项(或具有修改后的签名的 *l
)转移到 *mx
.
说明
This does the job very well. Do you mind explaining what happens with the
*mx
? Because I don't seem to understand how the*mx
is "increased"; it seems that the*mx
is always in the same pointer value.
我对它的工作感到欣慰,因为我在这次更新之前没有机会测试它。
假设我们接到这样的电话:
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
在示例调用的调用函数中 — 指向使其包含新节点的地址 (*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
.